Mr.doob 4 years ago
parent
commit
6bab8b7f07
100 changed files with 10738 additions and 8343 deletions
  1. 44 14
      build/three.js
  2. 0 0
      build/three.min.js
  3. 30 11
      build/three.module.js
  4. 3 3
      docs/api/en/animation/AnimationAction.html
  5. 1 1
      docs/api/en/core/Layers.html
  6. 1 3
      docs/api/en/core/Object3D.html
  7. 2 2
      docs/api/en/extras/ShapeUtils.html
  8. 3 12
      docs/api/en/helpers/CameraHelper.html
  9. 1 1
      docs/api/en/helpers/SkeletonHelper.html
  10. 1 1
      docs/api/en/lights/DirectionalLight.html
  11. 2 3
      docs/api/en/lights/Light.html
  12. 15 9
      docs/api/en/lights/PointLight.html
  13. 19 0
      docs/api/en/lights/RectAreaLight.html
  14. 15 8
      docs/api/en/lights/SpotLight.html
  15. 7 2
      docs/api/en/lights/shadows/LightShadow.html
  16. 1 1
      docs/api/en/loaders/managers/DefaultLoadingManager.html
  17. 2 2
      docs/api/en/loaders/managers/LoadingManager.html
  18. 1 1
      docs/api/en/materials/Material.html
  19. 2 3
      docs/api/en/materials/MeshPhysicalMaterial.html
  20. 1 2
      docs/api/en/materials/ShaderMaterial.html
  21. 1 1
      docs/api/en/math/Box3.html
  22. 4 1
      docs/api/en/math/Matrix4.html
  23. 1 1
      docs/api/en/math/Plane.html
  24. 1 1
      docs/api/en/math/Sphere.html
  25. 2 2
      docs/api/en/objects/InstancedMesh.html
  26. 9 4
      docs/api/en/renderers/WebGLRenderer.html
  27. 1 1
      docs/api/en/textures/DataTexture.html
  28. 1 1
      docs/api/en/textures/DataTexture2DArray.html
  29. 2 4
      docs/api/zh/core/Object3D.html
  30. 5 0
      docs/api/zh/lights/shadows/LightShadow.html
  31. 0 1
      docs/api/zh/materials/ShaderMaterial.html
  32. 1 1
      docs/api/zh/math/Box2.html
  33. 11 11
      docs/api/zh/math/Box3.html
  34. 7 7
      docs/api/zh/math/Color.html
  35. 4 4
      docs/api/zh/math/Frustum.html
  36. 20 22
      docs/api/zh/math/MathUtils.html
  37. 4 7
      docs/api/zh/math/Matrix3.html
  38. 17 16
      docs/api/zh/math/Matrix4.html
  39. 3 3
      docs/api/zh/math/Plane.html
  40. 20 24
      docs/api/zh/math/Quaternion.html
  41. 2 2
      docs/api/zh/math/Ray.html
  42. 10 11
      docs/api/zh/math/Sphere.html
  43. 4 4
      docs/api/zh/math/Spherical.html
  44. 1 1
      docs/api/zh/math/Triangle.html
  45. 11 13
      docs/api/zh/math/Vector3.html
  46. 2 2
      docs/api/zh/math/Vector4.html
  47. 2 2
      docs/api/zh/objects/InstancedMesh.html
  48. 8 3
      docs/api/zh/renderers/WebGLRenderer.html
  49. 1 1
      docs/api/zh/textures/DataTexture2DArray.html
  50. 1 1
      docs/index.html
  51. 0 1
      docs/manual/ar/introduction/How-to-create-VR-content.html
  52. 0 1
      docs/manual/en/introduction/How-to-create-VR-content.html
  53. 0 1
      docs/manual/ja/introduction/How-to-create-VR-content.html
  54. 2 3
      docs/manual/ko/introduction/How-to-create-VR-content.html
  55. 0 1
      docs/manual/zh/introduction/How-to-create-VR-content.html
  56. 4 4
      editor/js/libs/ui.js
  57. 1 1
      editor/sw.js
  58. 0 4
      examples/files.json
  59. 1 1
      examples/js/animation/MMDAnimationHelper.js
  60. 2 2
      examples/js/controls/OrbitControls.js
  61. 1 2
      examples/js/controls/TrackballControls.js
  62. 2 2
      examples/js/controls/experimental/CameraControls.js
  63. 5 5
      examples/js/csm/CSMShader.js
  64. 287 264
      examples/js/curves/NURBSUtils.js
  65. 1 1
      examples/js/geometries/LightningStrike.js
  66. 0 0
      examples/js/libs/basis/basis_transcoder.js
  67. BIN
      examples/js/libs/basis/basis_transcoder.wasm
  68. 1 11
      examples/js/loaders/FBXLoader.js
  69. 20 3
      examples/js/loaders/GLTFLoader.js
  70. 420 230
      examples/js/loaders/LDrawLoader.js
  71. 5 6
      examples/js/loaders/RGBELoader.js
  72. 2 3
      examples/js/loaders/TGALoader.js
  73. 2 2
      examples/js/postprocessing/EffectComposer.js
  74. 2 2
      examples/js/postprocessing/Pass.js
  75. 9 2
      examples/js/renderers/CSS3DRenderer.js
  76. 3 16
      examples/js/shaders/MMDToonShader.js
  77. 0 198
      examples/js/shaders/ParallaxShader.js
  78. 484 480
      examples/js/utils/BufferGeometryUtils.js
  79. 37 39
      examples/js/utils/CameraUtils.js
  80. 343 468
      examples/js/utils/GeometryCompressionUtils.js
  81. 134 134
      examples/js/utils/GeometryUtils.js
  82. 107 0
      examples/js/utils/PackedPhongMaterial.js
  83. 34 34
      examples/js/utils/SceneUtils.js
  84. 290 283
      examples/js/utils/SkeletonUtils.js
  85. 115 0
      examples/js/utils/WorkerPool.js
  86. 5 5
      examples/jsm/csm/CSMShader.js
  87. 1 1
      examples/jsm/curves/NURBSCurve.js
  88. 1 1
      examples/jsm/curves/NURBSSurface.js
  89. 281 273
      examples/jsm/curves/NURBSUtils.js
  90. 26 2
      examples/jsm/interactive/SelectionBox.js
  91. 0 114
      examples/jsm/libs/zstddec.module.js
  92. 222 62
      examples/jsm/lines/LineMaterial.js
  93. 5 11
      examples/jsm/loaders/FBXLoader.js
  94. 25 4
      examples/jsm/loaders/GLTFLoader.js
  95. 2175 628
      examples/jsm/loaders/IFCLoader.js
  96. 403 120
      examples/jsm/loaders/KTX2Loader.js
  97. 410 201
      examples/jsm/loaders/LDrawLoader.js
  98. 4 4
      examples/jsm/loaders/RGBELoader.js
  99. 2 2
      examples/jsm/loaders/TGALoader.js
  100. 4557 4480
      examples/jsm/loaders/ifc/web-ifc-api.js

File diff suppressed because it is too large
+ 44 - 14
build/three.js


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


File diff suppressed because it is too large
+ 30 - 11
build/three.module.js


+ 3 - 3
docs/api/en/animation/AnimationAction.html

@@ -73,9 +73,9 @@
 
 			Must be one of these constants:<br /><br />
 			[page:Animation THREE.LoopOnce] - playing the clip once,<br />
-			[page:Animation THREE.LoopRepeat] - playing the clip with the choosen number of *repetitions*,
+			[page:Animation THREE.LoopRepeat] - playing the clip with the chosen number of *repetitions*,
 			each time jumping from the end of the clip directly to its beginning,<br />
-			[page:Animation THREE.LoopPingPong] - playing the clip with the choosen number of *repetitions*,
+			[page:Animation THREE.LoopPingPong] - playing the clip with the chosen number of *repetitions*,
 			alternately playing forward and backward.
 		</p>
 
@@ -98,7 +98,7 @@
 			The local time of this action (in seconds, starting with 0).<br /><br />
 
 			The value gets clamped or wrapped to 0...clip.duration (according to the loop state). It can be
-			scaled relativly to the global mixer time by changing [page:.timeScale timeScale] (using
+			scaled relatively to the global mixer time by changing [page:.timeScale timeScale] (using
 			[page:.setEffectiveTimeScale setEffectiveTimeScale] or [page:.setDuration setDuration]).<br />
 		</p>
 

+ 1 - 1
docs/api/en/core/Layers.html

@@ -15,7 +15,7 @@
 			Object3Ds are a member of layer 0.<br /><br />
 
 			This can be used to control visibility - an object must share a layer with a [page:Camera camera] to be visible when that camera's
-			view is renderered.<br /><br />
+			view is rendered.<br /><br />
 
 			All classes that inherit from [page:Object3D] have an [page:Object3D.layers] property which is an instance of this class.
 		</p>

+ 1 - 3
docs/api/en/core/Object3D.html

@@ -41,9 +41,7 @@
 		<h3>[property:Material customDepthMaterial]</h3>
 		<p>
 		Custom depth material to be used when rendering to the depth map. Can only be used in context of meshes.
-		When shadow-casting with a [page:DirectionalLight] or [page:SpotLight], if you are (a) modifying vertex positions in the vertex shader,
-		(b) using a displacement map, (c) using an alpha map with alphaTest, or (d) using a transparent texture with alphaTest,
-		you must specify a customDepthMaterial for proper shadows. Default is *undefined*.
+		When shadow-casting with a [page:DirectionalLight] or [page:SpotLight], if you are modifying vertex positions in the vertex shader you must specify a customDepthMaterial for proper shadows. Default is *undefined*.
 		</p>
 
 		<h3>[property:Material customDistanceMaterial]</h3>

+ 2 - 2
docs/api/en/extras/ShapeUtils.html

@@ -12,7 +12,7 @@
 		<p class="desc">
 		A class containing utility functions for shapes.<br /><br />
 
-		Note that these are all linear functions so it is neccessary to calculate separately for
+		Note that these are all linear functions so it is necessary to calculate separately for
 		x, y (and z, w if present) components of a vector.
 		</p>
 
@@ -30,7 +30,7 @@
 		<p>
 		pts -- points defining a 2D polygon<br /><br />
 
-		Note that this is a linear function so it is neccessary to calculate separately for
+		Note that this is a linear function so it is necessary to calculate separately for
 		x, y  components of a polygon.<br /><br />
 
 		Used internally by [page:Path Path],

+ 3 - 12
docs/api/en/helpers/CameraHelper.html

@@ -31,7 +31,6 @@ scene.add( helper );
 
 		<h2>Constructor</h2>
 
-
 		<h3>[name]( [param:Camera camera] )</h3>
 		<p>
 		[page:Camera camera] -- The camera to visualize.<br /><br />
@@ -39,13 +38,9 @@ scene.add( helper );
 		This create a new [Name] for the specified camera.
 		</p>
 
-
-
 		<h2>Properties</h2>
 		<p>See the base [page:LineSegments] class for common properties.</p>
 
-
-
 		<h3>[property:Camera camera]</h3>
 		<p>The camera being visualized.</p>
 
@@ -57,20 +52,16 @@ scene.add( helper );
 
 		<h3>[property:Object matrixAutoUpdate]</h3>
 		<p>
-			See [page:Object3D.matrixAutoUpdate]. Set to *false* here as the helper is using the
+			See [page:Object3D.matrixAutoUpdate]. Set to *false* here as the helper is using the 
 			camera's [page:Object3D.matrixWorld matrixWorld].
 		</p>
 
-
-
-
-
 		<h2>Methods</h2>
 		<p>See the base [page:LineSegments] class for common methods.</p>
 
-		<h3>[method:CameraHelper dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
-		Disposes of the internally-created [page:Line.material material] and [page:Line.geometry geometry] used by this helper.
+			Disposes of the internally-created [page:Line.material material] and [page:Line.geometry geometry] used by this helper.
 		</p>
 
 		<h3>[method:null update]()</h3>

+ 1 - 1
docs/api/en/helpers/SkeletonHelper.html

@@ -13,7 +13,7 @@
 
 		<p class="desc">
 		A helper object to assist with visualizing a [page:Skeleton Skeleton].
-		The helper is renderered using a [page:LineBasicMaterial LineBasicMaterial].
+		The helper is rendered using a [page:LineBasicMaterial LineBasicMaterial].
 		</p>
 
 		<h2>Code Example</h2>

+ 1 - 1
docs/api/en/lights/DirectionalLight.html

@@ -117,7 +117,7 @@
 
 		<p>See the base [page:Light Light] class for common methods.</p>
 
-		<h3>[method:DirectionalLight dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		Override of base class's [page:Light.dispose dispose].
 		Disposes of this light's [page:DirectionalLightShadow shadow].

+ 2 - 3
docs/api/en/lights/Light.html

@@ -41,8 +41,7 @@
 		<h3>[property:Float intensity]</h3>
 		<p>
 			The light's intensity, or strength.<br />
-			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, the product of
-			[page:.color color] * intensity is interpreted as luminous intensity measured in candela.<br />
+			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, the units of intensity depend on the type of light.<br />
 			Default - *1.0*.
 		</p>
 
@@ -52,7 +51,7 @@
 			See the base [page:Object3D Object3D] class for common methods.
 		</p>
 
-		<h3>[method:Light dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		Abstract dispose method for lights; implemented by subclasses that have disposable resources.
 		</p>

+ 15 - 9
docs/api/en/lights/PointLight.html

@@ -77,17 +77,23 @@ scene.add( light );
 			Default is *0.0*.
 		</p>
 
+		<h3>[property:Float intensity]</h3>
+		<p>
+			The light's intensity. Default is *1*. <br />
+			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, intensity is the luminous
+			intensity of the light measured in candela (cd).<br /><br />
+
+			Changing the intensity will also change the light's power.
+
+		</p>
+
 		<h3>[property:Float power]</h3>
 		<p>
 			The light's power.<br />
-			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, the luminous
-			power of the light measured in lumens. Default is *4Math.PI*. <br /><br />
-
-			This is directly related to the [page:.intensity intensity] in the ratio
-			<code>
-				power = intensity * 4&pi;
-			</code>
-			and changing this will also change the intensity.
+			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, power is the luminous
+			power of the light measured in lumens (lm). <br /><br />
+
+			Changing the power will also change the light's intensity.
 		</p>
 
 		<h3>[property:PointLightShadow shadow]</h3>
@@ -105,7 +111,7 @@ scene.add( light );
 			See the base [page:Light Light] class for common methods.
 		</p>
 
-		<h3>[method:PointLight dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		Override of base class's [page:Light.dispose dispose].
 		Disposes of this light's [page:PointLightShadow shadow].

+ 19 - 0
docs/api/en/lights/RectAreaLight.html

@@ -62,6 +62,25 @@ rectLight.add( rectLightHelper );
 			See the base [page:Light Light] class for common properties.
 		</p>
 
+		<h3>[property:Float intensity]</h3>
+		<p>
+			The light's intensity. Default is *1*. <br />
+			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, intensity is the luminance
+			(brightness) of the light measured in nits (cd/m^2).<br /><br />
+
+			Changing the intensity will also change the light's power.
+
+		</p>
+
+		<h3>[property:Float power]</h3>
+		<p>
+			The light's power.<br />
+			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, power is the luminous
+			power of the light measured in lumens (lm). <br /><br />
+
+			Changing the power will also change the light's intensity.
+		</p>
+
 		<h2>Methods</h2>
 		<p>
 			See the base [page:Light Light] class for common methods.

+ 15 - 8
docs/api/en/lights/SpotLight.html

@@ -103,6 +103,16 @@
 			Default is *0.0*.
 		</p>
 
+		<h3>[property:Float intensity]</h3>
+		<p>
+			The light's intensity. Default is *1*. <br />
+			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, intensity is the luminous
+			intensity of the light measured in candela (cd).<br /><br />
+
+			Changing the power will also change the light's intensity.
+
+		</p>
+
 		<h3>[property:Float penumbra]</h3>
 		<p>
 			Percent of the spotlight cone that is attenuated due to penumbra. Takes values between
@@ -117,16 +127,13 @@
 		<h3>[property:Float power]</h3>
 		<p>
 			The light's power.<br />
-			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, the luminous
-			power of the light measured in lumens. Default is *4Math.PI*. <br /><br />
+			In [page:WebGLRenderer.physicallyCorrectLights physically correct] mode, power is the luminous
+			power of the light measured in lumens (lm). <br /><br />
 
-			This is directly related to the [page:.intensity intensity] in the ratio
-			<code>
-				power = intensity * &pi;
-			</code>
-			and changing this will also change the intensity.
+			Changing this will also change the light's intensity.
 		</p>
 
+
 		<h3>[property:SpotLightShadow shadow]</h3>
 		<p>
 			A [page:SpotLightShadow] used to calculate shadows for this light.
@@ -163,7 +170,7 @@ light.target = targetObject;
 
 		<p>See the base [page:Light Light] class for common methods.</p>
 
-		<h3>[method:SpotLight dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		Override of base class's [page:Light.dispose dispose].
 		Disposes of this light's [page:SpotLightShadow shadow].

+ 7 - 2
docs/api/en/lights/shadows/LightShadow.html

@@ -42,7 +42,12 @@
 		<h3>[property:Float bias]</h3>
 		<p>
 			Shadow map bias, how much to add or subtract from the normalized depth when deciding whether a surface is in shadow.<br />
-			The default is 0. Very tiny adjustments here (in the order of 0.0001) may help reduce artefacts in shadows
+			The default is 0. Very tiny adjustments here (in the order of 0.0001) may help reduce artifacts in shadows
+		</p>
+
+		<h3>[property:Integer blurSamples]</h3>
+		<p>
+			The amount of samples to use when blurring a VSM shadow map.
 		</p>
 
 		<h3>[property:WebGLRenderTarget map]</h3>
@@ -122,7 +127,7 @@
 		Used internally by the renderer to get the number of viewports that need to be rendered for this shadow.
 		</p>
 
-		<h3>[method:LightShadow dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		Disposes of this shadow's textures ([page:LightShadow.map map] and [page:LightShadow.mapPass mapPass]).
 		</p>

+ 1 - 1
docs/api/en/loaders/managers/DefaultLoadingManager.html

@@ -12,7 +12,7 @@
 		<p class="desc">A global instance of the [page:LoadingManager LoadingManager], used by most loaders
 			when no custom manager has been specified.<br /><br />
 
-		  This will be sufficient for most purposes, however there may be times when you desire seperate loading managers
+		  This will be sufficient for most purposes, however there may be times when you desire separate loading managers
 			for say, textures and models.
 		</p>
 

+ 2 - 2
docs/api/en/loaders/managers/LoadingManager.html

@@ -13,8 +13,8 @@
 			Handles and keeps track of loaded and pending data. A default global instance of this class
 			is created and used by loaders if not supplied manually - see [page:DefaultLoadingManager].<br /><br />
 
-			In general that should be sufficient, however there are times when it can be useful to have seperate loaders -
-			for example if you want to show seperate loading bars for objects and textures.
+			In general that should be sufficient, however there are times when it can be useful to have separate loaders -
+			for example if you want to show separate loading bars for objects and textures.
 
 		</p>
 

+ 1 - 1
docs/api/en/materials/Material.html

@@ -32,7 +32,7 @@
 		<h3>[property:Float alphaTest]</h3>
 		<p>
 		Sets the alpha value to be used when running an alpha test.
-		The material will not be renderered if the opacity is lower than this value.
+		The material will not be rendered if the opacity is lower than this value.
 		Default is *0*.
 		</p>
 

+ 2 - 3
docs/api/en/materials/MeshPhysicalMaterial.html

@@ -133,10 +133,9 @@
 			This models the reflectivity of non-metallic materials. It has no effect when [page:MeshStandardMaterial.metalness metalness] is *1.0*
 		</p>
 
-		<h3>[property:Color sheen]</h3>
+		<h3>[property:Color sheenTint]</h3>
 		<p>
-			If a color is assigned to this property, the material will use a special sheen BRDF intended for rendering cloth materials such as velvet.
-			The sheen color provides the ability to create two-tone specular materials. *null* by default.
+			Used for rendering materials such as velvet. It has no effect when set to black (0x000000). Default is black.
 		</p>
 
 		<h3>[property:Float transmission]</h3>

+ 1 - 2
docs/api/en/materials/ShaderMaterial.html

@@ -53,7 +53,7 @@
 						The loop variable has to be *i*.
 					</li>
 					<li>
-						The value *UNROLLED_LOOP_INDEX* will be replaced with the explicity value of *i* for the given iteration and can be used in preprocessor statements.
+						The value *UNROLLED_LOOP_INDEX* will be replaced with the explicitly value of *i* for the given iteration and can be used in preprocessor statements.
 					</li>
 				</ul>
 				<code>
@@ -109,7 +109,6 @@
 			[example:webgl_marchingcubes webgl / marchingcubes]<br />
 			[example:webgl_materials_envmaps webgl / materials / envmaps]<br />
 			[example:webgl_materials_lightmap webgl / materials / lightmap]<br />
-			[example:webgl_materials_parallaxmap webgl / materials / parallaxmap]<br />
 			[example:webgl_materials_wireframe webgl / materials / wireframe]<br />
 			[example:webgl_modifier_tessellation webgl / modifier / tessellation]<br />
 			[example:webgl_postprocessing_dof2 webgl / postprocessing / dof2]<br />

+ 1 - 1
docs/api/en/math/Box3.html

@@ -175,7 +175,7 @@
 		[page:Vector3 point] - [page:Vector3].<br/>
 		[page:Vector3 target] — the result will be copied into this Vector3.<br /><br />
 
-		Returns a point as a proportion of this box's width and height.
+		Returns a point as a proportion of this box's width, height and depth.
 		</p>
 
 		<h3>[method:Vector3 getSize]( [param:Vector3 target] )</h3>

+ 4 - 1
docs/api/en/math/Matrix4.html

@@ -36,7 +36,7 @@
 				</li>
 			</ul>
 
-			[page:Camera Cameras] have two additional Matrix4s:
+			[page:Camera Cameras] have three additional Matrix4s:
 			<ul>
 				<li>
 					[page:Camera.matrixWorldInverse]: The view matrix - the inverse of the Camera's [page:Object3D.matrixWorld matrixWorld].
@@ -44,6 +44,9 @@
 				<li>
 					[page:Camera.projectionMatrix]: Represents the information how to project the scene to clip space.
 				</li>
+				<li>
+					[page:Camera.projectionMatrixInverse]: The inverse of projectionMatrix.
+				</li>
 			</ul>
 			Note: [page:Object3D.normalMatrix] is not a Matrix4, but a [page:Matrix3].
 		</p>

+ 1 - 1
docs/api/en/math/Plane.html

@@ -152,7 +152,7 @@
 		and determines the direction of the [page:.normal normal].
 		</p>
 
-		<h3>[method:Plane setFromNormalAndCoplanarPoint]( [param:Vector3 normal], [param:Vector3 point] ) [param:Vector3 this]</h3>
+		<h3>[method:Plane setFromNormalAndCoplanarPoint]( [param:Vector3 normal], [param:Vector3 point] )</h3>
 		<p>
 		[page:Vector3 normal] - a unit length [page:Vector3] defining the normal of the plane.<br />
 		[page:Vector3 point] - [page:Vector3]<br /><br />

+ 1 - 1
docs/api/en/math/Sphere.html

@@ -29,7 +29,7 @@
 		<p>A [page:Vector3] defining the center of the sphere. Default is (0, 0, 0).</p>
 
 		<h3>[property:Float radius]</h3>
-		<p>The radius of the sphere. Default is 0.</p>
+		<p>The radius of the sphere. Default is -1.</p>
 
 		<h2>Methods</h2>
 

+ 2 - 2
docs/api/en/objects/InstancedMesh.html

@@ -51,13 +51,13 @@
 			If you need more instances than the original count value, you have to create a new [name].
 		</p>
 
-		<h3>[property:BufferAttribute instanceColor]</h3>
+		<h3>[property:InstancedBufferAttribute instanceColor]</h3>
 		<p>
 			Represents the colors of all instances. *null* by default.
 			You have to set its [page:BufferAttribute.needsUpdate needsUpdate] flag to true if you modify instanced data via [page:.setColorAt]().
 		</p>
 
-		<h3>[property:BufferAttribute instanceMatrix]</h3>
+		<h3>[property:InstancedBufferAttribute instanceMatrix]</h3>
 		<p>
 			Represents the local transformation of all instances.
 			You have to set its [page:BufferAttribute.needsUpdate needsUpdate] flag to true if you modify instanced data via [page:.setMatrixAt]().

+ 9 - 4
docs/api/en/renderers/WebGLRenderer.html

@@ -56,7 +56,7 @@
 		of GPU is suitable for this WebGL context. Can be *"high-performance"*, *"low-power"* or *"default"*. Default is *"default"*.
 		See [link:https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.2 WebGL spec] for details.<br />
 
-		[page:Boolean failIfMajorPerformanceCaveat] - whether the renderer creation will fail upon low perfomance is detected. Default is *false*.
+		[page:Boolean failIfMajorPerformanceCaveat] - whether the renderer creation will fail upon low performance is detected. Default is *false*.
 		See [link:https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.2 WebGL spec] for details.<br />
 
 		[page:Boolean depth] - whether the drawing buffer has a
@@ -64,7 +64,7 @@
 		Default is *true*.<br />
 
 		[page:Boolean logarithmicDepthBuffer] -  whether to use a logarithmic depth buffer. It may
-		be neccesary to use this if dealing with huge differences in scale in a single scene. Note that this setting
+		be necessary to use this if dealing with huge differences in scale in a single scene. Note that this setting
 		uses gl_FragDepth if available which disables the [link:https://www.khronos.org/opengl/wiki/Early_Fragment_Test Early Fragment Test]
 		optimization and can cause a decrease in performance.
 		Default is *false*. See the [example:webgl_camera_logarithmicdepthbuffer camera / logarithmicdepthbuffer] example.
@@ -316,11 +316,16 @@
 		- *WEBGL_compressed_texture_etc1*
 		</p>
 
-		<h3>[method:null forceContextLoss]( )</h3>
+		<h3>[method:void forceContextLoss]()</h3>
 		<p>
 		Simulate loss of the WebGL context. This requires support for the
 			[link:https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context WEBGL_lose_context] extensions.
-		According to [link:https://webglstats.com/ WebGLStats], as of February 2016 90% of WebGL enabled devices support this.
+		</p>
+
+		<h3>[method:void forceContextRestore]( )</h3>
+		<p>
+		Simulate restore of the WebGL context. This requires support for the
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context WEBGL_lose_context] extensions.
 		</p>
 
 		<h3>[method:Float getClearAlpha]()</h3>

+ 1 - 1
docs/api/en/textures/DataTexture.html

@@ -19,7 +19,7 @@
 		<h3>[name]( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding )</h3>
 		<p>
 			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
-			Further parameters correspond to the properties inherited from [page:Texture], where both magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are intially set to false.
+			Further parameters correspond to the properties inherited from [page:Texture], where both magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are initially set to false.
 		</p>
 		<p>
 			The interpretation of the data depends on type and format:

+ 1 - 1
docs/api/en/textures/DataTexture2DArray.html

@@ -19,7 +19,7 @@
 		<h3>[name]( data, width, height, depth )</h3>
 		<p>
 			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
-			The properties inherited from [page:Texture] are the default, except magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are intially set to false.
+			The properties inherited from [page:Texture] are the default, except magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are initially set to false.
 		</p>
 		<p>
 			The interpretation of the data depends on type and format:

+ 2 - 4
docs/api/zh/core/Object3D.html

@@ -39,10 +39,8 @@
 	<p>含有对象的子级的数组。请参阅[page:Group]来了解将手动对象进行分组的相关信息。</p>
 
 	<h3>[property:Material customDepthMaterial]</h3>
-	<p>渲染到深度贴图时此材质要使用的自定义深度材质。
-		当使用[page:DirectionalLight]或[page:SpotLight]进行阴影投射时,如果您正在(a)修改顶点着色器中的顶点位置,
-		(b)使用位移贴图,(c)alphaTest中使用alpha贴图,或(d)alphaTest中使用透明纹理,
-		您必须指定customDepthMaterial以得到合适的阴影。默认值*undefined*。
+	<p>Custom depth material to be used when rendering to the depth map. Can only be used in context of meshes.
+	When shadow-casting with a [page:DirectionalLight] or [page:SpotLight], if you are modifying vertex positions in the vertex shader you must specify a customDepthMaterial for proper shadows. Default is *undefined*.
 	</p>
 
 	<h3>[property:Material customDistanceMaterial]</h3>

+ 5 - 0
docs/api/zh/lights/shadows/LightShadow.html

@@ -43,6 +43,11 @@
 			默认值为0.此处非常小的调整(大约0.0001)可能有助于减少阴影中的伪影
 		</p>
 
+		<h3>[property:Integer blurSamples]</h3>
+		<p>
+			The amount of samples to use when blurring a VSM shadow map.
+		</p>
+
 		<h3>[property:WebGLRenderTarget map]</h3>
 		<p>
 			使用内置摄像头生成的深度图;超出像素深度的位置在阴影中。在渲染期间内部计算。

+ 0 - 1
docs/api/zh/materials/ShaderMaterial.html

@@ -99,7 +99,6 @@
 			[example:webgl_marchingcubes webgl / marchingcubes]<br />
 			[example:webgl_materials_envmaps webgl / materials / envmaps]<br />
 			[example:webgl_materials_lightmap webgl / materials / lightmap]<br />
-			[example:webgl_materials_parallaxmap webgl / materials / parallaxmap]<br />
 			[example:webgl_materials_wireframe webgl / materials / wireframe]<br />
 			[example:webgl_modifier_tessellation webgl / modifier / tessellation]<br />
 			[example:webgl_postprocessing_dof2 webgl / postprocessing / dof2]<br />

+ 1 - 1
docs/api/zh/math/Box2.html

@@ -154,7 +154,7 @@
 			[page:Vector2 max]  - (必须) 表示该盒子的上边界(x, y)。 <br /><br />
 
 			设置这个盒子的上下(x, y)的界限。<br>
-			Please note that this method only copies the values from the given objects.
+			请注意,此方法仅复制给定对象的值。
 		</p>
 
 		<h3>[method:Box2 setFromCenterAndSize]( [param:Vector2 center], [param:Vector2 size] )</h3>

+ 11 - 11
docs/api/zh/math/Box3.html

@@ -52,7 +52,7 @@
 		<h3>[property:Vector3 min]</h3>
 		<p>
 			[page:Vector3] 表示包围盒的下边界。<br />
-			默认值是( - Infinity, - Infinity, - Infinity )。
+			默认值是( + Infinity, + Infinity, + Infinity )。
 		</p>
 
 		<h3>[property:Vector3 max]</h3>
@@ -77,7 +77,7 @@
 		[page:Vector3 point] - 需要做clamp 的坐标 [page:Vector3]。 <br>
 		[page:Vector3 target] — 结果将会被拷贝到这个对象中<br /><br />
 
-		这个点[page:Vector3 point] [link:https://en.wikipedia.org/wiki/Clamping_(graphics) Clamps](处于范围内) 处于包围盒边界范围内。<br />
+		使这个点[page:Vector3 point] [link:https://en.wikipedia.org/wiki/Clamping_(graphics) Clamps](处于范围内) 处于包围盒边界范围内。<br />
 		</p>
 
 		<h3>[method:Box3 clone]()</h3>
@@ -123,7 +123,7 @@
 		[page:Object3D object] - 包裹在包围盒中的3d对象 [page:Object3D]。<br /><br />
 
 		扩展此包围盒的边界,使得对象及其子对象在包围盒内,包括对象和子对象的世界坐标的变换。
-		The function may result in a larger box than strictly necessary.
+		该方法可能会导致一个比严格需要的更大的框。
 
 		</p>
 
@@ -171,7 +171,7 @@
 		[page:Vector3 point] - [page:Vector3].<br/>
 		[page:Vector3 target] — 如果指定了target ,结果将会被拷贝到target。<br /><br />
 
-		返回一个点为这个盒子的宽度和高度的比例。
+		返回一个点为这个盒子的宽度、高度和深度的比例。
 		</p>
 
 		<h3>[method:Vector3 getSize]( [param:Vector3 target] )</h3>
@@ -186,7 +186,7 @@
 		[page:Box3 box] - 与包围盒的交集<br /><br />
 
 		计算此包围盒和 [page:Box3 box] 的交集,将此框的上界设置为两个框的max的较小部分,
-		将此包围盒的下界设置为两个包围盒的min的较大部分。If there's no overlap, makes this box empty.
+		将此包围盒的下界设置为两个包围盒的min的较大部分。如果两个包围盒不相交,则清空此包围盒。
 		</p>
 
 		<h3>[method:Boolean intersectsBox]( [param:Box3 box] )</h3>
@@ -232,24 +232,24 @@
 		[page:Vector3 max] - [page:Vector3] 表示上边界每个纬度(x,y,z)的值。<br /><br />
 
 		设置包围盒上下边界每个纬度(x,y,z)的值。<br>
-		Please note that this method only copies the values from the given objects.
+		请注意,此方法仅复制给定对象的值。
 		</p>
 
-		<h3>[method:this setFromArray]( [param:Array array] ) [param:Box3 this]</h3>
+		<h3>[method:this setFromArray]( [param:Array array] )</h3>
 		<p>
-		array -- 数组当中的所有的点都将被包围盒包裹。<br /><br />
+		array - 数组当中的所有的点都将被包围盒包裹。<br /><br />
 
 		设置包围盒的上下边界使得数组 *array* 中的所有点的点都被包含在内。
 		</p>
 
-		<h3>[method:this setFromBufferAttribute]( [param:BufferAttribute attribute] ) [param:Box3 this]</h3>
+		<h3>[method:this setFromBufferAttribute]( [param:BufferAttribute attribute] )</h3>
 		<p>
 		[page:BufferAttribute attribute] - 位置的缓冲数据,包含在返回的包围盒内。<br /><br />
 
 		设置此包围盒的上边界和下边界,以包含 [page:BufferAttribute attribute] 中的所有位置数据。
 		</p>
 
-		<h3>[method:this setFromCenterAndSize]( [param:Vector3 center], [param:Vector3 size] ) [param:Box3 this]</h3>
+		<h3>[method:this setFromCenterAndSize]( [param:Vector3 center], [param:Vector3 size] )</h3>
 		<p>
 		[page:Vector3 center], - 包围盒所要设置的中心位置。 <br>
 		[page:Vector3 size] - 包围盒所要设置的x、y和z尺寸(宽/高/长)。<br /><br />
@@ -262,7 +262,7 @@
 		[page:Object3D object] - 用来计算包围盒的3D对象 [page:Object3D]。<br /><br />
 
 		计算和世界轴对齐的一个对象 [page:Object3D] (含其子对象)的包围盒,计算对象和子对象的世界坐标变换。
-		The function may result in a larger box than strictly necessary.
+		该方法可能会导致一个比严格需要的更大的框。
 
 		</p>
 

+ 7 - 7
docs/api/zh/math/Color.html

@@ -178,10 +178,10 @@
 
 		<h3>[method:this fromBufferAttribute]( [param:BufferAttribute attribute], [param:Integer index] )</h3>
 		<p>
-		[page:BufferAttribute attribute] - the source attribute.<br />
-		[page:Integer index] - index in the attribute.<br /><br />
+		[page:BufferAttribute attribute] - 数据源<br />
+		[page:Integer index] - 索引值<br /><br />
 
-		Sets this color's components from the [page:BufferAttribute attribute].
+		根据参数 [page:BufferAttribute attribute] 设置该颜色。
 		</p>
 
 		<h3>[method:Integer getHex]()</h3>
@@ -234,7 +234,7 @@
 		</p>
 
 		<h3>[method:Color multiply]( [param:Color color] ) </h3>
-		<p>将此颜色的RGB值乘以给定的[page: color color]的RGB值。</p>
+		<p>将此颜色的RGB值乘以给定的 [page:Color color] 的RGB值。</p>
 
 		<h3>[method:Color multiplyScalar]( [param:Number s] ) </h3>
 		<p>将此颜色的RGB值乘以给定的[page:Number s]的值。</p>
@@ -308,11 +308,11 @@
 
 		<h3>[method:Color setColorName]( [param:String style] ) </h3>
 		<p>
-		[page:String style] — color name ( from [link:https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart X11 color names] ).<br /><br />
+		[page:String style] — 颜色名字的英文单词 ( 具体请查阅 [link:https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart X11 color names] )<br /><br />
 
-		Sets this color from a color name. Faster than [page:.setStyle] method if you don't need the other CSS-style formats.<br/><br/>
+		通过颜色名字设置该颜色。如果你不使用其他 CSS 颜色样式形式,那么这种方式会略快于 [page:.setStyle] 方法。<br/><br/>
 
-		For convenience, the list of names is exposed in Color.NAMES as a hash: <code>Color.NAMES.aliceblue // returns 0xF0F8FF</code>
+		为了方便使用,颜色名称都可以通过 Color.NAMES 访问,例如:<code>Color.NAMES.aliceblue // returns 0xF0F8FF</code>
 		</p>
 
 		<h3>[method:Color sub]( [param:Color color] ) </h3>

+ 4 - 4
docs/api/zh/math/Frustum.html

@@ -47,7 +47,7 @@
 
 		<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
 		<p>
-		[page:Vector3 point] - [page:Vector3] to test.<br /><br />
+		[page:Vector3 point] - [page:Vector3] 被检测的点。<br /><br />
 
 		检测该点 [page:Vector3 point] 是否在视锥体内。
 		</p>
@@ -88,13 +88,13 @@
 		<h3>[method:this set]( [param:Plane p0], [param:Plane p1], [param:Plane p2], [param:Plane p3], [param:Plane p4], [param:Plane p5] )</h3>
 		<p>
 		从传入的平面设置当前视锥体。没有隐式的顺序。<br>
-		Note that this method only copies the values from the given objects.
+		请注意,此方法仅复制给定对象的值。
 		</p>
 
 		<h3>[method:this setFromProjectionMatrix]( [param:Matrix4 matrix] )</h3>
 		<p>
-		[page:Matrix4 matrix] - Projection [page:Matrix4] used to set the [page:.planes planes]<br /><br />
-		Sets the frustum planes from the projection matrix.
+		[page:Matrix4 matrix] - 投影矩阵 [page:Matrix4] 会被用来设置该视椎体的 [page:.planes planes]<br /><br />
+		根据投影矩阵 matrix 来设置当前视椎体的六个面。
 		</p>
 
 

+ 20 - 22
docs/api/zh/math/MathUtils.html

@@ -42,11 +42,11 @@
 
 		<h3>[method:Float inverseLerp]( [param:Float x], [param:Float y], [param:Float value] )</h3>
 		<p>
-		[page:Float x] - Start point.<br />
-		[page:Float y] - End point.<br />
-		[page:Float value] - A value between start and end.<br><br />
+		[page:Float x] - 起始点<br />
+		[page:Float y] - 终点<br />
+		[page:Float value] - 介于起始点和终点的值<br><br />
 
-		Returns the percentage in the closed interval [0, 1] of the given value between the start and end point.
+		返回参数 value 在起点 x 与终点 y 的闭区间 [0,1] 中的百分比。
 		</p>
 
 		<h3>[method:Float lerp]( [param:Float x], [param:Float y], [param:Float t] )</h3>
@@ -61,13 +61,12 @@
 
 		<h3>[method:Float damp]( [param:Float x], [param:Float y], [param:Float lambda], [param:Float dt] )</h3>
 		<p>
-		[page:Float x] - Current point. <br />
-		[page:Float y] - Target point. <br />
-		[page:Float lambda] - A higher lambda value will make the movement more sudden, and a lower value will make the movement more gradual. <br />
-		[page:Float dt] - Delta time in seconds.<br><br />
+		[page:Float x] - 当前点<br />
+		[page:Float y] - 目标点<br />
+		[page:Float lambda] - 较高的参数 lambda 值会使运动更加突然,而较低的值会使运动更加平缓。<br />
+		[page:Float dt] - 以秒为单位的增量时间<br><br />
 
-		Smoothly interpolate a number from [page:Float x] toward [page:Float y] in a spring-like manner using the [page:Float dt] to maintain frame rate independent movement.
-		For details, see [link:http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ Frame rate independent damping using lerp].
+		使用 [page:Float dt] 以类似弹簧的方式从 [page:Float x] 向 [page:Float y] 平滑地插入一个数字,以保持与帧速率无关的运动。有关详细信息,请参阅 [link:http://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ Frame rate independent damping using lerp].
 		</p>
 
 		<h3>[method:Float mapLinear]( [param:Float x], [param:Float a1], [param:Float a2], [param:Float b1], [param:Float b2] )</h3>
@@ -83,10 +82,10 @@
 
 		<h3>[method:Float pingpong]( [param:Float x], [param:Float length] )</h3>
 		<p>
-		[page:Float x] — The value to pingpong.<br />
-		[page:Float length] — The positive value the function will pingpong to. Default is 1.<br /><br />
+		[page:Float x] — pingpong 的值<br />
+		[page:Float length] — 函数将 pingpong 传递到的正值。默认值为 1。<br /><br />
 
-		Returns a value that alternates between 0 and [param:Float length].</p>
+		返回一个介于 0 和 [param:Float length] 之间的值。</p>
 
 		<h3>[method:Integer ceilPowerOfTwo]( [param:Number n] )</h3>
 		<p>返回大于等于 [page:Number n] 的2的最小次幂。</p>
@@ -131,15 +130,14 @@
 
 		<h3>[method:null setQuaternionFromProperEuler]( [param:Quaternion q], [param:Float a], [param:Float b], [param:Float c], [param:String order] )</h3>
 		<p>
-		[page:Quaternion q] - the quaternion to be set<br />
-		[page:Float a] - the rotation applied to the first axis, in radians <br />
-		[page:Float b] - the rotation applied to the second axis, in radians <br />
-		[page:Float c] - the rotation applied to the third axis, in radians <br />
-		[page:String order] - a string specifying the axes order: 'XYX', 'XZX', 'YXY', 'YZY', 'ZXZ', or 'ZYZ'<br /><br />
-
-		Sets quaternion [page:Quaternion q] from the [link:http://en.wikipedia.org/wiki/Euler_angles intrinsic Proper Euler Angles] defined by angles [page:Float a], [page:Float b], and [page:Float c], and order [page:String order].<br />
-
-		Rotations are applied to the axes in the order specified by [page:String order]: rotation by angle [page:Float a] is applied first, then by angle [page:Float b], then by angle [page:Float c]. Angles are in radians.
+		[page:Quaternion q] - 将被设置的的四元数。<br />
+		[page:Float a] - 应用于第一个轴的旋转,以弧度为单位。<br />
+		[page:Float b] - 应用于第二个轴的旋转,以弧度为单位。<br />
+		[page:Float c] - 应用于第三个轴的旋转,以弧度为单位。<br />
+		[page:String order] - 指定轴旋转顺序的字符串:'XYX', 'XZX', 'YXY', 'YZY', 'ZXZ', 或 'ZYZ'<br /><br />
+
+		根据 [page:Float a]、[page:Float b]、[page:Float c]、[page:String order] 组成的欧拉角 [link:http://en.wikipedia.org/wiki/Euler_angles intrinsic Proper Euler Angles] 来设置四元数 [page:Quaternion q]。<br />
+		按照 [page:String order] 指定的轴旋转顺序:先旋转角度 [page:Float a],再旋转角度 [page:Float b],最后旋转角度 [page:Float c]。角度以弧度为单位。
 		</p>
 
 		<h2>Source</h2>

+ 4 - 7
docs/api/zh/math/Matrix3.html

@@ -77,14 +77,13 @@ m.elements = [ 11, 21, 31,
 
 		<h3>[method:this extractBasis]( [param:Vector3 xAxis], [param:Vector3 yAxis], [param:Vector3 zAxis] )</h3>
 		<p>
-		Extracts the [link:https://en.wikipedia.org/wiki/Basis_(linear_algebra) basis] of this
-		matrix into the three axis vectors provided. If this matrix is:
+		将该矩阵的基向量 [link:https://en.wikipedia.org/wiki/Basis_(linear_algebra) basis] 提取到提供的三个轴向中。如果该矩阵如下:
 		<code>
 a, b, c,
 d, e, f,
 g, h, i
 		</code>
-		then the [page:Vector3 xAxis], [page:Vector3 yAxis], [page:Vector3 zAxis] will be set to:
+		那么 [page:Vector3 xAxis], [page:Vector3 yAxis], [page:Vector3 zAxis] 将会被设置为:
 		<code>
 xAxis = (a, d, g)
 yAxis = (b, e, h)
@@ -142,9 +141,7 @@ zAxis = (c, f, i)
 		[page:Float n32] - 设置第三行第二列的值。<br />
 		[page:Float n33] - 设置第三行第三列的值。<br /><br />
 
-		Sets the 3x3 matrix values to the given
-		[link:https://en.wikipedia.org/wiki/Row-_and_column-major_order row-major]
-		sequence of values.
+		使用行优先 [link:https://en.wikipedia.org/wiki/Row-_and_column-major_order row-major] 的格式来设置该矩阵。
 		</p>
 
 		<h3>[method:this premultiply]( [param:Matrix3 m] )</h3>
@@ -181,7 +178,7 @@ zAxis = (c, f, i)
 		<p>
 		[page:Array array] -  用于存储当前矩阵转置结果的数组。<br /><br />
 
-		将当前矩阵的转置[link:https://en.wikipedia.org/wiki/Transpose Transposes]存入给定的数组[param:Array array]但不改变当前矩阵,
+		将当前矩阵的转置[link:https://en.wikipedia.org/wiki/Transpose Transposes]存入给定的数组 array 中,但不改变当前矩阵,
 		并返回当前矩阵。
 		</p>
 

+ 17 - 16
docs/api/zh/math/Matrix4.html

@@ -20,24 +20,27 @@
 			任何3D物体[page:Object3D]都有三个关联的矩阵:
 			<ul>
 				<li>
-					[page:Object3D.matrix]: 存储物体的本地变换。 这是对象相对于其父对象的变换。
+					[page:Object3D.matrix]: 存储物体的本地变换矩阵。 这是对象相对于其父对象的变换矩阵
 				</li>
 				<li>
-					[page:Object3D.matrixWorld]: 对象的全局或世界变换。如果对象没有父对象,那么这与存储在矩阵[page:Object3D.matrix matrix]中的本地变换相同。
+					[page:Object3D.matrixWorld]: 对象的全局或世界变换矩阵。如果对象没有父对象,那么这与存储在矩阵[page:Object3D.matrix matrix]中的本地变换矩阵相同。
 				</li>
 				<li>
-					[page:Object3D.modelViewMatrix]: 表示对象相坐标相对于摄像机空间坐标转换
+					[page:Object3D.modelViewMatrix]: 表示对象相对于摄像机坐标系的变换矩阵
 					一个对象的 modelViewMatrix 是物体世界变换矩阵乘以摄像机相对于世界空间变换矩阵的逆矩阵。
 				</li>
 			</ul>
 
-			摄像机[page:Camera Cameras] 有个额外的四维矩阵:
+			摄像机[page:Camera Cameras] 有个额外的四维矩阵:
 			<ul>
 				<li>
 					[page:Camera.matrixWorldInverse]: 视矩阵 - 摄像机世界坐标变换的逆矩阵。
 				</li>
 				<li>
-					[page:Camera.projectionMatrix]: 表示将场景中的信息投影到裁剪空间。
+					[page:Camera.projectionMatrix]: 投影变换矩阵,表示将场景中的信息投影到裁剪空间。
+				</li>
+				<li>
+					[page:Camera.projectionMatrixInverse]: 投影变换矩阵的逆矩阵。
 				</li>
 			</ul>
 			注意:物体的正规矩阵 [page:Object3D.normalMatrix] 并不是一个4维矩阵,而是一个三维矩阵[page:Matrix3]。
@@ -70,21 +73,21 @@ m.elements = [ 11, 21, 31, 41,
 		请记住,如果您正在阅读源代码,您必须对这里列出的任何矩阵进行转置[link:https://en.wikipedia.org/wiki/Transpose transpose],以理解计算。
 		</p>
 
-		<h2>Extracting position, rotation and scale</h2>
+		<h2>提取位置(平移)、旋转和缩放</h2>
 		<p>
-			There are several options available for extracting position, rotation and scale from a Matrix4.
+			有多种选项可用于从 Matrix4 中提取位置、旋转和缩放。
 			<ul>
 				<li>
-					[page:Vector3.setFromMatrixPosition]: can be used to extract the translation component.
+					[page:Vector3.setFromMatrixPosition]:可用于提取位置相关的分量。
 				</li>
 				<li>
-					[page:Vector3.setFromMatrixScale]: can be used to extract the scale component.
+					[page:Vector3.setFromMatrixScale]:可用于提取缩放相关的分量。
 				</li>
 				<li>
-					[page:Quaternion.setFromRotationMatrix], [page:Euler.setFromRotationMatrix] or [page:.extractRotation extractRotation] can be used to extract the rotation component.
+					[page:Quaternion.setFromRotationMatrix], [page:Euler.setFromRotationMatrix] 或 [page:.extractRotation extractRotation]:可用于从纯(未缩放)矩阵中提取旋转相关分量。
 				</li>
 				<li>
-					[page:.decompose decompose] can be used to extract position, rotation and scale all at once.
+					[page:.decompose decompose]:可用于一次性提取位置、旋转和缩放
 				</li>
 			</ul>
 		</p>
@@ -123,13 +126,13 @@ m.elements = [ 11, 21, 31, 41,
 
 		<h3>[method:this copyPosition]( [param:Matrix4 m] )</h3>
 		<p>
-		将给定矩阵[param:Matrix4 m] 的平移分量拷贝到当前矩阵中。
+		将给定矩阵 [param:Matrix4 m] 的平移分量拷贝到当前矩阵中。
 		</p>
 
 		<h3>[method:null decompose]( [param:Vector3 position], [param:Quaternion quaternion], [param:Vector3 scale] )</h3>
 		<p>
 			将矩阵分解到给定的平移[page:Vector3 position] ,旋转 [page:Quaternion quaternion],缩放[page:Vector3 scale]分量中。<br/><br/>
-			Note: Not all matrices are decomposable in this way. For example, if an object has a non-uniformly scaled parent, then the object's world matrix may not be decomposable, and this method may not be appropriate.
+			注意:并非所有矩阵都可以通过这种方式分解。 例如,如果一个对象有一个非均匀缩放的父对象,那么该对象的世界矩阵可能是不可分解的,这种方法可能不合适。
 		</p>
 
 		<h3>[method:Float determinant]()</h3>
@@ -186,9 +189,7 @@ zAxis = (c, g, k)
 
 		<h3>[method:this lookAt]( [param:Vector3 eye], [param:Vector3 center], [param:Vector3 up], )</h3>
 		<p>
-			构造一个旋转矩阵,从[page:Vector3 eye] 指向 [page:Vector3 center],由向量 [param:Vector3 up] 定向。
-<!--			Constructs a rotation matrix, looking from [page:Vector3 eye] towards [page:Vector3 center]
-			oriented by the [page:Vector3 up] vector.-->
+			构造一个旋转矩阵,从[page:Vector3 eye] 指向 [page:Vector3 center],由向量 [page:Vector3 up] 定向。
 		</p>
 
 		<h3>[method:this makeRotationAxis]( [param:Vector3 axis], [param:Float theta] )</h3>

+ 3 - 3
docs/api/zh/math/Plane.html

@@ -122,7 +122,7 @@
 			[page:Vector3 normal] - 单位长度的向量表示平面的法向量。<br />
 			[page:Float constant] - 原点到平面有符号距离。默认值为 *0*。<br /><br />
 
-			设置平面 [page:.normal normal] 的法线和常量 [page:.constant constant] 属性值 by copying the values from the given normal
+			设置平面 [page:.normal normal] 的法线和常量 [page:.constant constant] 属性值。
 		</p>
 
 		<h3>[method:Plane setComponents]( [param:Float x], [param:Float y], [param:Float z], [param:Float w] )</h3>
@@ -144,12 +144,12 @@
 		 根据给定的三个点确定平面。如果三个点共线将会抛出错误。通过右手螺旋规则确定(向量叉乘)法向量 [page:.normal normal]。
 		</p>
 
-		<h3>[method:Plane setFromNormalAndCoplanarPoint]( [param:Vector3 normal], [param:Vector3 point] ) [param:Vector3 this]</h3>
+		<h3>[method:Plane setFromNormalAndCoplanarPoint]( [param:Vector3 normal], [param:Vector3 point] )</h3>
 		<p>
 		[page:Vector3 normal] - 平面单位法向量<br />
 		[page:Vector3 point] - 平面上的点<br /><br />
 
-		通过平面上的一点以及法线确定原点到平面的最短距离(常量)
+		通过参数提供的法线 normal 和 平面上的一个点 point 来设置该平面
 		</p>
 
 		<h3>[method:Plane translate]( [param:Vector3 offset] )</h3>

+ 20 - 24
docs/api/zh/math/Quaternion.html

@@ -132,26 +132,23 @@
 		</p>
 
 		<h3>[method:Quaternion premultiply]( [param:Quaternion q] )</h3>
-		<p>Pre-multiplies this quaternion by [page:Quaternion q].</p>
+		<p>使用 [page:Quaternion q] 乘以该四元数。</p>
 
 		<h3>[method:Quaternion rotateTowards]( [param:Quaternion q], [param:Float step] )</h3>
 		<p>
-			[page:Quaternion q] - The target quaternion.<br />
-			[page:Float step] - The angular step in radians.<br /><br />
+			[page:Quaternion q] - 目标四元数<br />
+			[page:Float step] - 以弧度为单位的角度步长<br /><br />
 
-			Rotates this quaternion by a given angular step to the defined quaternion *q*.
-			The method ensures that the final quaternion will not overshoot *q*.
+			将该四元数按照步长 step 向目标 *q* 进行旋转。该方法确保最终的四元数不会超过 *q*。
 		</p>
 
 		<h3>[method:Quaternion slerp]( [param:Quaternion qb], [param:Float t] )</h3>
 		<p>
-			[page:Quaternion qb] - The other quaternion rotation<br />
-			[page:Float t] - interpolation factor in the closed interval [0, 1].<br /><br />
+			[page:Quaternion qb] - 另一个四元数旋转<br />
+			[page:Float t] - 闭区间 [0, 1] 中的插值因子<br /><br />
 
-			Handles the spherical linear interpolation between quaternions. [page:Float t] represents the
-			amount of rotation between this quaternion (where [page:Float t] is 0) and [page:Quaternion qb] (where
-			[page:Float t] is 1). This quaternion is set to the result. Also see the static version of the
-			*slerp* below.
+			处理四元数之间的球面线性插值。[page:Float t] 表示该四元数(其中 [page:Float t] 为 0) 和 [page:Quaternion qb] (其中 [page:Float t] 为1) 之间的旋转量。
+			该四元数会被设置为上述计算的结果。另请参阅下面 *slerp* 的静态版本。
 
 			<code>
 			// rotate a mesh towards a target quaternion
@@ -160,7 +157,7 @@
 		</p>
 
 		<h3>[method:this slerpQuaternions]( [param:Quaternion qa], [param:Quaternion qb], [param:Float t] )</h3>
-		<p>Performs a spherical linear interpolation between the given quaternions and stores the result in this quaternion.</p>
+		<p>在给定的四元数之间执行球面线性插值,并将结果存储在这个四元数中</p>
 
 		<h3>[method:Quaternion set]( [param:Float x], [param:Float y], [param:Float z], [param:Float w] )</h3>
 		<p>设置该四元数的 [page:.x x]、[page:.y y]、[page:.z z]和[page:.w w]属性。</p>
@@ -183,10 +180,9 @@
 
 		<h3>[method:Quaternion setFromUnitVectors]( [param:Vector3 vFrom], [param:Vector3 vTo] )</h3>
 		<p>
-		Sets this quaternion to the rotation required to rotate direction vector [page:Vector3 vFrom] to
-		direction vector [page:Vector3 vTo].<br />
-		Adapted from the method [link:http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors here].<br />
-		[page:Vector3 vFrom] and [page:Vector3 vTo] are assumed to be normalized.
+		将该四元数设置为从方向向量 [page:Vector3 vFrom] 旋转到方向向量 [page:Vector3 vTo] 所需的旋转。<br />
+		改编自方法 [link:http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors here]。<br />
+		假设 [page:Vector3 vFrom] 和 [page:Vector3 vTo] 都已归一化。
 		</p>
 
 		<h3>[method:Array toArray]( [param:Array array], [param:Integer offset] )</h3>
@@ -210,16 +206,16 @@
 
 		<h3>[method:null slerpFlat]( [param:Array dst], [param:Integer dstOffset], [param:Array src0], [param:Integer srcOffset0], [param:Array src1], [param:Integer srcOffset1], [param:Float t] )</h3>
 		<p>
-		[page:Array dst] - The output array.<br />
-		[page:Integer dstOffset] - An offset into the output array.<br />
-		[page:Array src0] - The source array of the starting quaternion.<br />
-		[page:Integer srcOffset0] - An offset into the array *src0*.<br />
-		[page:Array src1] - The source array of the target quatnerion.<br />
-		[page:Integer srcOffset1] - An offset into the array *src1*.<br />
-		[page:Float t] - Normalized interpolation factor (between 0 and 1).<br /><br />
+		[page:Array dst] - 输出数组<br />
+		[page:Integer dstOffset] - 输出数组的偏移量<br />
+		[page:Array src0] - 起始四元数的源数组<br />
+		[page:Integer srcOffset0] - 数组 *src0* 的偏移量<br />
+		[page:Array src1] - 目标四元数的源数组<br />
+		[page:Integer srcOffset1] - 数组 *src1* 的偏移量<br />
+		[page:Float t] - 归一化插值因子(介于 0 和 1 之间)<br /><br />
 		</p>
 		<p>
-		Like the static *slerp* method above, but operates directly on flat arrays of numbers.
+		类似于上面的 *slerp* 方法,但直接对平面数组进行操作。
 		</p>
 
 		<!-- Note: Do not add non-static methods to the bottom of this page. Put them above the <h2>Static Methods</h2> -->

+ 2 - 2
docs/api/zh/math/Ray.html

@@ -79,7 +79,7 @@
 
 		<h3>[method:Float distanceSqToPoint]( [param:Vector3 point] )</h3>
 		<p>
-		[page:Vector3 point] - the [page:Vector3] to compute a distance to.<br /><br />
+		[page:Vector3 point] - 将被用于计算到其距离的 [page:Vector3]。<br /><br />
 
 		获得[page:Ray]与传入的[page:Vector3]之间最近的平方距离。
 		</p>
@@ -193,7 +193,7 @@
 		该向量必须经过标准化(使用[page:Vector3.normalize]),这样才能使方法正常运行。
 		<br /><br />
 
-		Sets this ray's [page:.origin origin] and [page:.direction direction] properties by copying the values from the given objects.
+		根据参数设置该射线的 [page:.origin origin] 和 [page:.direction direction] 。
 		</p>
 
 

+ 10 - 11
docs/api/zh/math/Sphere.html

@@ -29,7 +29,7 @@
 		<p>A [page:Vector3]定义了球心的位置,默认值位于(0, 0, 0)。</p>
 
 		<h3>[property:Float radius]</h3>
-		<p>球的半径,默认值为0。</p>
+		<p>球的半径,默认值为-1。</p>
 
 		<h2>方法</h2>
 
@@ -53,7 +53,7 @@
 
 		<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
 		<p>
-		[page:Vector3 point] - the [page:Vector3] to be checked<br /><br />
+		[page:Vector3 point] - [page:Vector3] 要被检查的点<br /><br />
 
 		检查球体中是否包含所传入的[page:Vector3 point]点,包括球的表面。
 		</p>
@@ -66,24 +66,23 @@
 		<h3>[method:Float distanceToPoint]( [param:Vector3 point] )</h3>
 		<p>
 		返回球的边界到所传入的[page:Vector3 point]点的最近距离。
-		若这个点,则距离将为负值。
+		若这个点位于球内,则距离将为负值。
 		</p>
 
 		<h3>[method:this expandByPoint]( [param:Vector3 point] )</h3>
 		<p>
-		[page:Vector3 point] - [page:Vector3] that should be included in the sphere.<br /><br />
+		[page:Vector3 point] - [page:Vector3] 将被包含在球内的点<br /><br />
 
-		Expands the boundaries of this sphere to include [page:Vector3 point].
+		扩展该球的边界以包含此点 [page:Vector3 point]。
 		</p>
 
 		<h3>[method:Boolean isEmpty]()</h3>
 		<p>
-		检查球是否为空(the radius set to a negative number).
-		Spheres with a radius of 0 contain only their center point and are not considered to be empty.
+		检查球是否为空(球半径为负值)。半径为 0 的球体仅包含其中心点,并不会被视为空。
 		</p>
 
 		<h3>[method:Sphere makeEmpty]()</h3>
-		<p>Makes the sphere empty by setting [page:.center center] to (0, 0, 0) and [page:.radius radius] to -1.</p>
+		<p>将该球修改为空,即中心点 [page:.center center] 为 (0,0,0),半径 [page:.radius radius] 为 -1。</p>
 
 		<h3>[method:Boolean equals]( [param:Sphere sphere] )</h3>
 		<p>
@@ -124,7 +123,7 @@
 			[page:Float radius] - 球的半径。<br /><br />
 
 		设置球的[page:.center center]和[page:.radius radius]属性。<br>
-		Please note that this method only copies the values from the given center.
+		请注意此,方法使用复制的方式来设置中心值。
 		</p>
 
 		<h3>[method:Sphere setFromPoints]( [param:Array points], [param:Vector3 optionalCenter] )</h3>
@@ -143,9 +142,9 @@
 
 		<h3>[method:this union]( [param:Sphere sphere] )</h3>
 		<p>
-		[page:Sphere sphere] - Bounding sphere that will be unioned with this sphere.<br /><br />
+		[page:Sphere sphere] - 将与该球体即将结合的边界球体。<br /><br />
 
-		Expands this sphere to enclose both the original sphere and the given sphere.
+		扩展此球体以包含原始球体和给定球体。
 		</p>
 
 		<h2>源代码</h2>

+ 4 - 4
docs/api/zh/math/Spherical.html

@@ -19,10 +19,10 @@
 		<p>
 		[page:Float radius] - 半径值,或者说从该点到原点的
 		[link:https://en.wikipedia.org/wiki/Euclidean_distance Euclidean distance](欧几里得距离,即直线距离)。默认值为*1.0*。<br />
-		[page:Float phi] - 与 y (up) 轴的极坐标角(以弧度为单位)。 默认值为 *0*。<br />
-		[page:Float theta] - 绕 y (up) 轴的赤道角(以弧度为单位)。 默认值为 *0*。<br /><br />
+		[page:Float phi] - 与 y (up) 轴的极角(以弧度为单位)。 默认值为 *0*。<br />
+		[page:Float theta] - 绕 y (up) 轴的赤道角(方位角)(以弧度为单位)。 默认值为 *0*。<br /><br />
 
-		极点(φ phi)位于正 y 轴和负 y 轴上。赤道(θ theta)从正 z 开始。
+		极角(phi)位于正 y 轴和负 y 轴上。赤道角(方位角)(theta)从正 z 开始。
 		</p>
 
 
@@ -51,7 +51,7 @@
 
 		<h3>[method:Spherical makeSafe]()</h3>
 		<p>
-		将极角 [page:.phi phi] 的值限制在0.000001 和 pi - 0.000001 之间。
+		将极角 [page:.phi phi] 的值限制在0.000001 和 π - 0.000001 之间。
 		</p>
 
 		<h3>[method:Spherical set]( [param:Float radius], [param:Float phi], [param:Float theta] )</h3>

+ 1 - 1
docs/api/zh/math/Triangle.html

@@ -120,7 +120,7 @@
 		<h3>[method:Triangle set]( [param:Vector3 a], [param:Vector3 b], [param:Vector3 c] ) [param:Triangle this]</h3>
 		<p>
 			将三角形的[page:.a a]、[page:.b b]和[page:.c c]属性设置为所传入的[page:Vector3 vector3]。<br>
-			Please note that this method only copies the values from the given objects.
+			请注意,此方法仅复制给定对象的值。
 		</p>
 
 		<h3>[method:Triangle setFromPointsAndIndices]( [param:Array points], [param:Integer i0], [param:Integer i1], [param:Integer i2] ) [param:Triangle this]</h3>

+ 11 - 13
docs/api/zh/math/Vector3.html

@@ -101,11 +101,11 @@
 
 		<h3>[method:this applyMatrix4]( [param:Matrix4 m] )</h3>
 		<p>
-			将该向量乘以四阶矩阵m(第四个维度隐式地为1),and divides by perspective.
+			将该向量乘以四阶矩阵m(第四个维度隐式地为1),并按角度进行划分。
 		</p>
 
 		<h3>[method:this applyNormalMatrix]( [param:Matrix3 m] )</h3>
-		<p>Multiplies this vector by normal matrix [page:Matrix3 m] and normalizes the result.</p>
+		<p>将该向量乘以正规矩阵 [page:Matrix3 m],并将结果进行归一化。</p>
 
 		<h3>[method:this applyQuaternion]( [param:Quaternion quaternion] )</h3>
 		<p>
@@ -302,15 +302,14 @@
 		<p>
 		[page:Camera camera] — 在投影中使用的摄像机。<br /><br />
 
-		Projects this vector from world space into the camera's normalized device coordinate (NDC) space.
+		将此向量(坐标)从世界空间投影到相机的标准化设备坐标 (NDC) 空间。
 		</p>
 
 		<h3>[method:this projectOnPlane]( [param:Vector3 planeNormal] )</h3>
 		<p>
-		[page:Vector3 planeNormal] - A vector representing a plane normal.<br /><br />
+		[page:Vector3 planeNormal] - 表示平面法线的向量<br /><br />
 
-		[link:https://en.wikipedia.org/wiki/Vector_projection Projects] this vector onto a plane by subtracting this vector projected onto the plane's
-		normal from this vector.
+		[link:https://en.wikipedia.org/wiki/Vector_projection Projects] 通过从该向量减去投影到平面法线上的向量,将该向量投影到平面上。
 		</p>
 
 		<h3>[method:this projectOnVector]( [param:Vector3 v] )</h3>
@@ -318,14 +317,13 @@
 
 		<h3>[method:this reflect]( [param:Vector3 normal] )</h3>
 		<p>
-		[page:Vector3 normal] - the normal to the reflecting plane<br /><br />
+		[page:Vector3 normal] - 反射面法线<br /><br />
 
-		Reflect this vector off of plane orthogonal to [page:Vector3 normal]. Normal is assumed to
-		have unit length.
+		将该向量设置为对指定 normal 法线的表面的反射向量。假设法线具有单位长度。
 		</p>
 
 		<h3>[method:this round]()</h3>
-		<p>	向量中的分量四舍五入取整为最接近的整数值。</p>
+		<p>向量中的分量四舍五入取整为最接近的整数值。</p>
 
 		<h3>[method:this roundToZero]()</h3>
 		<p>
@@ -361,7 +359,7 @@
 
 		<h3>[method:this setFromMatrix3Column]( [param:Matrix3 matrix], [param:Integer index] )</h3>
 		<p>
-		Sets this vector's [page:.x x], [page:.y y] and [page:.z z] components from [page:Integer index] column of [page:Matrix3 matrix].
+		从传入的三阶矩阵 [page:Matrix3 matrix] 由 [page:Integer index] 指定的列中,设置该向量的 [page:.x x] 值、[page:.y y] 值和 [page:.z z] 值。
 		</p>
 
 		<h3>[method:this setFromMatrixPosition]( [param:Matrix4 m] )</h3>
@@ -430,12 +428,12 @@
 		<p>
 		[page:Camera camera] — 在投影中使用的摄像机。<br /><br />
 
-		Projects this vector from the camera's normalized device coordinate (NDC) space into world space.
+		将此向量(坐标)从相机的标准化设备坐标 (NDC) 空间投影到世界空间。
 		</p>
 
 		<h3>[method:this random]()</h3>
 		<p>
-			Sets each component of this vector to a pseudo-random value between 0 and 1, excluding 1.
+		将该向量的每个分量(x、y、z)设置为介于 0 和 1 之间的伪随机数,不包括 1。
 		</p>
 
 		<h2>源代码</h2>

+ 2 - 2
docs/api/zh/math/Vector4.html

@@ -275,7 +275,7 @@
 
 		<h3>[method:null setComponent]( [param:Integer index], [param:Float value] )</h3>
 		<p>
-		[page:Integer index] - 0、1或2。<br />
+		[page:Integer index] - 0、1、2 或 3。<br />
 		[page:Float value] - [page:Float]<br /><br />
 
 		若index为 0 则设置 [page:.x x] 值为 [page:Float value]。<br />
@@ -326,7 +326,7 @@
 
 		<h3>[method:this random]()</h3>
 		<p>
-			Sets each component of this vector to a pseudo-random value between 0 and 1, excluding 1.
+		将该向量的每个分量(x、y、z、w)设置为介于 0 和 1 之间的伪随机数,不包括 1。
 		</p>
 
 		<h2>源代码</h2>

+ 2 - 2
docs/api/zh/objects/InstancedMesh.html

@@ -49,13 +49,13 @@
 			如果你需要比原先的数量更多的实例数量,你需要创建一个新的[name]。
 		</p>
 
-		<h3>[property:BufferAttribute instanceColor]</h3>
+		<h3>[property:InstancedBufferAttribute instanceColor]</h3>
 		<p>
 			Represents the colors of all instances. *null* by default.
 			You have to set its [page:BufferAttribute.needsUpdate needsUpdate] flag to true if you modify instanced data via [page:.setColorAt]().
 		</p>
 
-		<h3>[property:BufferAttribute instanceMatrix]</h3>
+		<h3>[property:InstancedBufferAttribute instanceMatrix]</h3>
 		<p>
 			表示所有实例的本地变换。
 			如果你要通过 [page:.setMatrixAt]() 来修改实例数据,你必须将它的 [page:BufferAttribute.needsUpdate needsUpdate] 标识为 true 。

+ 8 - 3
docs/api/zh/renderers/WebGLRenderer.html

@@ -300,11 +300,16 @@
 		- *WEBGL_compressed_texture_etc1*
 		</p>
 
-		<h3>[method:null forceContextLoss]( )</h3>
+		<h3>[method:void forceContextLoss]()</h3>
 		<p>
 		模拟WebGL环境的丢失。需要支持
-			[link:https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context WEBGL_lose_context]扩展才能用。
-		根据[link:https://webglstats.com/ WebGLStats], as of February 2016 90% of WebGL enabled devices support this.
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context WEBGL_lose_context] 扩展才能用。
+		</p>
+
+		<h3>[method:void forceContextRestore]( )</h3>
+		<p>
+		模拟WebGL环境的恢复。需要支持
+			[link:https://developer.mozilla.org/en-US/docs/Web/API/WEBGL_lose_context WEBGL_lose_context] 扩展才能用。
 		</p>
 
 		<h3>[method:Float getClearAlpha]()</h3>

+ 1 - 1
docs/api/zh/textures/DataTexture2DArray.html

@@ -19,7 +19,7 @@
 		<h3>[name]( data, width, height, depth )</h3>
 		<p>
 			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
-			The properties inherited from [page:Texture] are the default, except magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are intially set to false.
+			The properties inherited from [page:Texture] are the default, except magFilter and minFilter default to THREE.NearestFilter. The properties flipY and generateMipmaps are initially set to false.
 		</p>
 		<p>
 			The interpretation of the data depends on type and format:

+ 1 - 1
docs/index.html

@@ -448,7 +448,7 @@
 		function setUrlFragment( pageName ) { // eslint-disable-line no-undef
 
 			// Handle navigation from the subpages (iframes):
-			// First separate the memeber (if existing) from the page name,
+			// First separate the member (if existing) from the page name,
 			// then identify the subpage's URL and set it as URL fragment (re-adding the member)
 
 			const splitPageName = decomposePageName( pageName, '.', '.' );

+ 0 - 1
docs/manual/ar/introduction/How-to-create-VR-content.html

@@ -62,7 +62,6 @@ renderer.setAnimationLoop( function () {
 			[example:webxr_vr_ballshooter WebXR / VR / ballshooter]<br />
 			[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 			[example:webxr_vr_dragging WebXR / VR / dragging]<br />
-			[example:webxr_vr_lorenzattractor WebXR / VR / lorenzattractor]<br />
 			[example:webxr_vr_paint WebXR / VR / paint]<br />
 			[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 			[example:webxr_vr_panorama WebXR / VR / panorama]<br />

+ 0 - 1
docs/manual/en/introduction/How-to-create-VR-content.html

@@ -67,7 +67,6 @@ renderer.setAnimationLoop( function () {
 		[example:webxr_vr_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<br />
-		[example:webxr_vr_lorenzattractor WebXR / VR / lorenzattractor]<br />
 		[example:webxr_vr_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<br />

+ 0 - 1
docs/manual/ja/introduction/How-to-create-VR-content.html

@@ -61,7 +61,6 @@ renderer.setAnimationLoop( function () {
 		[example:webxr_vr_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<br />
-		[example:webxr_vr_lorenzattractor WebXR / VR / lorenzattractor]<br />
 		[example:webxr_vr_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<br />

+ 2 - 3
docs/manual/ko/introduction/How-to-create-VR-content.html

@@ -44,7 +44,7 @@ renderer.xr.enabled = true;
 	</code>
 
 	<p>
-        마지막으로, 자주 쓰이는 *window.requestAnimationFrame()* 기능을 활용할 수 없기 때문에, 애니메이션 루프를 수정해주어야 합니다. 
+        마지막으로, 자주 쓰이는 *window.requestAnimationFrame()* 기능을 활용할 수 없기 때문에, 애니메이션 루프를 수정해주어야 합니다.
         VR 프로젝트에서는 [page:WebGLRenderer.setAnimationLoop setAnimationLoop]를 사용합니다.
 		가장 간소화된 코드는 다음과 같습니다:
 	</p>
@@ -65,7 +65,6 @@ renderer.setAnimationLoop( function () {
 		[example:webxr_vr_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<br />
-		[example:webxr_vr_lorenzattractor WebXR / VR / lorenzattractor]<br />
 		[example:webxr_vr_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<br />
@@ -77,4 +76,4 @@ renderer.setAnimationLoop( function () {
 
 </body>
 
-</html>
+</html>

+ 0 - 1
docs/manual/zh/introduction/How-to-create-VR-content.html

@@ -67,7 +67,6 @@ renderer.setAnimationLoop( function () {
 		[example:webxr_vr_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<br />
-		[example:webxr_vr_lorenzattractor WebXR / VR / lorenzattractor]<br />
 		[example:webxr_vr_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<br />

+ 4 - 4
editor/js/libs/ui.js

@@ -315,11 +315,11 @@ class UITextArea extends UIElement {
 
 				event.preventDefault();
 
-				const cursor = this.dom.selectionStart;
+				const cursor = this.selectionStart;
 
-				this.dom.value = this.dom.value.substring( 0, cursor ) + '\t' + this.dom.value.substring( cursor );
-				this.dom.selectionStart = cursor + 1;
-				this.dom.selectionEnd = this.dom.selectionStart;
+				this.value = this.value.substring( 0, cursor ) + '\t' + this.value.substring( cursor );
+				this.selectionStart = cursor + 1;
+				this.selectionEnd = this.selectionStart;
 
 			}
 

+ 1 - 1
editor/sw.js

@@ -1,4 +1,4 @@
-// r131
+// r132
 
 const cacheName = 'threejs-editor';
 

+ 0 - 4
examples/files.json

@@ -85,7 +85,6 @@
 		"webgl_loader_gcode",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf_compressed",
-		"webgl_loader_gltf_extensions",
 		"webgl_loader_gltf_transmission",
 		"webgl_loader_gltf_variants",
 		"webgl_loader_ifc",
@@ -142,12 +141,10 @@
 		"webgl_materials_envmaps",
 		"webgl_materials_envmaps_exr",
 		"webgl_materials_envmaps_hdr",
-		"webgl_materials_envmaps_parallax",
 		"webgl_materials_lightmap",
 		"webgl_materials_matcap",
 		"webgl_materials_normalmap",
 		"webgl_materials_normalmap_object_space",
-		"webgl_materials_parallaxmap",
 		"webgl_materials_physical_clearcoat",
 		"webgl_materials_physical_reflectivity",
 		"webgl_materials_physical_sheen",
@@ -348,7 +345,6 @@
 		"webxr_vr_handinput_pressbutton",
 		"webxr_vr_haptics",
 		"webxr_vr_layers",
-		"webxr_vr_lorenzattractor",
 		"webxr_vr_panorama",
 		"webxr_vr_panorama_depth",
 		"webxr_vr_paint",

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

@@ -44,7 +44,7 @@
 				cameraAnimation: true
 			};
 
-			this.onBeforePhysics = function ( ) {}; // experimental
+			this.onBeforePhysics = function () {}; // experimental
 
 
 			this.sharedPhysics = false;

+ 2 - 2
examples/js/controls/OrbitControls.js

@@ -514,7 +514,7 @@
 
 			}
 
-			function handleMouseUp( ) { // no-op
+			function handleMouseUp() { // no-op
 			}
 
 			function handleMouseWheel( event ) {
@@ -698,7 +698,7 @@
 
 			}
 
-			function handleTouchEnd( ) { // no-op
+			function handleTouchEnd() { // no-op
 			} //
 			// event handlers - FSM: listen for events and reset state
 			//

+ 1 - 2
examples/js/controls/TrackballControls.js

@@ -117,8 +117,7 @@
 				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
-					);
+					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 );
 					return vector;
 
 				};

+ 2 - 2
examples/js/controls/experimental/CameraControls.js

@@ -510,7 +510,7 @@
 
 		}
 
-		function handleMouseUp( ) { // no-op
+		function handleMouseUp() { // no-op
 		}
 
 		function handleMouseWheel( event ) {
@@ -691,7 +691,7 @@
 
 		}
 
-		function handleTouchEnd( ) { // no-op
+		function handleTouchEnd() { // no-op
 		} //
 		// event handlers - FSM: listen for events and reset state
 		//

+ 5 - 5
examples/js/csm/CSMShader.js

@@ -30,7 +30,7 @@ IncidentLight directLight;
 
 		pointLight = pointLights[ i ];
 
-		getPointDirectLightIrradiance( pointLight, geometry, directLight );
+		getPointLightInfo( pointLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
 		pointLightShadow = pointLightShadows[ i ];
@@ -56,7 +56,7 @@ IncidentLight directLight;
 
 		spotLight = spotLights[ i ];
 
-		getSpotDirectLightIrradiance( spotLight, geometry, directLight );
+		getSpotLightInfo( spotLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
 		spotLightShadow = spotLightShadows[ i ];
@@ -90,7 +90,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 		// NOTE: Depth gets larger away from the camera.
 		// cascade.x is closer, cascade.y is further
@@ -136,7 +136,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 
@@ -167,7 +167,7 @@ IncidentLight directLight;
 
 		directionalLight = directionalLights[ i ];
 
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		directionalLightShadow = directionalLightShadows[ i ];

+ 287 - 264
examples/js/curves/NURBSUtils.js

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

+ 1 - 1
examples/js/geometries/LightningStrike.js

@@ -452,7 +452,7 @@
 
 		}
 
-		addNewSubray( ) {
+		addNewSubray() {
 
 			return this.subrays[ this.numSubrays ++ ];
 

File diff suppressed because it is too large
+ 0 - 0
examples/js/libs/basis/basis_transcoder.js


BIN
examples/js/libs/basis/basis_transcoder.wasm


+ 1 - 11
examples/js/loaders/FBXLoader.js

@@ -636,17 +636,7 @@
 
 			}
 
-			const texture = textureMap.get( id );
-
-			if ( texture.image !== undefined ) {
-
-				return texture;
-
-			} else {
-
-				return undefined;
-
-			}
+			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

+ 20 - 3
examples/js/loaders/GLTFLoader.js

@@ -1244,7 +1244,7 @@
 			const glossinessMapParsFragmentChunk = [ '#ifdef USE_GLOSSINESSMAP', '	uniform sampler2D glossinessMap;', '#endif' ].join( '\n' );
 			const specularMapFragmentChunk = [ 'vec3 specularFactor = specular;', '#ifdef USE_SPECULARMAP', '	vec4 texelSpecular = texture2D( specularMap, vUv );', '	texelSpecular = sRGBToLinear( texelSpecular );', '	// reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture', '	specularFactor *= texelSpecular.rgb;', '#endif' ].join( '\n' );
 			const glossinessMapFragmentChunk = [ 'float glossinessFactor = glossiness;', '#ifdef USE_GLOSSINESSMAP', '	vec4 texelGlossiness = texture2D( glossinessMap, vUv );', '	// reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture', '	glossinessFactor *= texelGlossiness.a;', '#endif' ].join( '\n' );
-			const lightPhysicalFragmentChunk = [ 'PhysicalMaterial material;', 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', 'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', 'material.specularRoughness += geometryRoughness;', 'material.specularRoughness = min( material.specularRoughness, 1.0 );', 'material.specularColor = specularFactor;' ].join( '\n' );
+			const lightPhysicalFragmentChunk = [ 'PhysicalMaterial material;', 'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );', 'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );', 'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );', 'material.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.', 'material.roughness += geometryRoughness;', 'material.roughness = min( material.roughness, 1.0 );', 'material.specularColor = specularFactor;' ].join( '\n' );
 			const uniforms = {
 				specular: {
 					value: new THREE.Color().setHex( 0xffffff )
@@ -1555,6 +1555,22 @@
 		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 ************/
@@ -2707,7 +2723,7 @@
 
 		}
 
-		getMaterialType( ) {
+		getMaterialType() {
 
 			return THREE.MeshStandardMaterial;
 
@@ -3271,7 +3287,8 @@
 								// 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.
-								return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result );
+								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.
 

File diff suppressed because it is too large
+ 420 - 230
examples/js/loaders/LDrawLoader.js


+ 5 - 6
examples/js/loaders/RGBELoader.js

@@ -220,8 +220,7 @@
 					const scanline_width = w;
 
 					if ( // run length encoding is not allowed so read flat
-						scanline_width < 8 || scanline_width > 0x7fff || // this file is not run length encoded
-      2 !== buffer[ 0 ] || 2 !== buffer[ 1 ] || buffer[ 2 ] & 0x80 ) {
+						scanline_width < 8 || scanline_width > 0x7fff || 2 !== buffer[ 0 ] || 2 !== buffer[ 1 ] || buffer[ 2 ] & 0x80 ) {
 
 						// return the flat buffer
 						return new Uint8Array( buffer );
@@ -381,8 +380,8 @@
 							break;
 
 						case THREE.FloatType:
-							numElements = image_rgba_data.length / 4 * 3;
-							const floatArray = new Float32Array( numElements );
+							numElements = image_rgba_data.length / 4;
+							const floatArray = new Float32Array( numElements * 3 );
 
 							for ( let j = 0; j < numElements; j ++ ) {
 
@@ -396,8 +395,8 @@
 							break;
 
 						case THREE.HalfFloatType:
-							numElements = image_rgba_data.length / 4 * 3;
-							const halfArray = new Uint16Array( numElements );
+							numElements = image_rgba_data.length / 4;
+							const halfArray = new Uint16Array( numElements * 3 );
 
 							for ( let j = 0; j < numElements; j ++ ) {
 

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

@@ -179,11 +179,10 @@
 
 					for ( x = x_start; x !== x_end; x += x_step, i += 2 ) {
 
-						color = image[ i + 0 ] + ( image[ i + 1 ] << 8 ); // Inversed ?
-
+						color = image[ i + 0 ] + ( image[ i + 1 ] << 8 );
 						imageData[ ( x + width * y ) * 4 + 0 ] = ( color & 0x7C00 ) >> 7;
 						imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2;
-						imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) >> 3;
+						imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) << 3;
 						imageData[ ( x + width * y ) * 4 + 3 ] = color & 0x8000 ? 0 : 255;
 
 					}

+ 2 - 2
examples/js/postprocessing/EffectComposer.js

@@ -223,9 +223,9 @@
 
 		}
 
-		setSize( ) {}
+		setSize() {}
 
-		render( ) {
+		render() {
 
 			console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
 

+ 2 - 2
examples/js/postprocessing/Pass.js

@@ -15,9 +15,9 @@
 
 		}
 
-		setSize( ) {}
+		setSize() {}
 
-		render( ) {
+		render() {
 
 			console.error( 'THREE.Pass: .render() must be implemented in derived pass.' );
 

+ 9 - 2
examples/js/renderers/CSS3DRenderer.js

@@ -4,6 +4,12 @@
  * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
  */
 
+	const _position = new THREE.Vector3();
+
+	const _quaternion = new THREE.Quaternion();
+
+	const _scale = new THREE.Vector3();
+
 	class CSS3DObject extends THREE.Object3D {
 
 		constructor( element ) {
@@ -186,10 +192,11 @@
 						_matrix.transpose();
 
 						if ( object.rotation2D !== 0 ) _matrix.multiply( _matrix2.makeRotationZ( object.rotation2D ) );
+						object.matrixWorld.decompose( _position, _quaternion, _scale );
 
-						_matrix.copyPosition( object.matrixWorld );
+						_matrix.setPosition( _position );
 
-						_matrix.scale( object.scale );
+						_matrix.scale( _scale );
 
 						_matrix.elements[ 3 ] = 0;
 						_matrix.elements[ 7 ] = 0;

+ 3 - 16
examples/js/shaders/MMDToonShader.js

@@ -17,13 +17,6 @@
 	const lights_mmd_toon_pars_fragment = `
 varying vec3 vViewPosition;
 
-#ifndef FLAT_SHADED
-
-	varying vec3 vNormal;
-
-#endif
-
-
 struct BlinnPhongMaterial {
 
 	vec3 diffuseColor;
@@ -37,21 +30,15 @@ void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in Geometri
 
 	vec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;
 
-	#ifndef PHYSICALLY_CORRECT_LIGHTS
-
-		irradiance *= PI; // punctual light
-
-	#endif
-
-	reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
+	reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );
 
-	reflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;
+	reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;
 
 }
 
 void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {
 
-	reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
+	reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );
 
 }
 

+ 0 - 198
examples/js/shaders/ParallaxShader.js

@@ -1,198 +0,0 @@
-( function () {
-
-	// Parallax Occlusion shaders from
-	//    http://sunandblackcat.com/tipFullView.php?topicid=28
-	// No tangent-space transforms logic based on
-	//   http://mmikkelsen3d.blogspot.sk/2012/02/parallaxpoc-mapping-and-no-tangent.html
-	const ParallaxShader = {
-		// Ordered from fastest to best quality.
-		modes: {
-			none: 'NO_PARALLAX',
-			basic: 'USE_BASIC_PARALLAX',
-			steep: 'USE_STEEP_PARALLAX',
-			occlusion: 'USE_OCLUSION_PARALLAX',
-			// a.k.a. POM
-			relief: 'USE_RELIEF_PARALLAX'
-		},
-		uniforms: {
-			'bumpMap': {
-				value: null
-			},
-			'map': {
-				value: null
-			},
-			'parallaxScale': {
-				value: null
-			},
-			'parallaxMinLayers': {
-				value: null
-			},
-			'parallaxMaxLayers': {
-				value: null
-			}
-		},
-		vertexShader:
-  /* glsl */
-  `
-
-		varying vec2 vUv;
-		varying vec3 vViewPosition;
-		varying vec3 vNormal;
-
-		void main() {
-
-			vUv = uv;
-			vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
-			vViewPosition = -mvPosition.xyz;
-			vNormal = normalize( normalMatrix * normal );
-			gl_Position = projectionMatrix * mvPosition;
-
-		}`,
-		fragmentShader:
-  /* glsl */
-  `
-
-		uniform sampler2D bumpMap;
-		uniform sampler2D map;
-
-		uniform float parallaxScale;
-		uniform float parallaxMinLayers;
-		uniform float parallaxMaxLayers;
-
-		varying vec2 vUv;
-		varying vec3 vViewPosition;
-		varying vec3 vNormal;
-
-		#ifdef USE_BASIC_PARALLAX
-
-			vec2 parallaxMap( in vec3 V ) {
-
-				float initialHeight = texture2D( bumpMap, vUv ).r;
-
-				// No Offset Limitting: messy, floating output at grazing angles.
-			//"vec2 texCoordOffset = parallaxScale * V.xy / V.z * initialHeight;",
-
-			// Offset Limiting
-				vec2 texCoordOffset = parallaxScale * V.xy * initialHeight;
-				return vUv - texCoordOffset;
-
-			}
-
-		#else
-
-			vec2 parallaxMap( in vec3 V ) {
-
-				// Determine number of layers from angle between V and N
-				float numLayers = mix( parallaxMaxLayers, parallaxMinLayers, abs( dot( vec3( 0.0, 0.0, 1.0 ), V ) ) );
-
-				float layerHeight = 1.0 / numLayers;
-				float currentLayerHeight = 0.0;
-				// Shift of texture coordinates for each iteration
-				vec2 dtex = parallaxScale * V.xy / V.z / numLayers;
-
-				vec2 currentTextureCoords = vUv;
-
-				float heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;
-
-				// while ( heightFromTexture > currentLayerHeight )
-				// Infinite loops are not well supported. Do a "large" finite
-				// loop, but not too large, as it slows down some compilers.
-				for ( int i = 0; i < 30; i += 1 ) {
-					if ( heightFromTexture <= currentLayerHeight ) {
-						break;
-					}
-					currentLayerHeight += layerHeight;
-					// Shift texture coordinates along vector V
-					currentTextureCoords -= dtex;
-					heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;
-				}
-
-				#ifdef USE_STEEP_PARALLAX
-
-					return currentTextureCoords;
-
-				#elif defined( USE_RELIEF_PARALLAX )
-
-					vec2 deltaTexCoord = dtex / 2.0;
-					float deltaHeight = layerHeight / 2.0;
-
-					// Return to the mid point of previous layer
-					currentTextureCoords += deltaTexCoord;
-					currentLayerHeight -= deltaHeight;
-
-					// Binary search to increase precision of Steep Parallax Mapping
-					const int numSearches = 5;
-					for ( int i = 0; i < numSearches; i += 1 ) {
-
-						deltaTexCoord /= 2.0;
-						deltaHeight /= 2.0;
-						heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;
-						// Shift along or against vector V
-						if( heightFromTexture > currentLayerHeight ) { // Below the surface
-
-							currentTextureCoords -= deltaTexCoord;
-							currentLayerHeight += deltaHeight;
-
-						} else { // above the surface
-
-							currentTextureCoords += deltaTexCoord;
-							currentLayerHeight -= deltaHeight;
-
-						}
-
-					}
-					return currentTextureCoords;
-
-				#elif defined( USE_OCLUSION_PARALLAX )
-
-					vec2 prevTCoords = currentTextureCoords + dtex;
-
-					// Heights for linear interpolation
-					float nextH = heightFromTexture - currentLayerHeight;
-					float prevH = texture2D( bumpMap, prevTCoords ).r - currentLayerHeight + layerHeight;
-
-					// Proportions for linear interpolation
-					float weight = nextH / ( nextH - prevH );
-
-					// Interpolation of texture coordinates
-					return prevTCoords * weight + currentTextureCoords * ( 1.0 - weight );
-
-				#else // NO_PARALLAX
-
-					return vUv;
-
-				#endif
-
-			}
-		#endif
-
-		vec2 perturbUv( vec3 surfPosition, vec3 surfNormal, vec3 viewPosition ) {
-
- 			vec2 texDx = dFdx( vUv );
-			vec2 texDy = dFdy( vUv );
-
-			vec3 vSigmaX = dFdx( surfPosition );
-			vec3 vSigmaY = dFdy( surfPosition );
-			vec3 vR1 = cross( vSigmaY, surfNormal );
-			vec3 vR2 = cross( surfNormal, vSigmaX );
-			float fDet = dot( vSigmaX, vR1 );
-
-			vec2 vProjVscr = ( 1.0 / fDet ) * vec2( dot( vR1, viewPosition ), dot( vR2, viewPosition ) );
-			vec3 vProjVtex;
-			vProjVtex.xy = texDx * vProjVscr.x + texDy * vProjVscr.y;
-			vProjVtex.z = dot( surfNormal, viewPosition );
-
-			return parallaxMap( vProjVtex );
-		}
-
-		void main() {
-
-			vec2 mapUv = perturbUv( -vViewPosition, normalize( vNormal ), normalize( vViewPosition ) );
-			gl_FragColor = texture2D( map, mapUv );
-
-		}`
-	};
-
-	THREE.ParallaxShader = ParallaxShader;
-
-} )();

+ 484 - 480
examples/js/utils/BufferGeometryUtils.js

@@ -1,445 +1,441 @@
 ( function () {
 
-	class BufferGeometryUtils {
+	function computeTangents( geometry ) {
 
-		static computeTangents( geometry ) {
+		geometry.computeTangents();
+		console.warn( 'THREE.BufferGeometryUtils: .computeTangents() has been removed. Use THREE.BufferGeometry.computeTangents() instead.' );
 
-			geometry.computeTangents();
-			console.warn( 'THREE.BufferGeometryUtils: .computeTangents() has been removed. Use THREE.BufferGeometry.computeTangents() instead.' );
-
-		}
-		/**
-   * @param  {Array<BufferGeometry>} geometries
-   * @param  {Boolean} useGroups
-   * @return {BufferGeometry}
-   */
-
-
-		static mergeBufferGeometries( geometries, useGroups = false ) {
+	}
+	/**
+	 * @param  {Array<BufferGeometry>} geometries
+	 * @param  {Boolean} useGroups
+	 * @return {BufferGeometry}
+	 */
 
-			const isIndexed = geometries[ 0 ].index !== null;
-			const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) );
-			const morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) );
-			const attributes = {};
-			const morphAttributes = {};
-			const morphTargetsRelative = geometries[ 0 ].morphTargetsRelative;
-			const mergedGeometry = new THREE.BufferGeometry();
-			let offset = 0;
 
-			for ( let i = 0; i < geometries.length; ++ i ) {
+	function mergeBufferGeometries( geometries, useGroups = false ) {
 
-				const geometry = geometries[ i ];
-				let attributesCount = 0; // ensure that all geometries are indexed, or none
+		const isIndexed = geometries[ 0 ].index !== null;
+		const attributesUsed = new Set( Object.keys( geometries[ 0 ].attributes ) );
+		const morphAttributesUsed = new Set( Object.keys( geometries[ 0 ].morphAttributes ) );
+		const attributes = {};
+		const morphAttributes = {};
+		const morphTargetsRelative = geometries[ 0 ].morphTargetsRelative;
+		const mergedGeometry = new THREE.BufferGeometry();
+		let offset = 0;
 
-				if ( isIndexed !== ( geometry.index !== null ) ) {
+		for ( let i = 0; i < geometries.length; ++ i ) {
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' );
-					return null;
+			const geometry = geometries[ i ];
+			let attributesCount = 0; // ensure that all geometries are indexed, or none
 
-				} // gather attributes, exit early if they're different
+			if ( isIndexed !== ( geometry.index !== null ) ) {
 
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure index attribute exists among all geometries, or in none of them.' );
+				return null;
 
-				for ( const name in geometry.attributes ) {
+			} // gather attributes, exit early if they're different
 
-					if ( ! attributesUsed.has( name ) ) {
 
-						console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' );
-						return null;
+			for ( const name in geometry.attributes ) {
 
-					}
+				if ( ! attributesUsed.has( name ) ) {
 
-					if ( attributes[ name ] === undefined ) attributes[ name ] = [];
-					attributes[ name ].push( geometry.attributes[ name ] );
-					attributesCount ++;
+					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. All geometries must have compatible attributes; make sure "' + name + '" attribute exists among all geometries, or in none of them.' );
+					return null;
 
-				} // ensure geometries have the same number of attributes
+				}
 
+				if ( attributes[ name ] === undefined ) attributes[ name ] = [];
+				attributes[ name ].push( geometry.attributes[ name ] );
+				attributesCount ++;
 
-				if ( attributesCount !== attributesUsed.size ) {
+			} // ensure geometries have the same number of attributes
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' );
-					return null;
 
-				} // gather morph attributes, exit early if they're different
+			if ( attributesCount !== attributesUsed.size ) {
 
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. Make sure all geometries have the same number of attributes.' );
+				return null;
 
-				if ( morphTargetsRelative !== geometry.morphTargetsRelative ) {
+			} // gather morph attributes, exit early if they're different
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' );
-					return null;
 
-				}
+			if ( morphTargetsRelative !== geometry.morphTargetsRelative ) {
 
-				for ( const name in geometry.morphAttributes ) {
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. .morphTargetsRelative must be consistent throughout all geometries.' );
+				return null;
 
-					if ( ! morphAttributesUsed.has( name ) ) {
+			}
 
-						console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '.  .morphAttributes must be consistent throughout all geometries.' );
-						return null;
+			for ( const name in geometry.morphAttributes ) {
 
-					}
+				if ( ! morphAttributesUsed.has( name ) ) {
 
-					if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = [];
-					morphAttributes[ name ].push( geometry.morphAttributes[ name ] );
+					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '.  .morphAttributes must be consistent throughout all geometries.' );
+					return null;
 
-				} // gather .userData
+				}
 
+				if ( morphAttributes[ name ] === undefined ) morphAttributes[ name ] = [];
+				morphAttributes[ name ].push( geometry.morphAttributes[ name ] );
 
-				mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || [];
-				mergedGeometry.userData.mergedUserData.push( geometry.userData );
+			} // gather .userData
 
-				if ( useGroups ) {
 
-					let count;
+			mergedGeometry.userData.mergedUserData = mergedGeometry.userData.mergedUserData || [];
+			mergedGeometry.userData.mergedUserData.push( geometry.userData );
 
-					if ( isIndexed ) {
+			if ( useGroups ) {
 
-						count = geometry.index.count;
+				let count;
 
-					} else if ( geometry.attributes.position !== undefined ) {
+				if ( isIndexed ) {
 
-						count = geometry.attributes.position.count;
+					count = geometry.index.count;
 
-					} else {
+				} else if ( geometry.attributes.position !== undefined ) {
 
-						console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' );
-						return null;
+					count = geometry.attributes.position.count;
 
-					}
+				} else {
 
-					mergedGeometry.addGroup( offset, count, i );
-					offset += count;
+					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed with geometry at index ' + i + '. The geometry must have either an index or a position attribute' );
+					return null;
 
 				}
 
-			} // merge indices
+				mergedGeometry.addGroup( offset, count, i );
+				offset += count;
 
+			}
 
-			if ( isIndexed ) {
+		} // merge indices
 
-				let indexOffset = 0;
-				const mergedIndex = [];
 
-				for ( let i = 0; i < geometries.length; ++ i ) {
+		if ( isIndexed ) {
 
-					const index = geometries[ i ].index;
+			let indexOffset = 0;
+			const mergedIndex = [];
 
-					for ( let j = 0; j < index.count; ++ j ) {
+			for ( let i = 0; i < geometries.length; ++ i ) {
 
-						mergedIndex.push( index.getX( j ) + indexOffset );
+				const index = geometries[ i ].index;
 
-					}
+				for ( let j = 0; j < index.count; ++ j ) {
 
-					indexOffset += geometries[ i ].attributes.position.count;
+					mergedIndex.push( index.getX( j ) + indexOffset );
 
 				}
 
-				mergedGeometry.setIndex( mergedIndex );
+				indexOffset += geometries[ i ].attributes.position.count;
 
-			} // merge attributes
+			}
 
+			mergedGeometry.setIndex( mergedIndex );
 
-			for ( const name in attributes ) {
+		} // merge attributes
 
-				const mergedAttribute = this.mergeBufferAttributes( attributes[ name ] );
 
-				if ( ! mergedAttribute ) {
+		for ( const name in attributes ) {
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' attribute.' );
-					return null;
+			const mergedAttribute = mergeBufferAttributes( attributes[ name ] );
 
-				}
+			if ( ! mergedAttribute ) {
 
-				mergedGeometry.setAttribute( name, mergedAttribute );
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' attribute.' );
+				return null;
 
-			} // merge morph attributes
+			}
 
+			mergedGeometry.setAttribute( name, mergedAttribute );
 
-			for ( const name in morphAttributes ) {
+		} // merge morph attributes
 
-				const numMorphTargets = morphAttributes[ name ][ 0 ].length;
-				if ( numMorphTargets === 0 ) break;
-				mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {};
-				mergedGeometry.morphAttributes[ name ] = [];
 
-				for ( let i = 0; i < numMorphTargets; ++ i ) {
+		for ( const name in morphAttributes ) {
 
-					const morphAttributesToMerge = [];
+			const numMorphTargets = morphAttributes[ name ][ 0 ].length;
+			if ( numMorphTargets === 0 ) break;
+			mergedGeometry.morphAttributes = mergedGeometry.morphAttributes || {};
+			mergedGeometry.morphAttributes[ name ] = [];
 
-					for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) {
+			for ( let i = 0; i < numMorphTargets; ++ i ) {
 
-						morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] );
+				const morphAttributesToMerge = [];
 
-					}
+				for ( let j = 0; j < morphAttributes[ name ].length; ++ j ) {
 
-					const mergedMorphAttribute = this.mergeBufferAttributes( morphAttributesToMerge );
+					morphAttributesToMerge.push( morphAttributes[ name ][ j ][ i ] );
 
-					if ( ! mergedMorphAttribute ) {
+				}
 
-						console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' morphAttribute.' );
-						return null;
+				const mergedMorphAttribute = mergeBufferAttributes( morphAttributesToMerge );
 
-					}
+				if ( ! mergedMorphAttribute ) {
 
-					mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute );
+					console.error( 'THREE.BufferGeometryUtils: .mergeBufferGeometries() failed while trying to merge the ' + name + ' morphAttribute.' );
+					return null;
 
 				}
 
-			}
+				mergedGeometry.morphAttributes[ name ].push( mergedMorphAttribute );
 
-			return mergedGeometry;
+			}
 
 		}
-		/**
-   * @param {Array<BufferAttribute>} attributes
-   * @return {BufferAttribute}
-   */
 
+		return mergedGeometry;
 
-		static mergeBufferAttributes( attributes ) {
+	}
+	/**
+ * @param {Array<BufferAttribute>} attributes
+ * @return {BufferAttribute}
+ */
 
-			let TypedArray;
-			let itemSize;
-			let normalized;
-			let arrayLength = 0;
 
-			for ( let i = 0; i < attributes.length; ++ i ) {
+	function mergeBufferAttributes( attributes ) {
 
-				const attribute = attributes[ i ];
+		let TypedArray;
+		let itemSize;
+		let normalized;
+		let arrayLength = 0;
 
-				if ( attribute.isInterleavedBufferAttribute ) {
+		for ( let i = 0; i < attributes.length; ++ i ) {
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. InterleavedBufferAttributes are not supported.' );
-					return null;
+			const attribute = attributes[ i ];
 
-				}
+			if ( attribute.isInterleavedBufferAttribute ) {
 
-				if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. InterleavedBufferAttributes are not supported.' );
+				return null;
 
-				if ( TypedArray !== attribute.array.constructor ) {
+			}
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. THREE.BufferAttribute.array must be of consistent array types across matching attributes.' );
-					return null;
+			if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
 
-				}
+			if ( TypedArray !== attribute.array.constructor ) {
 
-				if ( itemSize === undefined ) itemSize = attribute.itemSize;
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. THREE.BufferAttribute.array must be of consistent array types across matching attributes.' );
+				return null;
 
-				if ( itemSize !== attribute.itemSize ) {
+			}
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. THREE.BufferAttribute.itemSize must be consistent across matching attributes.' );
-					return null;
+			if ( itemSize === undefined ) itemSize = attribute.itemSize;
 
-				}
+			if ( itemSize !== attribute.itemSize ) {
 
-				if ( normalized === undefined ) normalized = attribute.normalized;
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. THREE.BufferAttribute.itemSize must be consistent across matching attributes.' );
+				return null;
 
-				if ( normalized !== attribute.normalized ) {
+			}
 
-					console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. THREE.BufferAttribute.normalized must be consistent across matching attributes.' );
-					return null;
+			if ( normalized === undefined ) normalized = attribute.normalized;
 
-				}
+			if ( normalized !== attribute.normalized ) {
 
-				arrayLength += attribute.array.length;
+				console.error( 'THREE.BufferGeometryUtils: .mergeBufferAttributes() failed. THREE.BufferAttribute.normalized must be consistent across matching attributes.' );
+				return null;
 
 			}
 
-			const array = new TypedArray( arrayLength );
-			let offset = 0;
+			arrayLength += attribute.array.length;
 
-			for ( let i = 0; i < attributes.length; ++ i ) {
+		}
 
-				array.set( attributes[ i ].array, offset );
-				offset += attributes[ i ].array.length;
+		const array = new TypedArray( arrayLength );
+		let offset = 0;
 
-			}
+		for ( let i = 0; i < attributes.length; ++ i ) {
 
-			return new THREE.BufferAttribute( array, itemSize, normalized );
+			array.set( attributes[ i ].array, offset );
+			offset += attributes[ i ].array.length;
 
 		}
-		/**
-   * @param {Array<BufferAttribute>} attributes
-   * @return {Array<InterleavedBufferAttribute>}
-   */
 
+		return new THREE.BufferAttribute( array, itemSize, normalized );
 
-		static interleaveAttributes( attributes ) {
+	}
+	/**
+ * @param {Array<BufferAttribute>} attributes
+ * @return {Array<InterleavedBufferAttribute>}
+ */
 
-			// Interleaves the provided attributes into an THREE.InterleavedBuffer and returns
-			// a set of InterleavedBufferAttributes for each attribute
-			let TypedArray;
-			let arrayLength = 0;
-			let stride = 0; // calculate the the length and type of the interleavedBuffer
 
-			for ( let i = 0, l = attributes.length; i < l; ++ i ) {
+	function interleaveAttributes( attributes ) {
 
-				const attribute = attributes[ i ];
-				if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
+		// Interleaves the provided attributes into an THREE.InterleavedBuffer and returns
+		// a set of InterleavedBufferAttributes for each attribute
+		let TypedArray;
+		let arrayLength = 0;
+		let stride = 0; // calculate the the length and type of the interleavedBuffer
 
-				if ( TypedArray !== attribute.array.constructor ) {
+		for ( let i = 0, l = attributes.length; i < l; ++ i ) {
 
-					console.error( 'AttributeBuffers of different types cannot be interleaved' );
-					return null;
+			const attribute = attributes[ i ];
+			if ( TypedArray === undefined ) TypedArray = attribute.array.constructor;
 
-				}
+			if ( TypedArray !== attribute.array.constructor ) {
 
-				arrayLength += attribute.array.length;
-				stride += attribute.itemSize;
+				console.error( 'AttributeBuffers of different types cannot be interleaved' );
+				return null;
 
-			} // Create the set of buffer attributes
+			}
 
+			arrayLength += attribute.array.length;
+			stride += attribute.itemSize;
 
-			const interleavedBuffer = new THREE.InterleavedBuffer( new TypedArray( arrayLength ), stride );
-			let offset = 0;
-			const res = [];
-			const getters = [ 'getX', 'getY', 'getZ', 'getW' ];
-			const setters = [ 'setX', 'setY', 'setZ', 'setW' ];
+		} // Create the set of buffer attributes
 
-			for ( let j = 0, l = attributes.length; j < l; j ++ ) {
 
-				const attribute = attributes[ j ];
-				const itemSize = attribute.itemSize;
-				const count = attribute.count;
-				const iba = new THREE.InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, attribute.normalized );
-				res.push( iba );
-				offset += itemSize; // Move the data for each attribute into the new interleavedBuffer
-				// at the appropriate offset
+		const interleavedBuffer = new THREE.InterleavedBuffer( new TypedArray( arrayLength ), stride );
+		let offset = 0;
+		const res = [];
+		const getters = [ 'getX', 'getY', 'getZ', 'getW' ];
+		const setters = [ 'setX', 'setY', 'setZ', 'setW' ];
 
-				for ( let c = 0; c < count; c ++ ) {
+		for ( let j = 0, l = attributes.length; j < l; j ++ ) {
 
-					for ( let k = 0; k < itemSize; k ++ ) {
+			const attribute = attributes[ j ];
+			const itemSize = attribute.itemSize;
+			const count = attribute.count;
+			const iba = new THREE.InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, attribute.normalized );
+			res.push( iba );
+			offset += itemSize; // Move the data for each attribute into the new interleavedBuffer
+			// at the appropriate offset
 
-						iba[ setters[ k ] ]( c, attribute[ getters[ k ] ]( c ) );
+			for ( let c = 0; c < count; c ++ ) {
 
-					}
+				for ( let k = 0; k < itemSize; k ++ ) {
+
+					iba[ setters[ k ] ]( c, attribute[ getters[ k ] ]( c ) );
 
 				}
 
 			}
 
-			return res;
-
 		}
-		/**
-   * @param {Array<BufferGeometry>} geometry
-   * @return {number}
-   */
 
+		return res;
 
-		static estimateBytesUsed( geometry ) {
+	}
+	/**
+ * @param {Array<BufferGeometry>} geometry
+ * @return {number}
+ */
 
-			// Return the estimated memory used by this geometry in bytes
-			// Calculate using itemSize, count, and BYTES_PER_ELEMENT to account
-			// for InterleavedBufferAttributes.
-			let mem = 0;
 
-			for ( const name in geometry.attributes ) {
+	function estimateBytesUsed( geometry ) {
 
-				const attr = geometry.getAttribute( name );
-				mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT;
+		// Return the estimated memory used by this geometry in bytes
+		// Calculate using itemSize, count, and BYTES_PER_ELEMENT to account
+		// for InterleavedBufferAttributes.
+		let mem = 0;
 
-			}
+		for ( const name in geometry.attributes ) {
 
-			const indices = geometry.getIndex();
-			mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0;
-			return mem;
+			const attr = geometry.getAttribute( name );
+			mem += attr.count * attr.itemSize * attr.array.BYTES_PER_ELEMENT;
 
 		}
-		/**
-   * @param {BufferGeometry} geometry
-   * @param {number} tolerance
-   * @return {BufferGeometry>}
-   */
 
+		const indices = geometry.getIndex();
+		mem += indices ? indices.count * indices.itemSize * indices.array.BYTES_PER_ELEMENT : 0;
+		return mem;
 
-		static mergeVertices( geometry, tolerance = 1e-4 ) {
+	}
+	/**
+ * @param {BufferGeometry} geometry
+ * @param {number} tolerance
+ * @return {BufferGeometry>}
+ */
 
-			tolerance = Math.max( tolerance, Number.EPSILON ); // Generate an index buffer if the geometry doesn't have one, or optimize it
-			// if it's already available.
 
-			const hashToIndex = {};
-			const indices = geometry.getIndex();
-			const positions = geometry.getAttribute( 'position' );
-			const vertexCount = indices ? indices.count : positions.count; // next value for triangle indices
+	function mergeVertices( geometry, tolerance = 1e-4 ) {
 
-			let nextIndex = 0; // attributes and new attribute arrays
+		tolerance = Math.max( tolerance, Number.EPSILON ); // Generate an index buffer if the geometry doesn't have one, or optimize it
+		// if it's already available.
 
-			const attributeNames = Object.keys( geometry.attributes );
-			const attrArrays = {};
-			const morphAttrsArrays = {};
-			const newIndices = [];
-			const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; // initialize the arrays
+		const hashToIndex = {};
+		const indices = geometry.getIndex();
+		const positions = geometry.getAttribute( 'position' );
+		const vertexCount = indices ? indices.count : positions.count; // next value for triangle indices
 
-			for ( let i = 0, l = attributeNames.length; i < l; i ++ ) {
+		let nextIndex = 0; // attributes and new attribute arrays
 
-				const name = attributeNames[ i ];
-				attrArrays[ name ] = [];
-				const morphAttr = geometry.morphAttributes[ name ];
+		const attributeNames = Object.keys( geometry.attributes );
+		const attrArrays = {};
+		const morphAttrsArrays = {};
+		const newIndices = [];
+		const getters = [ 'getX', 'getY', 'getZ', 'getW' ]; // initialize the arrays
 
-				if ( morphAttr ) {
+		for ( let i = 0, l = attributeNames.length; i < l; i ++ ) {
 
-					morphAttrsArrays[ name ] = new Array( morphAttr.length ).fill().map( () => [] );
+			const name = attributeNames[ i ];
+			attrArrays[ name ] = [];
+			const morphAttr = geometry.morphAttributes[ name ];
 
-				}
+			if ( morphAttr ) {
 
-			} // convert the error tolerance to an amount of decimal places to truncate to
+				morphAttrsArrays[ name ] = new Array( morphAttr.length ).fill().map( () => [] );
 
+			}
 
-			const decimalShift = Math.log10( 1 / tolerance );
-			const shiftMultiplier = Math.pow( 10, decimalShift );
+		} // convert the error tolerance to an amount of decimal places to truncate to
 
-			for ( let i = 0; i < vertexCount; i ++ ) {
 
-				const index = indices ? indices.getX( i ) : i; // Generate a hash for the vertex attributes at the current index 'i'
+		const decimalShift = Math.log10( 1 / tolerance );
+		const shiftMultiplier = Math.pow( 10, decimalShift );
 
-				let hash = '';
+		for ( let i = 0; i < vertexCount; i ++ ) {
 
-				for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
+			const index = indices ? indices.getX( i ) : i; // Generate a hash for the vertex attributes at the current index 'i'
 
-					const name = attributeNames[ j ];
-					const attribute = geometry.getAttribute( name );
-					const itemSize = attribute.itemSize;
+			let hash = '';
 
-					for ( let k = 0; k < itemSize; k ++ ) {
+			for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
 
-						// double tilde truncates the decimal value
-						hash += `${~ ~ ( attribute[ getters[ k ] ]( index ) * shiftMultiplier )},`;
+				const name = attributeNames[ j ];
+				const attribute = geometry.getAttribute( name );
+				const itemSize = attribute.itemSize;
 
-					}
+				for ( let k = 0; k < itemSize; k ++ ) {
 
-				} // Add another reference to the vertex if it's already
-				// used by another index
+					// double tilde truncates the decimal value
+					hash += `${~ ~ ( attribute[ getters[ k ] ]( index ) * shiftMultiplier )},`;
 
+				}
 
-				if ( hash in hashToIndex ) {
+			} // Add another reference to the vertex if it's already
+			// used by another index
 
-					newIndices.push( hashToIndex[ hash ] );
 
-				} else {
+			if ( hash in hashToIndex ) {
 
-					// copy data to the new index in the attribute arrays
-					for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
+				newIndices.push( hashToIndex[ hash ] );
 
-						const name = attributeNames[ j ];
-						const attribute = geometry.getAttribute( name );
-						const morphAttr = geometry.morphAttributes[ name ];
-						const itemSize = attribute.itemSize;
-						const newarray = attrArrays[ name ];
-						const newMorphArrays = morphAttrsArrays[ name ];
+			} else {
 
-						for ( let k = 0; k < itemSize; k ++ ) {
+				// copy data to the new index in the attribute arrays
+				for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
 
-							const getterFunc = getters[ k ];
-							newarray.push( attribute[ getterFunc ]( index ) );
+					const name = attributeNames[ j ];
+					const attribute = geometry.getAttribute( name );
+					const morphAttr = geometry.morphAttributes[ name ];
+					const itemSize = attribute.itemSize;
+					const newarray = attrArrays[ name ];
+					const newMorphArrays = morphAttrsArrays[ name ];
 
-							if ( morphAttr ) {
+					for ( let k = 0; k < itemSize; k ++ ) {
 
-								for ( let m = 0, ml = morphAttr.length; m < ml; m ++ ) {
+						const getterFunc = getters[ k ];
+						newarray.push( attribute[ getterFunc ]( index ) );
 
-									newMorphArrays[ m ].push( morphAttr[ m ][ getterFunc ]( index ) );
+						if ( morphAttr ) {
 
-								}
+							for ( let m = 0, ml = morphAttr.length; m < ml; m ++ ) {
+
+								newMorphArrays[ m ].push( morphAttr[ m ][ getterFunc ]( index ) );
 
 							}
 
@@ -447,384 +443,392 @@
 
 					}
 
-					hashToIndex[ hash ] = nextIndex;
-					newIndices.push( nextIndex );
-					nextIndex ++;
-
 				}
 
-			} // Generate typed arrays from new attribute arrays and update
-			// the attributeBuffers
+				hashToIndex[ hash ] = nextIndex;
+				newIndices.push( nextIndex );
+				nextIndex ++;
 
+			}
 
-			const result = geometry.clone();
+		} // Generate typed arrays from new attribute arrays and update
+		// the attributeBuffers
 
-			for ( let i = 0, l = attributeNames.length; i < l; i ++ ) {
 
-				const name = attributeNames[ i ];
-				const oldAttribute = geometry.getAttribute( name );
-				const buffer = new oldAttribute.array.constructor( attrArrays[ name ] );
-				const attribute = new THREE.BufferAttribute( buffer, oldAttribute.itemSize, oldAttribute.normalized );
-				result.setAttribute( name, attribute ); // Update the attribute arrays
+		const result = geometry.clone();
 
-				if ( name in morphAttrsArrays ) {
+		for ( let i = 0, l = attributeNames.length; i < l; i ++ ) {
 
-					for ( let j = 0; j < morphAttrsArrays[ name ].length; j ++ ) {
+			const name = attributeNames[ i ];
+			const oldAttribute = geometry.getAttribute( name );
+			const buffer = new oldAttribute.array.constructor( attrArrays[ name ] );
+			const attribute = new THREE.BufferAttribute( buffer, oldAttribute.itemSize, oldAttribute.normalized );
+			result.setAttribute( name, attribute ); // Update the attribute arrays
 
-						const oldMorphAttribute = geometry.morphAttributes[ name ][ j ];
-						const buffer = new oldMorphAttribute.array.constructor( morphAttrsArrays[ name ][ j ] );
-						const morphAttribute = new THREE.BufferAttribute( buffer, oldMorphAttribute.itemSize, oldMorphAttribute.normalized );
-						result.morphAttributes[ name ][ j ] = morphAttribute;
+			if ( name in morphAttrsArrays ) {
 
-					}
+				for ( let j = 0; j < morphAttrsArrays[ name ].length; j ++ ) {
+
+					const oldMorphAttribute = geometry.morphAttributes[ name ][ j ];
+					const buffer = new oldMorphAttribute.array.constructor( morphAttrsArrays[ name ][ j ] );
+					const morphAttribute = new THREE.BufferAttribute( buffer, oldMorphAttribute.itemSize, oldMorphAttribute.normalized );
+					result.morphAttributes[ name ][ j ] = morphAttribute;
 
 				}
 
-			} // indices
+			}
 
+		} // indices
 
-			result.setIndex( newIndices );
-			return result;
 
-		}
-		/**
-   * @param {BufferGeometry} geometry
-   * @param {number} drawMode
-   * @return {BufferGeometry>}
-   */
+		result.setIndex( newIndices );
+		return result;
 
+	}
+	/**
+ * @param {BufferGeometry} geometry
+ * @param {number} drawMode
+ * @return {BufferGeometry>}
+ */
 
-		static toTrianglesDrawMode( geometry, drawMode ) {
 
-			if ( drawMode === THREE.TrianglesDrawMode ) {
+	function toTrianglesDrawMode( geometry, drawMode ) {
 
-				console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' );
-				return geometry;
+		if ( drawMode === THREE.TrianglesDrawMode ) {
 
-			}
+			console.warn( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Geometry already defined as triangles.' );
+			return geometry;
 
-			if ( drawMode === THREE.TriangleFanDrawMode || drawMode === THREE.TriangleStripDrawMode ) {
+		}
 
-				let index = geometry.getIndex(); // generate index if not present
+		if ( drawMode === THREE.TriangleFanDrawMode || drawMode === THREE.TriangleStripDrawMode ) {
 
-				if ( index === null ) {
+			let index = geometry.getIndex(); // generate index if not present
 
-					const indices = [];
-					const position = geometry.getAttribute( 'position' );
+			if ( index === null ) {
 
-					if ( position !== undefined ) {
+				const indices = [];
+				const position = geometry.getAttribute( 'position' );
 
-						for ( let i = 0; i < position.count; i ++ ) {
+				if ( position !== undefined ) {
 
-							indices.push( i );
+					for ( let i = 0; i < position.count; i ++ ) {
 
-						}
+						indices.push( i );
 
-						geometry.setIndex( indices );
-						index = geometry.getIndex();
+					}
 
-					} else {
+					geometry.setIndex( indices );
+					index = geometry.getIndex();
 
-						console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
-						return geometry;
+				} else {
 
-					}
+					console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Undefined position attribute. Processing not possible.' );
+					return geometry;
 
-				} //
+				}
 
+			} //
 
-				const numberOfTriangles = index.count - 2;
-				const newIndices = [];
 
-				if ( drawMode === THREE.TriangleFanDrawMode ) {
+			const numberOfTriangles = index.count - 2;
+			const newIndices = [];
 
-					// gl.TRIANGLE_FAN
-					for ( let i = 1; i <= numberOfTriangles; i ++ ) {
+			if ( drawMode === THREE.TriangleFanDrawMode ) {
 
-						newIndices.push( index.getX( 0 ) );
-						newIndices.push( index.getX( i ) );
-						newIndices.push( index.getX( i + 1 ) );
+				// 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 ++ ) {
+			} else {
 
-						if ( i % 2 === 0 ) {
+				// gl.TRIANGLE_STRIP
+				for ( let i = 0; i < numberOfTriangles; i ++ ) {
 
-							newIndices.push( index.getX( i ) );
-							newIndices.push( index.getX( i + 1 ) );
-							newIndices.push( index.getX( i + 2 ) );
+					if ( i % 2 === 0 ) {
 
-						} else {
+						newIndices.push( index.getX( i ) );
+						newIndices.push( index.getX( i + 1 ) );
+						newIndices.push( index.getX( i + 2 ) );
 
-							newIndices.push( index.getX( i + 2 ) );
-							newIndices.push( index.getX( i + 1 ) );
-							newIndices.push( index.getX( i ) );
+					} 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.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' );
+			if ( newIndices.length / 3 !== numberOfTriangles ) {
 
-				} // build final geometry
+				console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unable to generate correct amount of triangles.' );
 
+			} // build final geometry
 
-				const newGeometry = geometry.clone();
-				newGeometry.setIndex( newIndices );
-				newGeometry.clearGroups();
-				return newGeometry;
 
-			} else {
+			const newGeometry = geometry.clone();
+			newGeometry.setIndex( newIndices );
+			newGeometry.clearGroups();
+			return newGeometry;
 
-				console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode );
-				return geometry;
+		} else {
 
-			}
+			console.error( 'THREE.BufferGeometryUtils.toTrianglesDrawMode(): Unknown draw mode:', drawMode );
+			return geometry;
 
 		}
-		/**
-   * Calculates the morphed attributes of a morphed/skinned THREE.BufferGeometry.
-   * Helpful for Raytracing or Decals.
-   * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points.
-   * @return {Object} An Object with original position/normal attributes and morphed ones.
-   */
 
+	}
+	/**
+ * Calculates the morphed attributes of a morphed/skinned THREE.BufferGeometry.
+ * Helpful for Raytracing or Decals.
+ * @param {Mesh | Line | Points} object An instance of Mesh, Line or Points.
+ * @return {Object} An Object with original position/normal attributes and morphed ones.
+ */
 
-		static computeMorphedAttributes( object ) {
 
-			if ( object.geometry.isBufferGeometry !== true ) {
+	function computeMorphedAttributes( object ) {
 
-				console.error( 'THREE.BufferGeometryUtils: Geometry is not of type THREE.BufferGeometry.' );
-				return null;
+		if ( object.geometry.isBufferGeometry !== true ) {
 
-			}
+			console.error( 'THREE.BufferGeometryUtils: Geometry is not of type THREE.BufferGeometry.' );
+			return null;
 
-			const _vA = new THREE.Vector3();
+		}
 
-			const _vB = new THREE.Vector3();
+		const _vA = new THREE.Vector3();
 
-			const _vC = new THREE.Vector3();
+		const _vB = new THREE.Vector3();
 
-			const _tempA = new THREE.Vector3();
+		const _vC = new THREE.Vector3();
 
-			const _tempB = new THREE.Vector3();
+		const _tempA = new THREE.Vector3();
 
-			const _tempC = new THREE.Vector3();
+		const _tempB = new THREE.Vector3();
 
-			const _morphA = new THREE.Vector3();
+		const _tempC = new THREE.Vector3();
 
-			const _morphB = new THREE.Vector3();
+		const _morphA = new THREE.Vector3();
 
-			const _morphC = new THREE.Vector3();
+		const _morphB = new THREE.Vector3();
 
-			function _calculateMorphedAttributeData( object, material, attribute, morphAttribute, morphTargetsRelative, a, b, c, modifiedAttributeArray ) {
+		const _morphC = new THREE.Vector3();
 
-				_vA.fromBufferAttribute( attribute, a );
+		function _calculateMorphedAttributeData( object, material, attribute, morphAttribute, morphTargetsRelative, a, b, c, modifiedAttributeArray ) {
 
-				_vB.fromBufferAttribute( attribute, b );
+			_vA.fromBufferAttribute( attribute, a );
 
-				_vC.fromBufferAttribute( attribute, c );
+			_vB.fromBufferAttribute( attribute, b );
 
-				const morphInfluences = object.morphTargetInfluences;
+			_vC.fromBufferAttribute( attribute, c );
 
-				if ( material.morphTargets && morphAttribute && morphInfluences ) {
+			const morphInfluences = object.morphTargetInfluences;
 
-					_morphA.set( 0, 0, 0 );
+			if ( material.morphTargets && morphAttribute && morphInfluences ) {
 
-					_morphB.set( 0, 0, 0 );
+				_morphA.set( 0, 0, 0 );
 
-					_morphC.set( 0, 0, 0 );
+				_morphB.set( 0, 0, 0 );
 
-					for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {
+				_morphC.set( 0, 0, 0 );
 
-						const influence = morphInfluences[ i ];
-						const morph = morphAttribute[ i ];
-						if ( influence === 0 ) continue;
+				for ( let i = 0, il = morphAttribute.length; i < il; i ++ ) {
 
-						_tempA.fromBufferAttribute( morph, a );
+					const influence = morphInfluences[ i ];
+					const morph = morphAttribute[ i ];
+					if ( influence === 0 ) continue;
 
-						_tempB.fromBufferAttribute( morph, b );
+					_tempA.fromBufferAttribute( morph, a );
 
-						_tempC.fromBufferAttribute( morph, c );
+					_tempB.fromBufferAttribute( morph, b );
 
-						if ( morphTargetsRelative ) {
+					_tempC.fromBufferAttribute( morph, c );
 
-							_morphA.addScaledVector( _tempA, influence );
+					if ( morphTargetsRelative ) {
 
-							_morphB.addScaledVector( _tempB, influence );
+						_morphA.addScaledVector( _tempA, influence );
 
-							_morphC.addScaledVector( _tempC, influence );
+						_morphB.addScaledVector( _tempB, influence );
 
-						} else {
+						_morphC.addScaledVector( _tempC, influence );
 
-							_morphA.addScaledVector( _tempA.sub( _vA ), influence );
+					} else {
 
-							_morphB.addScaledVector( _tempB.sub( _vB ), influence );
+						_morphA.addScaledVector( _tempA.sub( _vA ), influence );
 
-							_morphC.addScaledVector( _tempC.sub( _vC ), influence );
+						_morphB.addScaledVector( _tempB.sub( _vB ), influence );
 
-						}
+						_morphC.addScaledVector( _tempC.sub( _vC ), influence );
 
 					}
 
-					_vA.add( _morphA );
-
-					_vB.add( _morphB );
+				}
 
-					_vC.add( _morphC );
+				_vA.add( _morphA );
 
-				}
+				_vB.add( _morphB );
 
-				if ( object.isSkinnedMesh ) {
+				_vC.add( _morphC );
 
-					object.boneTransform( a, _vA );
-					object.boneTransform( b, _vB );
-					object.boneTransform( c, _vC );
+			}
 
-				}
+			if ( object.isSkinnedMesh ) {
 
-				modifiedAttributeArray[ a * 3 + 0 ] = _vA.x;
-				modifiedAttributeArray[ a * 3 + 1 ] = _vA.y;
-				modifiedAttributeArray[ a * 3 + 2 ] = _vA.z;
-				modifiedAttributeArray[ b * 3 + 0 ] = _vB.x;
-				modifiedAttributeArray[ b * 3 + 1 ] = _vB.y;
-				modifiedAttributeArray[ b * 3 + 2 ] = _vB.z;
-				modifiedAttributeArray[ c * 3 + 0 ] = _vC.x;
-				modifiedAttributeArray[ c * 3 + 1 ] = _vC.y;
-				modifiedAttributeArray[ c * 3 + 2 ] = _vC.z;
+				object.boneTransform( a, _vA );
+				object.boneTransform( b, _vB );
+				object.boneTransform( c, _vC );
 
 			}
 
-			const geometry = object.geometry;
-			const material = object.material;
-			let a, b, c;
-			const index = geometry.index;
-			const positionAttribute = geometry.attributes.position;
-			const morphPosition = geometry.morphAttributes.position;
-			const morphTargetsRelative = geometry.morphTargetsRelative;
-			const normalAttribute = geometry.attributes.normal;
-			const morphNormal = geometry.morphAttributes.position;
-			const groups = geometry.groups;
-			const drawRange = geometry.drawRange;
-			let i, j, il, jl;
-			let group, groupMaterial;
-			let start, end;
-			const modifiedPosition = new Float32Array( positionAttribute.count * positionAttribute.itemSize );
-			const modifiedNormal = new Float32Array( normalAttribute.count * normalAttribute.itemSize );
+			modifiedAttributeArray[ a * 3 + 0 ] = _vA.x;
+			modifiedAttributeArray[ a * 3 + 1 ] = _vA.y;
+			modifiedAttributeArray[ a * 3 + 2 ] = _vA.z;
+			modifiedAttributeArray[ b * 3 + 0 ] = _vB.x;
+			modifiedAttributeArray[ b * 3 + 1 ] = _vB.y;
+			modifiedAttributeArray[ b * 3 + 2 ] = _vB.z;
+			modifiedAttributeArray[ c * 3 + 0 ] = _vC.x;
+			modifiedAttributeArray[ c * 3 + 1 ] = _vC.y;
+			modifiedAttributeArray[ c * 3 + 2 ] = _vC.z;
 
-			if ( index !== null ) {
+		}
 
-				// indexed buffer geometry
-				if ( Array.isArray( material ) ) {
+		const geometry = object.geometry;
+		const material = object.material;
+		let a, b, c;
+		const index = geometry.index;
+		const positionAttribute = geometry.attributes.position;
+		const morphPosition = geometry.morphAttributes.position;
+		const morphTargetsRelative = geometry.morphTargetsRelative;
+		const normalAttribute = geometry.attributes.normal;
+		const morphNormal = geometry.morphAttributes.position;
+		const groups = geometry.groups;
+		const drawRange = geometry.drawRange;
+		let i, j, il, jl;
+		let group, groupMaterial;
+		let start, end;
+		const modifiedPosition = new Float32Array( positionAttribute.count * positionAttribute.itemSize );
+		const modifiedNormal = new Float32Array( normalAttribute.count * normalAttribute.itemSize );
 
-					for ( i = 0, il = groups.length; i < il; i ++ ) {
+		if ( index !== null ) {
 
-						group = groups[ i ];
-						groupMaterial = material[ group.materialIndex ];
-						start = Math.max( group.start, drawRange.start );
-						end = Math.min( group.start + group.count, drawRange.start + drawRange.count );
+			// indexed buffer geometry
+			if ( Array.isArray( material ) ) {
 
-						for ( j = start, jl = end; j < jl; j += 3 ) {
+				for ( i = 0, il = groups.length; i < il; i ++ ) {
 
-							a = index.getX( j );
-							b = index.getX( j + 1 );
-							c = index.getX( j + 2 );
+					group = groups[ i ];
+					groupMaterial = material[ group.materialIndex ];
+					start = Math.max( group.start, drawRange.start );
+					end = Math.min( group.start + group.count, drawRange.start + drawRange.count );
 
-							_calculateMorphedAttributeData( object, groupMaterial, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
+					for ( j = start, jl = end; j < jl; j += 3 ) {
 
-							_calculateMorphedAttributeData( object, groupMaterial, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
+						a = index.getX( j );
+						b = index.getX( j + 1 );
+						c = index.getX( j + 2 );
 
-						}
+						_calculateMorphedAttributeData( object, groupMaterial, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
+
+						_calculateMorphedAttributeData( object, groupMaterial, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
 
 					}
 
-				} else {
+				}
 
-					start = Math.max( 0, drawRange.start );
-					end = Math.min( index.count, drawRange.start + drawRange.count );
+			} else {
 
-					for ( i = start, il = end; i < il; i += 3 ) {
+				start = Math.max( 0, drawRange.start );
+				end = Math.min( index.count, drawRange.start + drawRange.count );
 
-						a = index.getX( i );
-						b = index.getX( i + 1 );
-						c = index.getX( i + 2 );
+				for ( i = start, il = end; i < il; i += 3 ) {
 
-						_calculateMorphedAttributeData( object, material, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
+					a = index.getX( i );
+					b = index.getX( i + 1 );
+					c = index.getX( i + 2 );
 
-						_calculateMorphedAttributeData( object, material, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
+					_calculateMorphedAttributeData( object, material, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
 
-					}
+					_calculateMorphedAttributeData( object, material, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
 
 				}
 
-			} else if ( positionAttribute !== undefined ) {
+			}
 
-				// non-indexed buffer geometry
-				if ( Array.isArray( material ) ) {
+		} else if ( positionAttribute !== undefined ) {
 
-					for ( i = 0, il = groups.length; i < il; i ++ ) {
+			// non-indexed buffer geometry
+			if ( Array.isArray( material ) ) {
 
-						group = groups[ i ];
-						groupMaterial = material[ group.materialIndex ];
-						start = Math.max( group.start, drawRange.start );
-						end = Math.min( group.start + group.count, drawRange.start + drawRange.count );
+				for ( i = 0, il = groups.length; i < il; i ++ ) {
 
-						for ( j = start, jl = end; j < jl; j += 3 ) {
+					group = groups[ i ];
+					groupMaterial = material[ group.materialIndex ];
+					start = Math.max( group.start, drawRange.start );
+					end = Math.min( group.start + group.count, drawRange.start + drawRange.count );
 
-							a = j;
-							b = j + 1;
-							c = j + 2;
+					for ( j = start, jl = end; j < jl; j += 3 ) {
 
-							_calculateMorphedAttributeData( object, groupMaterial, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
+						a = j;
+						b = j + 1;
+						c = j + 2;
 
-							_calculateMorphedAttributeData( object, groupMaterial, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
+						_calculateMorphedAttributeData( object, groupMaterial, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
 
-						}
+						_calculateMorphedAttributeData( object, groupMaterial, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
 
 					}
 
-				} else {
+				}
 
-					start = Math.max( 0, drawRange.start );
-					end = Math.min( positionAttribute.count, drawRange.start + drawRange.count );
+			} else {
 
-					for ( i = start, il = end; i < il; i += 3 ) {
+				start = Math.max( 0, drawRange.start );
+				end = Math.min( positionAttribute.count, drawRange.start + drawRange.count );
 
-						a = i;
-						b = i + 1;
-						c = i + 2;
+				for ( i = start, il = end; i < il; i += 3 ) {
 
-						_calculateMorphedAttributeData( object, material, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
+					a = i;
+					b = i + 1;
+					c = i + 2;
 
-						_calculateMorphedAttributeData( object, material, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
+					_calculateMorphedAttributeData( object, material, positionAttribute, morphPosition, morphTargetsRelative, a, b, c, modifiedPosition );
 
-					}
+					_calculateMorphedAttributeData( object, material, normalAttribute, morphNormal, morphTargetsRelative, a, b, c, modifiedNormal );
 
 				}
 
 			}
 
-			const morphedPositionAttribute = new THREE.Float32BufferAttribute( modifiedPosition, 3 );
-			const morphedNormalAttribute = new THREE.Float32BufferAttribute( modifiedNormal, 3 );
-			return {
-				positionAttribute: positionAttribute,
-				normalAttribute: normalAttribute,
-				morphedPositionAttribute: morphedPositionAttribute,
-				morphedNormalAttribute: morphedNormalAttribute
-			};
-
 		}
 
+		const morphedPositionAttribute = new THREE.Float32BufferAttribute( modifiedPosition, 3 );
+		const morphedNormalAttribute = new THREE.Float32BufferAttribute( modifiedNormal, 3 );
+		return {
+			positionAttribute: positionAttribute,
+			normalAttribute: normalAttribute,
+			morphedPositionAttribute: morphedPositionAttribute,
+			morphedNormalAttribute: morphedNormalAttribute
+		};
+
 	}
 
-	THREE.BufferGeometryUtils = BufferGeometryUtils;
+	THREE.BufferGeometryUtils = {};
+	THREE.BufferGeometryUtils.computeMorphedAttributes = computeMorphedAttributes;
+	THREE.BufferGeometryUtils.computeTangents = computeTangents;
+	THREE.BufferGeometryUtils.estimateBytesUsed = estimateBytesUsed;
+	THREE.BufferGeometryUtils.interleaveAttributes = interleaveAttributes;
+	THREE.BufferGeometryUtils.mergeBufferAttributes = mergeBufferAttributes;
+	THREE.BufferGeometryUtils.mergeBufferGeometries = mergeBufferGeometries;
+	THREE.BufferGeometryUtils.mergeVertices = mergeVertices;
+	THREE.BufferGeometryUtils.toTrianglesDrawMode = toTrianglesDrawMode;
 
 } )();

+ 37 - 39
examples/js/utils/CameraUtils.js

@@ -16,73 +16,71 @@
 		// temporary vector
 		_quat = /*@__PURE__*/new THREE.Quaternion(); // temporary quaternion
 
+	/** Set a PerspectiveCamera's projectionMatrix and quaternion
+ * to exactly frame the corners of an arbitrary rectangle.
+ * NOTE: This function ignores the standard parameters;
+ * do not call updateProjectionMatrix() after this!
+ * @param {Vector3} bottomLeftCorner
+ * @param {Vector3} bottomRightCorner
+ * @param {Vector3} topLeftCorner
+ * @param {boolean} estimateViewFrustum */
 
-	class CameraUtils {
 
-		/** Set a PerspectiveCamera's projectionMatrix and quaternion
-   * to exactly frame the corners of an arbitrary rectangle.
-   * NOTE: This function ignores the standard parameters;
-   * do not call updateProjectionMatrix() after this!
-   * @param {Vector3} bottomLeftCorner
-   * @param {Vector3} bottomRightCorner
-   * @param {Vector3} topLeftCorner
-   * @param {boolean} estimateViewFrustum */
-		static frameCorners( camera, bottomLeftCorner, bottomRightCorner, topLeftCorner, estimateViewFrustum = false ) {
+	function frameCorners( camera, bottomLeftCorner, bottomRightCorner, topLeftCorner, estimateViewFrustum = false ) {
 
-			const pa = bottomLeftCorner,
-				pb = bottomRightCorner,
-				pc = topLeftCorner;
-			const pe = camera.position; // eye position
+		const pa = bottomLeftCorner,
+			pb = bottomRightCorner,
+			pc = topLeftCorner;
+		const pe = camera.position; // eye position
 
-			const n = camera.near; // distance of near clipping plane
+		const n = camera.near; // distance of near clipping plane
 
-			const f = camera.far; //distance of far clipping plane
+		const f = camera.far; //distance of far clipping plane
 
-			_vr.copy( pb ).sub( pa ).normalize();
+		_vr.copy( pb ).sub( pa ).normalize();
 
-			_vu.copy( pc ).sub( pa ).normalize();
+		_vu.copy( pc ).sub( pa ).normalize();
 
-			_vn.crossVectors( _vr, _vu ).normalize();
+		_vn.crossVectors( _vr, _vu ).normalize();
 
-			_va.copy( pa ).sub( pe ); // from pe to pa
+		_va.copy( pa ).sub( pe ); // from pe to pa
 
 
-			_vb.copy( pb ).sub( pe ); // from pe to pb
+		_vb.copy( pb ).sub( pe ); // from pe to pb
 
 
-			_vc.copy( pc ).sub( pe ); // from pe to pc
+		_vc.copy( pc ).sub( pe ); // from pe to pc
 
 
-			const d = - _va.dot( _vn ); // distance from eye to screen
+		const d = - _va.dot( _vn ); // distance from eye to screen
 
-			const l = _vr.dot( _va ) * n / d; // distance to left screen edge
+		const l = _vr.dot( _va ) * n / d; // distance to left screen edge
 
-			const r = _vr.dot( _vb ) * n / d; // distance to right screen edge
+		const r = _vr.dot( _vb ) * n / d; // distance to right screen edge
 
-			const b = _vu.dot( _va ) * n / d; // distance to bottom screen edge
+		const b = _vu.dot( _va ) * n / d; // distance to bottom screen edge
 
-			const t = _vu.dot( _vc ) * n / d; // distance to top screen edge
-			// Set the camera rotation to match the focal plane to the corners' plane
+		const t = _vu.dot( _vc ) * n / d; // distance to top screen edge
+		// Set the camera rotation to match the focal plane to the corners' plane
 
-			_quat.setFromUnitVectors( _vec.set( 0, 1, 0 ), _vu );
+		_quat.setFromUnitVectors( _vec.set( 0, 1, 0 ), _vu );
 
-			camera.quaternion.setFromUnitVectors( _vec.set( 0, 0, 1 ).applyQuaternion( _quat ), _vn ).multiply( _quat ); // Set the off-axis projection matrix to match the corners
+		camera.quaternion.setFromUnitVectors( _vec.set( 0, 0, 1 ).applyQuaternion( _quat ), _vn ).multiply( _quat ); // Set the off-axis projection matrix to match the corners
 
-			camera.projectionMatrix.set( 2.0 * n / ( r - l ), 0.0, ( r + l ) / ( r - l ), 0.0, 0.0, 2.0 * n / ( t - b ), ( t + b ) / ( t - b ), 0.0, 0.0, 0.0, ( f + n ) / ( n - f ), 2.0 * f * n / ( n - f ), 0.0, 0.0, - 1.0, 0.0 );
-			camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); // FoV estimation to fix frustum culling
+		camera.projectionMatrix.set( 2.0 * n / ( r - l ), 0.0, ( r + l ) / ( r - l ), 0.0, 0.0, 2.0 * n / ( t - b ), ( t + b ) / ( t - b ), 0.0, 0.0, 0.0, ( f + n ) / ( n - f ), 2.0 * f * n / ( n - f ), 0.0, 0.0, - 1.0, 0.0 );
+		camera.projectionMatrixInverse.copy( camera.projectionMatrix ).invert(); // FoV estimation to fix frustum culling
 
-			if ( estimateViewFrustum ) {
+		if ( estimateViewFrustum ) {
 
-				// Set fieldOfView to a conservative estimate
-				// to make frustum tall/wide enough to encompass it
-				camera.fov = THREE.MathUtils.RAD2DEG / Math.min( 1.0, camera.aspect ) * Math.atan( ( _vec.copy( pb ).sub( pa ).length() + _vec.copy( pc ).sub( pa ).length() ) / _va.length() );
-
-			}
+			// Set fieldOfView to a conservative estimate
+			// to make frustum tall/wide enough to encompass it
+			camera.fov = THREE.MathUtils.RAD2DEG / Math.min( 1.0, camera.aspect ) * Math.atan( ( _vec.copy( pb ).sub( pa ).length() + _vec.copy( pc ).sub( pa ).length() ) / _va.length() );
 
 		}
 
 	}
 
-	THREE.CameraUtils = CameraUtils;
+	THREE.CameraUtils = {};
+	THREE.CameraUtils.frameCorners = frameCorners;
 
 } )();

+ 343 - 468
examples/js/utils/GeometryCompressionUtils.js

@@ -5,695 +5,570 @@
  *
  * @link https://github.com/tsherif/mesh-quantization-example
  *
+ */
+	/**
+ * Make the input mesh.geometry's normal attribute encoded and compressed by 3 different methods.
+ * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the normal data.
+ *
+ * @param {THREE.Mesh} mesh
+ * @param {String} encodeMethod		"DEFAULT" || "OCT1Byte" || "OCT2Byte" || "ANGLES"
+ *
  */
 
-	class GeometryCompressionUtils {
-
-		/**
-  	 * Make the input mesh.geometry's normal attribute encoded and compressed by 3 different methods.
-  	 * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the normal data.
-  	 *
-  	 * @param {THREE.Mesh} mesh
-  	 * @param {String} encodeMethod		"DEFAULT" || "OCT1Byte" || "OCT2Byte" || "ANGLES"
-  	 *
-  	 */
-		static compressNormals( mesh, encodeMethod ) {
-
-			if ( ! mesh.geometry ) {
-
-				console.error( 'Mesh must contain geometry. ' );
-
-			}
-
-			const normal = mesh.geometry.attributes.normal;
-
-			if ( ! normal ) {
-
-				console.error( 'Geometry must contain normal attribute. ' );
-
-			}
-
-			if ( normal.isPacked ) return;
-
-			if ( normal.itemSize != 3 ) {
-
-				console.error( 'normal.itemSize is not 3, which cannot be encoded. ' );
+	function compressNormals( mesh, encodeMethod ) {
 
-			}
+		if ( ! mesh.geometry ) {
 
-			const array = normal.array;
-			const count = normal.count;
-			let result;
+			console.error( 'Mesh must contain geometry. ' );
 
-			if ( encodeMethod == 'DEFAULT' ) {
+		}
 
-				// TODO: Add 1 byte to the result, making the encoded length to be 4 bytes.
-				result = new Uint8Array( count * 3 );
+		const normal = mesh.geometry.attributes.normal;
 
-				for ( let idx = 0; idx < array.length; idx += 3 ) {
+		if ( ! normal ) {
 
-					const encoded = EncodingFuncs.defaultEncode( array[ idx ], array[ idx + 1 ], array[ idx + 2 ], 1 );
-					result[ idx + 0 ] = encoded[ 0 ];
-					result[ idx + 1 ] = encoded[ 1 ];
-					result[ idx + 2 ] = encoded[ 2 ];
+			console.error( 'Geometry must contain normal attribute. ' );
 
-				}
+		}
 
-				mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 3, true ) );
-				mesh.geometry.attributes.normal.bytes = result.length * 1;
+		if ( normal.isPacked ) return;
 
-			} else if ( encodeMethod == 'OCT1Byte' ) {
+		if ( normal.itemSize != 3 ) {
 
-				/**
-      * It is not recommended to use 1-byte octahedron normals encoding unless you want to extremely reduce the memory usage
-      * As it makes vertex data not aligned to a 4 byte boundary which may harm some WebGL implementations and sometimes the normal distortion is visible
-      * Please refer to @zeux 's comments in https://github.com/mrdoob/three.js/pull/18208
-      */
-				result = new Int8Array( count * 2 );
+			console.error( 'normal.itemSize is not 3, which cannot be encoded. ' );
 
-				for ( let idx = 0; idx < array.length; idx += 3 ) {
+		}
 
-					const encoded = EncodingFuncs.octEncodeBest( array[ idx ], array[ idx + 1 ], array[ idx + 2 ], 1 );
-					result[ idx / 3 * 2 + 0 ] = encoded[ 0 ];
-					result[ idx / 3 * 2 + 1 ] = encoded[ 1 ];
+		const array = normal.array;
+		const count = normal.count;
+		let result;
 
-				}
+		if ( encodeMethod == 'DEFAULT' ) {
 
-				mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 2, true ) );
-				mesh.geometry.attributes.normal.bytes = result.length * 1;
+			// TODO: Add 1 byte to the result, making the encoded length to be 4 bytes.
+			result = new Uint8Array( count * 3 );
 
-			} else if ( encodeMethod == 'OCT2Byte' ) {
+			for ( let idx = 0; idx < array.length; idx += 3 ) {
 
-				result = new Int16Array( count * 2 );
+				const encoded = defaultEncode( array[ idx ], array[ idx + 1 ], array[ idx + 2 ], 1 );
+				result[ idx + 0 ] = encoded[ 0 ];
+				result[ idx + 1 ] = encoded[ 1 ];
+				result[ idx + 2 ] = encoded[ 2 ];
 
-				for ( let idx = 0; idx < array.length; idx += 3 ) {
+			}
 
-					const encoded = EncodingFuncs.octEncodeBest( array[ idx ], array[ idx + 1 ], array[ idx + 2 ], 2 );
-					result[ idx / 3 * 2 + 0 ] = encoded[ 0 ];
-					result[ idx / 3 * 2 + 1 ] = encoded[ 1 ];
+			mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 3, true ) );
+			mesh.geometry.attributes.normal.bytes = result.length * 1;
 
-				}
+		} else if ( encodeMethod == 'OCT1Byte' ) {
 
-				mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 2, true ) );
-				mesh.geometry.attributes.normal.bytes = result.length * 2;
+			/**
+    * It is not recommended to use 1-byte octahedron normals encoding unless you want to extremely reduce the memory usage
+    * As it makes vertex data not aligned to a 4 byte boundary which may harm some WebGL implementations and sometimes the normal distortion is visible
+    * Please refer to @zeux 's comments in https://github.com/mrdoob/three.js/pull/18208
+    */
+			result = new Int8Array( count * 2 );
 
-			} else if ( encodeMethod == 'ANGLES' ) {
+			for ( let idx = 0; idx < array.length; idx += 3 ) {
 
-				result = new Uint16Array( count * 2 );
+				const encoded = octEncodeBest( array[ idx ], array[ idx + 1 ], array[ idx + 2 ], 1 );
+				result[ idx / 3 * 2 + 0 ] = encoded[ 0 ];
+				result[ idx / 3 * 2 + 1 ] = encoded[ 1 ];
 
-				for ( let idx = 0; idx < array.length; idx += 3 ) {
+			}
 
-					const encoded = EncodingFuncs.anglesEncode( array[ idx ], array[ idx + 1 ], array[ idx + 2 ] );
-					result[ idx / 3 * 2 + 0 ] = encoded[ 0 ];
-					result[ idx / 3 * 2 + 1 ] = encoded[ 1 ];
+			mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 2, true ) );
+			mesh.geometry.attributes.normal.bytes = result.length * 1;
 
-				}
+		} else if ( encodeMethod == 'OCT2Byte' ) {
 
-				mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 2, true ) );
-				mesh.geometry.attributes.normal.bytes = result.length * 2;
+			result = new Int16Array( count * 2 );
 
-			} else {
+			for ( let idx = 0; idx < array.length; idx += 3 ) {
 
-				console.error( 'Unrecognized encoding method, should be `DEFAULT` or `ANGLES` or `OCT`. ' );
+				const encoded = octEncodeBest( array[ idx ], array[ idx + 1 ], array[ idx + 2 ], 2 );
+				result[ idx / 3 * 2 + 0 ] = encoded[ 0 ];
+				result[ idx / 3 * 2 + 1 ] = encoded[ 1 ];
 
 			}
 
-			mesh.geometry.attributes.normal.needsUpdate = true;
-			mesh.geometry.attributes.normal.isPacked = true;
-			mesh.geometry.attributes.normal.packingMethod = encodeMethod; // modify material
-
-			if ( ! ( mesh.material instanceof PackedPhongMaterial ) ) {
+			mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 2, true ) );
+			mesh.geometry.attributes.normal.bytes = result.length * 2;
 
-				mesh.material = new PackedPhongMaterial().copy( mesh.material );
+		} else if ( encodeMethod == 'ANGLES' ) {
 
-			}
+			result = new Uint16Array( count * 2 );
 
-			if ( encodeMethod == 'ANGLES' ) {
+			for ( let idx = 0; idx < array.length; idx += 3 ) {
 
-				mesh.material.defines.USE_PACKED_NORMAL = 0;
+				const encoded = anglesEncode( array[ idx ], array[ idx + 1 ], array[ idx + 2 ] );
+				result[ idx / 3 * 2 + 0 ] = encoded[ 0 ];
+				result[ idx / 3 * 2 + 1 ] = encoded[ 1 ];
 
 			}
 
-			if ( encodeMethod == 'OCT1Byte' ) {
+			mesh.geometry.setAttribute( 'normal', new THREE.BufferAttribute( result, 2, true ) );
+			mesh.geometry.attributes.normal.bytes = result.length * 2;
 
-				mesh.material.defines.USE_PACKED_NORMAL = 1;
+		} else {
 
-			}
+			console.error( 'Unrecognized encoding method, should be `DEFAULT` or `ANGLES` or `OCT`. ' );
 
-			if ( encodeMethod == 'OCT2Byte' ) {
+		}
 
-				mesh.material.defines.USE_PACKED_NORMAL = 1;
+		mesh.geometry.attributes.normal.needsUpdate = true;
+		mesh.geometry.attributes.normal.isPacked = true;
+		mesh.geometry.attributes.normal.packingMethod = encodeMethod; // modify material
 
-			}
+		if ( ! ( mesh.material instanceof THREE.PackedPhongMaterial ) ) {
 
-			if ( encodeMethod == 'DEFAULT' ) {
+			mesh.material = new THREE.PackedPhongMaterial().copy( mesh.material );
 
-				mesh.material.defines.USE_PACKED_NORMAL = 2;
+		}
 
-			}
+		if ( encodeMethod == 'ANGLES' ) {
 
-		}
-		/**
-  	 * Make the input mesh.geometry's position attribute encoded and compressed.
-  	 * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the position data.
-  	 *
-  	 * @param {THREE.Mesh} mesh
-  	 *
-  	 */
+			mesh.material.defines.USE_PACKED_NORMAL = 0;
 
+		}
 
-		static compressPositions( mesh ) {
+		if ( encodeMethod == 'OCT1Byte' ) {
 
-			if ( ! mesh.geometry ) {
+			mesh.material.defines.USE_PACKED_NORMAL = 1;
 
-				console.error( 'Mesh must contain geometry. ' );
+		}
 
-			}
+		if ( encodeMethod == 'OCT2Byte' ) {
 
-			const position = mesh.geometry.attributes.position;
+			mesh.material.defines.USE_PACKED_NORMAL = 1;
 
-			if ( ! position ) {
+		}
 
-				console.error( 'Geometry must contain position attribute. ' );
+		if ( encodeMethod == 'DEFAULT' ) {
 
-			}
+			mesh.material.defines.USE_PACKED_NORMAL = 2;
 
-			if ( position.isPacked ) return;
+		}
 
-			if ( position.itemSize != 3 ) {
+	}
+	/**
+	 * Make the input mesh.geometry's position attribute encoded and compressed.
+	 * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the position data.
+	 *
+	 * @param {THREE.Mesh} mesh
+	 *
+	 */
 
-				console.error( 'position.itemSize is not 3, which cannot be packed. ' );
 
-			}
+	function compressPositions( mesh ) {
 
-			const array = position.array;
-			const encodingBytes = 2;
-			const result = EncodingFuncs.quantizedEncode( array, encodingBytes );
-			const quantized = result.quantized;
-			const decodeMat = result.decodeMat; // IMPORTANT: calculate original geometry bounding info first, before updating packed positions
+		if ( ! mesh.geometry ) {
 
-			if ( mesh.geometry.boundingBox == null ) mesh.geometry.computeBoundingBox();
-			if ( mesh.geometry.boundingSphere == null ) mesh.geometry.computeBoundingSphere();
-			mesh.geometry.setAttribute( 'position', new THREE.BufferAttribute( quantized, 3 ) );
-			mesh.geometry.attributes.position.isPacked = true;
-			mesh.geometry.attributes.position.needsUpdate = true;
-			mesh.geometry.attributes.position.bytes = quantized.length * encodingBytes; // modify material
+			console.error( 'Mesh must contain geometry. ' );
 
-			if ( ! ( mesh.material instanceof PackedPhongMaterial ) ) {
+		}
 
-				mesh.material = new PackedPhongMaterial().copy( mesh.material );
+		const position = mesh.geometry.attributes.position;
 
-			}
+		if ( ! position ) {
 
-			mesh.material.defines.USE_PACKED_POSITION = 0;
-			mesh.material.uniforms.quantizeMatPos.value = decodeMat;
-			mesh.material.uniforms.quantizeMatPos.needsUpdate = true;
+			console.error( 'Geometry must contain position attribute. ' );
 
 		}
-		/**
-  	 * Make the input mesh.geometry's uv attribute encoded and compressed.
-  	 * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the uv data.
-  	 *
-  	 * @param {THREE.Mesh} mesh
-  	 *
-  	 */
 
+		if ( position.isPacked ) return;
 
-		static compressUvs( mesh ) {
+		if ( position.itemSize != 3 ) {
 
-			if ( ! mesh.geometry ) {
+			console.error( 'position.itemSize is not 3, which cannot be packed. ' );
 
-				console.error( 'Mesh must contain geometry property. ' );
-
-			}
+		}
 
-			const uvs = mesh.geometry.attributes.uv;
+		const array = position.array;
+		const encodingBytes = 2;
+		const result = quantizedEncode( array, encodingBytes );
+		const quantized = result.quantized;
+		const decodeMat = result.decodeMat; // IMPORTANT: calculate original geometry bounding info first, before updating packed positions
 
-			if ( ! uvs ) {
+		if ( mesh.geometry.boundingBox == null ) mesh.geometry.computeBoundingBox();
+		if ( mesh.geometry.boundingSphere == null ) mesh.geometry.computeBoundingSphere();
+		mesh.geometry.setAttribute( 'position', new THREE.BufferAttribute( quantized, 3 ) );
+		mesh.geometry.attributes.position.isPacked = true;
+		mesh.geometry.attributes.position.needsUpdate = true;
+		mesh.geometry.attributes.position.bytes = quantized.length * encodingBytes; // modify material
 
-				console.error( 'Geometry must contain uv attribute. ' );
+		if ( ! ( mesh.material instanceof THREE.PackedPhongMaterial ) ) {
 
-			}
+			mesh.material = new THREE.PackedPhongMaterial().copy( mesh.material );
 
-			if ( uvs.isPacked ) return;
-			const range = {
-				min: Infinity,
-				max: - Infinity
-			};
-			const array = uvs.array;
+		}
 
-			for ( let i = 0; i < array.length; i ++ ) {
+		mesh.material.defines.USE_PACKED_POSITION = 0;
+		mesh.material.uniforms.quantizeMatPos.value = decodeMat;
+		mesh.material.uniforms.quantizeMatPos.needsUpdate = true;
 
-				range.min = Math.min( range.min, array[ i ] );
-				range.max = Math.max( range.max, array[ i ] );
+	}
+	/**
+ * Make the input mesh.geometry's uv attribute encoded and compressed.
+ * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the uv data.
+ *
+ * @param {THREE.Mesh} mesh
+ *
+ */
 
-			}
 
-			let result;
+	function compressUvs( mesh ) {
 
-			if ( range.min >= - 1.0 && range.max <= 1.0 ) {
+		if ( ! mesh.geometry ) {
 
-				// use default encoding method
-				result = new Uint16Array( array.length );
+			console.error( 'Mesh must contain geometry property. ' );
 
-				for ( let i = 0; i < array.length; i += 2 ) {
+		}
 
-					const encoded = EncodingFuncs.defaultEncode( array[ i ], array[ i + 1 ], 0, 2 );
-					result[ i ] = encoded[ 0 ];
-					result[ i + 1 ] = encoded[ 1 ];
+		const uvs = mesh.geometry.attributes.uv;
 
-				}
+		if ( ! uvs ) {
 
-				mesh.geometry.setAttribute( 'uv', new THREE.BufferAttribute( result, 2, true ) );
-				mesh.geometry.attributes.uv.isPacked = true;
-				mesh.geometry.attributes.uv.needsUpdate = true;
-				mesh.geometry.attributes.uv.bytes = result.length * 2;
+			console.error( 'Geometry must contain uv attribute. ' );
 
-				if ( ! ( mesh.material instanceof PackedPhongMaterial ) ) {
+		}
 
-					mesh.material = new PackedPhongMaterial().copy( mesh.material );
+		if ( uvs.isPacked ) return;
+		const range = {
+			min: Infinity,
+			max: - Infinity
+		};
+		const array = uvs.array;
 
-				}
+		for ( let i = 0; i < array.length; i ++ ) {
 
-				mesh.material.defines.USE_PACKED_UV = 0;
+			range.min = Math.min( range.min, array[ i ] );
+			range.max = Math.max( range.max, array[ i ] );
 
-			} else {
+		}
 
-				// use quantized encoding method
-				result = EncodingFuncs.quantizedEncodeUV( array, 2 );
-				mesh.geometry.setAttribute( 'uv', new THREE.BufferAttribute( result.quantized, 2 ) );
-				mesh.geometry.attributes.uv.isPacked = true;
-				mesh.geometry.attributes.uv.needsUpdate = true;
-				mesh.geometry.attributes.uv.bytes = result.quantized.length * 2;
+		let result;
 
-				if ( ! ( mesh.material instanceof PackedPhongMaterial ) ) {
+		if ( range.min >= - 1.0 && range.max <= 1.0 ) {
 
-					mesh.material = new PackedPhongMaterial().copy( mesh.material );
+			// use default encoding method
+			result = new Uint16Array( array.length );
 
-				}
+			for ( let i = 0; i < array.length; i += 2 ) {
 
-				mesh.material.defines.USE_PACKED_UV = 1;
-				mesh.material.uniforms.quantizeMatUV.value = result.decodeMat;
-				mesh.material.uniforms.quantizeMatUV.needsUpdate = true;
+				const encoded = defaultEncode( array[ i ], array[ i + 1 ], 0, 2 );
+				result[ i ] = encoded[ 0 ];
+				result[ i + 1 ] = encoded[ 1 ];
 
 			}
 
-		}
-
-	}
+			mesh.geometry.setAttribute( 'uv', new THREE.BufferAttribute( result, 2, true ) );
+			mesh.geometry.attributes.uv.isPacked = true;
+			mesh.geometry.attributes.uv.needsUpdate = true;
+			mesh.geometry.attributes.uv.bytes = result.length * 2;
 
-	class EncodingFuncs {
+			if ( ! ( mesh.material instanceof THREE.PackedPhongMaterial ) ) {
 
-		static defaultEncode( x, y, z, bytes ) {
+				mesh.material = new THREE.PackedPhongMaterial().copy( mesh.material );
 
-			if ( bytes == 1 ) {
+			}
 
-				const tmpx = Math.round( ( x + 1 ) * 0.5 * 255 );
-				const tmpy = Math.round( ( y + 1 ) * 0.5 * 255 );
-				const tmpz = Math.round( ( z + 1 ) * 0.5 * 255 );
-				return new Uint8Array( [ tmpx, tmpy, tmpz ] );
+			mesh.material.defines.USE_PACKED_UV = 0;
 
-			} else if ( bytes == 2 ) {
+		} else {
 
-				const tmpx = Math.round( ( x + 1 ) * 0.5 * 65535 );
-				const tmpy = Math.round( ( y + 1 ) * 0.5 * 65535 );
-				const tmpz = Math.round( ( z + 1 ) * 0.5 * 65535 );
-				return new Uint16Array( [ tmpx, tmpy, tmpz ] );
+			// use quantized encoding method
+			result = quantizedEncodeUV( array, 2 );
+			mesh.geometry.setAttribute( 'uv', new THREE.BufferAttribute( result.quantized, 2 ) );
+			mesh.geometry.attributes.uv.isPacked = true;
+			mesh.geometry.attributes.uv.needsUpdate = true;
+			mesh.geometry.attributes.uv.bytes = result.quantized.length * 2;
 
-			} else {
+			if ( ! ( mesh.material instanceof THREE.PackedPhongMaterial ) ) {
 
-				console.error( 'number of bytes must be 1 or 2' );
+				mesh.material = new THREE.PackedPhongMaterial().copy( mesh.material );
 
 			}
 
-		}
-
-		static defaultDecode( array, bytes ) {
+			mesh.material.defines.USE_PACKED_UV = 1;
+			mesh.material.uniforms.quantizeMatUV.value = result.decodeMat;
+			mesh.material.uniforms.quantizeMatUV.needsUpdate = true;
 
-			if ( bytes == 1 ) {
+		}
 
-				return [ array[ 0 ] / 255 * 2.0 - 1.0, array[ 1 ] / 255 * 2.0 - 1.0, array[ 2 ] / 255 * 2.0 - 1.0 ];
+	} // Encoding functions
 
-			} else if ( bytes == 2 ) {
 
-				return [ array[ 0 ] / 65535 * 2.0 - 1.0, array[ 1 ] / 65535 * 2.0 - 1.0, array[ 2 ] / 65535 * 2.0 - 1.0 ];
+	function defaultEncode( x, y, z, bytes ) {
 
-			} else {
+		if ( bytes == 1 ) {
 
-				console.error( 'number of bytes must be 1 or 2' );
+			const tmpx = Math.round( ( x + 1 ) * 0.5 * 255 );
+			const tmpy = Math.round( ( y + 1 ) * 0.5 * 255 );
+			const tmpz = Math.round( ( z + 1 ) * 0.5 * 255 );
+			return new Uint8Array( [ tmpx, tmpy, tmpz ] );
 
-			}
+		} else if ( bytes == 2 ) {
 
-		} // for `Angles` encoding
+			const tmpx = Math.round( ( x + 1 ) * 0.5 * 65535 );
+			const tmpy = Math.round( ( y + 1 ) * 0.5 * 65535 );
+			const tmpz = Math.round( ( z + 1 ) * 0.5 * 65535 );
+			return new Uint16Array( [ tmpx, tmpy, tmpz ] );
 
+		} else {
 
-		static anglesEncode( x, y, z ) {
+			console.error( 'number of bytes must be 1 or 2' );
 
-			const normal0 = parseInt( 0.5 * ( 1.0 + Math.atan2( y, x ) / Math.PI ) * 65535 );
-			const normal1 = parseInt( 0.5 * ( 1.0 + z ) * 65535 );
-			return new Uint16Array( [ normal0, normal1 ] );
+		}
 
-		} // for `Octahedron` encoding
+	} // for `Angles` encoding
 
 
-		static octEncodeBest( x, y, z, bytes ) {
+	function anglesEncode( x, y, z ) {
 
-			let oct, dec, best, currentCos, bestCos; // Test various combinations of ceil and floor
-			// to minimize rounding errors
+		const normal0 = parseInt( 0.5 * ( 1.0 + Math.atan2( y, x ) / Math.PI ) * 65535 );
+		const normal1 = parseInt( 0.5 * ( 1.0 + z ) * 65535 );
+		return new Uint16Array( [ normal0, normal1 ] );
 
-			best = oct = octEncodeVec3( x, y, z, 'floor', 'floor' );
-			dec = octDecodeVec2( oct );
-			bestCos = dot( x, y, z, dec );
-			oct = octEncodeVec3( x, y, z, 'ceil', 'floor' );
-			dec = octDecodeVec2( oct );
-			currentCos = dot( x, y, z, dec );
+	} // for `Octahedron` encoding
 
-			if ( currentCos > bestCos ) {
 
-				best = oct;
-				bestCos = currentCos;
+	function octEncodeBest( x, y, z, bytes ) {
 
-			}
+		let oct, dec, best, currentCos, bestCos; // Test various combinations of ceil and floor
+		// to minimize rounding errors
 
-			oct = octEncodeVec3( x, y, z, 'floor', 'ceil' );
-			dec = octDecodeVec2( oct );
-			currentCos = dot( x, y, z, dec );
+		best = oct = octEncodeVec3( x, y, z, 'floor', 'floor' );
+		dec = octDecodeVec2( oct );
+		bestCos = dot( x, y, z, dec );
+		oct = octEncodeVec3( x, y, z, 'ceil', 'floor' );
+		dec = octDecodeVec2( oct );
+		currentCos = dot( x, y, z, dec );
 
-			if ( currentCos > bestCos ) {
+		if ( currentCos > bestCos ) {
 
-				best = oct;
-				bestCos = currentCos;
+			best = oct;
+			bestCos = currentCos;
 
-			}
+		}
 
-			oct = octEncodeVec3( x, y, z, 'ceil', 'ceil' );
-			dec = octDecodeVec2( oct );
-			currentCos = dot( x, y, z, dec );
+		oct = octEncodeVec3( x, y, z, 'floor', 'ceil' );
+		dec = octDecodeVec2( oct );
+		currentCos = dot( x, y, z, dec );
 
-			if ( currentCos > bestCos ) {
+		if ( currentCos > bestCos ) {
 
-				best = oct;
+			best = oct;
+			bestCos = currentCos;
 
-			}
+		}
 
-			return best;
+		oct = octEncodeVec3( x, y, z, 'ceil', 'ceil' );
+		dec = octDecodeVec2( oct );
+		currentCos = dot( x, y, z, dec );
 
-			function octEncodeVec3( x0, y0, z0, xfunc, yfunc ) {
+		if ( currentCos > bestCos ) {
 
-				let x = x0 / ( Math.abs( x0 ) + Math.abs( y0 ) + Math.abs( z0 ) );
-				let y = y0 / ( Math.abs( x0 ) + Math.abs( y0 ) + Math.abs( z0 ) );
+			best = oct;
 
-				if ( z < 0 ) {
+		}
 
-					const tempx = ( 1 - Math.abs( y ) ) * ( x >= 0 ? 1 : - 1 );
-					const tempy = ( 1 - Math.abs( x ) ) * ( y >= 0 ? 1 : - 1 );
-					x = tempx;
-					y = tempy;
-					let diff = 1 - Math.abs( x ) - Math.abs( y );
+		return best;
 
-					if ( diff > 0 ) {
+		function octEncodeVec3( x0, y0, z0, xfunc, yfunc ) {
 
-						diff += 0.001;
-						x += x > 0 ? diff / 2 : - diff / 2;
-						y += y > 0 ? diff / 2 : - diff / 2;
+			let x = x0 / ( Math.abs( x0 ) + Math.abs( y0 ) + Math.abs( z0 ) );
+			let y = y0 / ( Math.abs( x0 ) + Math.abs( y0 ) + Math.abs( z0 ) );
 
-					}
+			if ( z < 0 ) {
 
-				}
+				const tempx = ( 1 - Math.abs( y ) ) * ( x >= 0 ? 1 : - 1 );
+				const tempy = ( 1 - Math.abs( x ) ) * ( y >= 0 ? 1 : - 1 );
+				x = tempx;
+				y = tempy;
+				let diff = 1 - Math.abs( x ) - Math.abs( y );
 
-				if ( bytes == 1 ) {
+				if ( diff > 0 ) {
 
-					return new Int8Array( [ Math[ xfunc ]( x * 127.5 + ( x < 0 ? 1 : 0 ) ), Math[ yfunc ]( y * 127.5 + ( y < 0 ? 1 : 0 ) ) ] );
+					diff += 0.001;
+					x += x > 0 ? diff / 2 : - diff / 2;
+					y += y > 0 ? diff / 2 : - diff / 2;
 
 				}
 
-				if ( bytes == 2 ) {
+			}
 
-					return new Int16Array( [ Math[ xfunc ]( x * 32767.5 + ( x < 0 ? 1 : 0 ) ), Math[ yfunc ]( y * 32767.5 + ( y < 0 ? 1 : 0 ) ) ] );
+			if ( bytes == 1 ) {
 
-				}
+				return new Int8Array( [ Math[ xfunc ]( x * 127.5 + ( x < 0 ? 1 : 0 ) ), Math[ yfunc ]( y * 127.5 + ( y < 0 ? 1 : 0 ) ) ] );
 
 			}
 
-			function octDecodeVec2( oct ) {
+			if ( bytes == 2 ) {
 
-				let x = oct[ 0 ];
-				let y = oct[ 1 ];
+				return new Int16Array( [ Math[ xfunc ]( x * 32767.5 + ( x < 0 ? 1 : 0 ) ), Math[ yfunc ]( y * 32767.5 + ( y < 0 ? 1 : 0 ) ) ] );
 
-				if ( bytes == 1 ) {
-
-					x /= x < 0 ? 127 : 128;
-					y /= y < 0 ? 127 : 128;
+			}
 
-				} else if ( bytes == 2 ) {
+		}
 
-					x /= x < 0 ? 32767 : 32768;
-					y /= y < 0 ? 32767 : 32768;
+		function octDecodeVec2( oct ) {
 
-				}
+			let x = oct[ 0 ];
+			let y = oct[ 1 ];
 
-				const z = 1 - Math.abs( x ) - Math.abs( y );
-
-				if ( z < 0 ) {
+			if ( bytes == 1 ) {
 
-					const tmpx = x;
-					x = ( 1 - Math.abs( y ) ) * ( x >= 0 ? 1 : - 1 );
-					y = ( 1 - Math.abs( tmpx ) ) * ( y >= 0 ? 1 : - 1 );
+				x /= x < 0 ? 127 : 128;
+				y /= y < 0 ? 127 : 128;
 
-				}
+			} else if ( bytes == 2 ) {
 
-				const length = Math.sqrt( x * x + y * y + z * z );
-				return [ x / length, y / length, z / length ];
+				x /= x < 0 ? 32767 : 32768;
+				y /= y < 0 ? 32767 : 32768;
 
 			}
 
-			function dot( x, y, z, vec3 ) {
+			const z = 1 - Math.abs( x ) - Math.abs( y );
 
-				return x * vec3[ 0 ] + y * vec3[ 1 ] + z * vec3[ 2 ];
+			if ( z < 0 ) {
 
-			}
+				const tmpx = x;
+				x = ( 1 - Math.abs( y ) ) * ( x >= 0 ? 1 : - 1 );
+				y = ( 1 - Math.abs( tmpx ) ) * ( y >= 0 ? 1 : - 1 );
 
-		}
+			}
 
-		static quantizedEncode( array, bytes ) {
+			const length = Math.sqrt( x * x + y * y + z * z );
+			return [ x / length, y / length, z / length ];
 
-			let quantized, segments;
+		}
 
-			if ( bytes == 1 ) {
+		function dot( x, y, z, vec3 ) {
 
-				quantized = new Uint8Array( array.length );
-				segments = 255;
+			return x * vec3[ 0 ] + y * vec3[ 1 ] + z * vec3[ 2 ];
 
-			} else if ( bytes == 2 ) {
+		}
 
-				quantized = new Uint16Array( array.length );
-				segments = 65535;
+	}
 
-			} else {
+	function quantizedEncode( array, bytes ) {
 
-				console.error( 'number of bytes error! ' );
+		let quantized, segments;
 
-			}
+		if ( bytes == 1 ) {
 
-			const decodeMat = new THREE.Matrix4();
-			const min = new Float32Array( 3 );
-			const max = new Float32Array( 3 );
-			min[ 0 ] = min[ 1 ] = min[ 2 ] = Number.MAX_VALUE;
-			max[ 0 ] = max[ 1 ] = max[ 2 ] = - Number.MAX_VALUE;
+			quantized = new Uint8Array( array.length );
+			segments = 255;
 
-			for ( let i = 0; i < array.length; i += 3 ) {
+		} else if ( bytes == 2 ) {
 
-				min[ 0 ] = Math.min( min[ 0 ], array[ i + 0 ] );
-				min[ 1 ] = Math.min( min[ 1 ], array[ i + 1 ] );
-				min[ 2 ] = Math.min( min[ 2 ], array[ i + 2 ] );
-				max[ 0 ] = Math.max( max[ 0 ], array[ i + 0 ] );
-				max[ 1 ] = Math.max( max[ 1 ], array[ i + 1 ] );
-				max[ 2 ] = Math.max( max[ 2 ], array[ i + 2 ] );
+			quantized = new Uint16Array( array.length );
+			segments = 65535;
 
-			}
+		} else {
 
-			decodeMat.scale( new THREE.Vector3( ( max[ 0 ] - min[ 0 ] ) / segments, ( max[ 1 ] - min[ 1 ] ) / segments, ( max[ 2 ] - min[ 2 ] ) / segments ) );
-			decodeMat.elements[ 12 ] = min[ 0 ];
-			decodeMat.elements[ 13 ] = min[ 1 ];
-			decodeMat.elements[ 14 ] = min[ 2 ];
-			decodeMat.transpose();
-			const multiplier = new Float32Array( [ max[ 0 ] !== min[ 0 ] ? segments / ( max[ 0 ] - min[ 0 ] ) : 0, max[ 1 ] !== min[ 1 ] ? segments / ( max[ 1 ] - min[ 1 ] ) : 0, max[ 2 ] !== min[ 2 ] ? segments / ( max[ 2 ] - min[ 2 ] ) : 0 ] );
+			console.error( 'number of bytes error! ' );
 
-			for ( let i = 0; i < array.length; i += 3 ) {
+		}
 
-				quantized[ i + 0 ] = Math.floor( ( array[ i + 0 ] - min[ 0 ] ) * multiplier[ 0 ] );
-				quantized[ i + 1 ] = Math.floor( ( array[ i + 1 ] - min[ 1 ] ) * multiplier[ 1 ] );
-				quantized[ i + 2 ] = Math.floor( ( array[ i + 2 ] - min[ 2 ] ) * multiplier[ 2 ] );
+		const decodeMat = new THREE.Matrix4();
+		const min = new Float32Array( 3 );
+		const max = new Float32Array( 3 );
+		min[ 0 ] = min[ 1 ] = min[ 2 ] = Number.MAX_VALUE;
+		max[ 0 ] = max[ 1 ] = max[ 2 ] = - Number.MAX_VALUE;
 
-			}
+		for ( let i = 0; i < array.length; i += 3 ) {
 
-			return {
-				quantized: quantized,
-				decodeMat: decodeMat
-			};
+			min[ 0 ] = Math.min( min[ 0 ], array[ i + 0 ] );
+			min[ 1 ] = Math.min( min[ 1 ], array[ i + 1 ] );
+			min[ 2 ] = Math.min( min[ 2 ], array[ i + 2 ] );
+			max[ 0 ] = Math.max( max[ 0 ], array[ i + 0 ] );
+			max[ 1 ] = Math.max( max[ 1 ], array[ i + 1 ] );
+			max[ 2 ] = Math.max( max[ 2 ], array[ i + 2 ] );
 
 		}
 
-		static quantizedEncodeUV( array, bytes ) {
+		decodeMat.scale( new THREE.Vector3( ( max[ 0 ] - min[ 0 ] ) / segments, ( max[ 1 ] - min[ 1 ] ) / segments, ( max[ 2 ] - min[ 2 ] ) / segments ) );
+		decodeMat.elements[ 12 ] = min[ 0 ];
+		decodeMat.elements[ 13 ] = min[ 1 ];
+		decodeMat.elements[ 14 ] = min[ 2 ];
+		decodeMat.transpose();
+		const multiplier = new Float32Array( [ max[ 0 ] !== min[ 0 ] ? segments / ( max[ 0 ] - min[ 0 ] ) : 0, max[ 1 ] !== min[ 1 ] ? segments / ( max[ 1 ] - min[ 1 ] ) : 0, max[ 2 ] !== min[ 2 ] ? segments / ( max[ 2 ] - min[ 2 ] ) : 0 ] );
 
-			let quantized, segments;
+		for ( let i = 0; i < array.length; i += 3 ) {
 
-			if ( bytes == 1 ) {
+			quantized[ i + 0 ] = Math.floor( ( array[ i + 0 ] - min[ 0 ] ) * multiplier[ 0 ] );
+			quantized[ i + 1 ] = Math.floor( ( array[ i + 1 ] - min[ 1 ] ) * multiplier[ 1 ] );
+			quantized[ i + 2 ] = Math.floor( ( array[ i + 2 ] - min[ 2 ] ) * multiplier[ 2 ] );
 
-				quantized = new Uint8Array( array.length );
-				segments = 255;
+		}
 
-			} else if ( bytes == 2 ) {
+		return {
+			quantized: quantized,
+			decodeMat: decodeMat
+		};
 
-				quantized = new Uint16Array( array.length );
-				segments = 65535;
+	}
 
-			} else {
+	function quantizedEncodeUV( array, bytes ) {
 
-				console.error( 'number of bytes error! ' );
+		let quantized, segments;
 
-			}
+		if ( bytes == 1 ) {
 
-			const decodeMat = new THREE.Matrix3();
-			const min = new Float32Array( 2 );
-			const max = new Float32Array( 2 );
-			min[ 0 ] = min[ 1 ] = Number.MAX_VALUE;
-			max[ 0 ] = max[ 1 ] = - Number.MAX_VALUE;
+			quantized = new Uint8Array( array.length );
+			segments = 255;
 
-			for ( let i = 0; i < array.length; i += 2 ) {
+		} else if ( bytes == 2 ) {
 
-				min[ 0 ] = Math.min( min[ 0 ], array[ i + 0 ] );
-				min[ 1 ] = Math.min( min[ 1 ], array[ i + 1 ] );
-				max[ 0 ] = Math.max( max[ 0 ], array[ i + 0 ] );
-				max[ 1 ] = Math.max( max[ 1 ], array[ i + 1 ] );
+			quantized = new Uint16Array( array.length );
+			segments = 65535;
 
-			}
+		} else {
 
-			decodeMat.scale( ( max[ 0 ] - min[ 0 ] ) / segments, ( max[ 1 ] - min[ 1 ] ) / segments );
-			decodeMat.elements[ 6 ] = min[ 0 ];
-			decodeMat.elements[ 7 ] = min[ 1 ];
-			decodeMat.transpose();
-			const multiplier = new Float32Array( [ max[ 0 ] !== min[ 0 ] ? segments / ( max[ 0 ] - min[ 0 ] ) : 0, max[ 1 ] !== min[ 1 ] ? segments / ( max[ 1 ] - min[ 1 ] ) : 0 ] );
+			console.error( 'number of bytes error! ' );
 
-			for ( let i = 0; i < array.length; i += 2 ) {
+		}
 
-				quantized[ i + 0 ] = Math.floor( ( array[ i + 0 ] - min[ 0 ] ) * multiplier[ 0 ] );
-				quantized[ i + 1 ] = Math.floor( ( array[ i + 1 ] - min[ 1 ] ) * multiplier[ 1 ] );
+		const decodeMat = new THREE.Matrix3();
+		const min = new Float32Array( 2 );
+		const max = new Float32Array( 2 );
+		min[ 0 ] = min[ 1 ] = Number.MAX_VALUE;
+		max[ 0 ] = max[ 1 ] = - Number.MAX_VALUE;
 
-			}
+		for ( let i = 0; i < array.length; i += 2 ) {
 
-			return {
-				quantized: quantized,
-				decodeMat: decodeMat
-			};
+			min[ 0 ] = Math.min( min[ 0 ], array[ i + 0 ] );
+			min[ 1 ] = Math.min( min[ 1 ], array[ i + 1 ] );
+			max[ 0 ] = Math.max( max[ 0 ], array[ i + 0 ] );
+			max[ 1 ] = Math.max( max[ 1 ], array[ i + 1 ] );
 
 		}
 
-	}
-	/**
- * `PackedPhongMaterial` inherited from THREE.MeshPhongMaterial
- *
- * @param {Object} parameters
- */
-
+		decodeMat.scale( ( max[ 0 ] - min[ 0 ] ) / segments, ( max[ 1 ] - min[ 1 ] ) / segments );
+		decodeMat.elements[ 6 ] = min[ 0 ];
+		decodeMat.elements[ 7 ] = min[ 1 ];
+		decodeMat.transpose();
+		const multiplier = new Float32Array( [ max[ 0 ] !== min[ 0 ] ? segments / ( max[ 0 ] - min[ 0 ] ) : 0, max[ 1 ] !== min[ 1 ] ? segments / ( max[ 1 ] - min[ 1 ] ) : 0 ] );
 
-	class PackedPhongMaterial extends THREE.MeshPhongMaterial {
+		for ( let i = 0; i < array.length; i += 2 ) {
 
-		constructor( parameters ) {
-
-			super();
-			this.defines = {};
-			this.type = 'PackedPhongMaterial';
-			this.uniforms = THREE.UniformsUtils.merge( [ THREE.ShaderLib.phong.uniforms, {
-				quantizeMatPos: {
-					value: null
-				},
-				quantizeMatUV: {
-					value: null
-				}
-			} ] );
-			this.vertexShader = [ '#define PHONG', 'varying vec3 vViewPosition;', '#ifndef FLAT_SHADED', 'varying vec3 vNormal;', '#endif', THREE.ShaderChunk.common, THREE.ShaderChunk.uv_pars_vertex, THREE.ShaderChunk.uv2_pars_vertex, THREE.ShaderChunk.displacementmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.fog_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, THREE.ShaderChunk.logdepthbuf_pars_vertex, THREE.ShaderChunk.clipping_planes_pars_vertex, `#ifdef USE_PACKED_NORMAL
-					#if USE_PACKED_NORMAL == 0
-						vec3 decodeNormal(vec3 packedNormal)
-						{
-							float x = packedNormal.x * 2.0 - 1.0;
-							float y = packedNormal.y * 2.0 - 1.0;
-							vec2 scth = vec2(sin(x * PI), cos(x * PI));
-							vec2 scphi = vec2(sqrt(1.0 - y * y), y);
-							return normalize( vec3(scth.y * scphi.x, scth.x * scphi.x, scphi.y) );
-						}
-					#endif
-
-					#if USE_PACKED_NORMAL == 1
-						vec3 decodeNormal(vec3 packedNormal)
-						{
-							vec3 v = vec3(packedNormal.xy, 1.0 - abs(packedNormal.x) - abs(packedNormal.y));
-							if (v.z < 0.0)
-							{
-								v.xy = (1.0 - abs(v.yx)) * vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
-							}
-							return normalize(v);
-						}
-					#endif
-
-					#if USE_PACKED_NORMAL == 2
-						vec3 decodeNormal(vec3 packedNormal)
-						{
-							vec3 v = (packedNormal * 2.0) - 1.0;
-							return normalize(v);
-						}
-					#endif
-				#endif`, `#ifdef USE_PACKED_POSITION
-					#if USE_PACKED_POSITION == 0
-						uniform mat4 quantizeMatPos;
-					#endif
-				#endif`, `#ifdef USE_PACKED_UV
-					#if USE_PACKED_UV == 1
-						uniform mat3 quantizeMatUV;
-					#endif
-				#endif`, `#ifdef USE_PACKED_UV
-					#if USE_PACKED_UV == 0
-						vec2 decodeUV(vec2 packedUV)
-						{
-							vec2 uv = (packedUV * 2.0) - 1.0;
-							return uv;
-						}
-					#endif
-
-					#if USE_PACKED_UV == 1
-						vec2 decodeUV(vec2 packedUV)
-						{
-							vec2 uv = ( vec3(packedUV, 1.0) * quantizeMatUV ).xy;
-							return uv;
-						}
-					#endif
-				#endif`, 'void main() {', THREE.ShaderChunk.uv_vertex, `#ifdef USE_UV
-					#ifdef USE_PACKED_UV
-						vUv = decodeUV(vUv);
-					#endif
-				#endif`, THREE.ShaderChunk.uv2_vertex, THREE.ShaderChunk.color_vertex, THREE.ShaderChunk.beginnormal_vertex, `#ifdef USE_PACKED_NORMAL
-					objectNormal = decodeNormal(objectNormal);
-				#endif
-
-				#ifdef USE_TANGENT
-					vec3 objectTangent = vec3( tangent.xyz );
-				#endif
-				`, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, '#ifndef FLAT_SHADED', '	vNormal = normalize( transformedNormal );', '#endif', THREE.ShaderChunk.begin_vertex, `#ifdef USE_PACKED_POSITION
-					#if USE_PACKED_POSITION == 0
-						transformed = ( vec4(transformed, 1.0) * quantizeMatPos ).xyz;
-					#endif
-				#endif`, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.displacementmap_vertex, THREE.ShaderChunk.project_vertex, THREE.ShaderChunk.logdepthbuf_vertex, THREE.ShaderChunk.clipping_planes_vertex, 'vViewPosition = - mvPosition.xyz;', THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.shadowmap_vertex, THREE.ShaderChunk.fog_vertex, '}' ].join( '\n' ); // Use the original THREE.MeshPhongMaterial's fragmentShader.
-
-			this.fragmentShader = [ '#define PHONG', 'uniform vec3 diffuse;', 'uniform vec3 emissive;', 'uniform vec3 specular;', 'uniform float shininess;', 'uniform float opacity;', THREE.ShaderChunk.common, THREE.ShaderChunk.packing, THREE.ShaderChunk.dithering_pars_fragment, THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.uv_pars_fragment, THREE.ShaderChunk.uv2_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.alphamap_pars_fragment, THREE.ShaderChunk.aomap_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.emissivemap_pars_fragment, THREE.ShaderChunk.envmap_common_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.cube_uv_reflection_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.bsdfs, THREE.ShaderChunk.lights_pars_begin, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, THREE.ShaderChunk.logdepthbuf_pars_fragment, THREE.ShaderChunk.clipping_planes_pars_fragment, 'void main() {', THREE.ShaderChunk.clipping_planes_fragment, 'vec4 diffuseColor = vec4( diffuse, opacity );', 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', 'vec3 totalEmissiveRadiance = emissive;', THREE.ShaderChunk.logdepthbuf_fragment, THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.alphamap_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.normal_fragment_begin, THREE.ShaderChunk.normal_fragment_maps, THREE.ShaderChunk.emissivemap_fragment, // accumulation
-				THREE.ShaderChunk.lights_phong_fragment, THREE.ShaderChunk.lights_fragment_begin, THREE.ShaderChunk.lights_fragment_maps, THREE.ShaderChunk.lights_fragment_end, // modulation
-				THREE.ShaderChunk.aomap_fragment, 'vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;', THREE.ShaderChunk.envmap_fragment, 'gl_FragColor = vec4( outgoingLight, diffuseColor.a );', THREE.ShaderChunk.tonemapping_fragment, THREE.ShaderChunk.encodings_fragment, THREE.ShaderChunk.fog_fragment, THREE.ShaderChunk.premultiplied_alpha_fragment, THREE.ShaderChunk.dithering_fragment, '}' ].join( '\n' );
-			this.setValues( parameters );
+			quantized[ i + 0 ] = Math.floor( ( array[ i + 0 ] - min[ 0 ] ) * multiplier[ 0 ] );
+			quantized[ i + 1 ] = Math.floor( ( array[ i + 1 ] - min[ 1 ] ) * multiplier[ 1 ] );
 
 		}
 
+		return {
+			quantized: quantized,
+			decodeMat: decodeMat
+		};
+
 	}
 
-	THREE.GeometryCompressionUtils = GeometryCompressionUtils;
-	THREE.PackedPhongMaterial = PackedPhongMaterial;
+	THREE.GeometryCompressionUtils = {};
+	THREE.GeometryCompressionUtils.compressNormals = compressNormals;
+	THREE.GeometryCompressionUtils.compressPositions = compressPositions;
+	THREE.GeometryCompressionUtils.compressUvs = compressUvs;
 
 } )();

+ 134 - 134
examples/js/utils/GeometryUtils.js

@@ -1,189 +1,189 @@
 ( function () {
 
-	class GeometryUtils {
+	/**
+ * Generates 2D-Coordinates in a very fast way.
+ *
+ * Based on work by:
+ * @link http://www.openprocessing.org/sketch/15493
+ *
+ * @param center     Center of Hilbert curve.
+ * @param size       Total width of Hilbert curve.
+ * @param iterations Number of subdivisions.
+ * @param v0         Corner index -X, -Z.
+ * @param v1         Corner index -X, +Z.
+ * @param v2         Corner index +X, +Z.
+ * @param v3         Corner index +X, -Z.
+ */
 
-		/**
-   * Generates 2D-Coordinates in a very fast way.
-   *
-   * Based on work by:
-   * @link http://www.openprocessing.org/sketch/15493
-   *
-   * @param center     Center of Hilbert curve.
-   * @param size       Total width of Hilbert curve.
-   * @param iterations Number of subdivisions.
-   * @param v0         Corner index -X, -Z.
-   * @param v1         Corner index -X, +Z.
-   * @param v2         Corner index +X, +Z.
-   * @param v3         Corner index +X, -Z.
-   */
-		static hilbert2D( center = new THREE.Vector3( 0, 0, 0 ), size = 10, iterations = 1, v0 = 0, v1 = 1, v2 = 2, v3 = 3 ) {
+	function hilbert2D( center = new THREE.Vector3( 0, 0, 0 ), size = 10, iterations = 1, v0 = 0, v1 = 1, v2 = 2, v3 = 3 ) {
 
-			const half = size / 2;
-			const vec_s = [ new THREE.Vector3( center.x - half, center.y, center.z - half ), new THREE.Vector3( center.x - half, center.y, center.z + half ), new THREE.Vector3( center.x + half, center.y, center.z + half ), new THREE.Vector3( center.x + half, center.y, center.z - half ) ];
-			const vec = [ vec_s[ v0 ], vec_s[ v1 ], vec_s[ v2 ], vec_s[ v3 ] ]; // Recurse iterations
+		const half = size / 2;
+		const vec_s = [ new THREE.Vector3( center.x - half, center.y, center.z - half ), new THREE.Vector3( center.x - half, center.y, center.z + half ), new THREE.Vector3( center.x + half, center.y, center.z + half ), new THREE.Vector3( center.x + half, center.y, center.z - half ) ];
+		const vec = [ vec_s[ v0 ], vec_s[ v1 ], vec_s[ v2 ], vec_s[ v3 ] ]; // Recurse iterations
 
-			if ( 0 <= -- iterations ) {
+		if ( 0 <= -- iterations ) {
 
-				const tmp = [];
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 0 ], half, iterations, v0, v3, v2, v1 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 1 ], half, iterations, v0, v1, v2, v3 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 2 ], half, iterations, v0, v1, v2, v3 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert2D( vec[ 3 ], half, iterations, v2, v1, v0, v3 ) ); // Return recursive call
+			const tmp = [];
+			Array.prototype.push.apply( tmp, hilbert2D( vec[ 0 ], half, iterations, v0, v3, v2, v1 ) );
+			Array.prototype.push.apply( tmp, hilbert2D( vec[ 1 ], half, iterations, v0, v1, v2, v3 ) );
+			Array.prototype.push.apply( tmp, hilbert2D( vec[ 2 ], half, iterations, v0, v1, v2, v3 ) );
+			Array.prototype.push.apply( tmp, hilbert2D( vec[ 3 ], half, iterations, v2, v1, v0, v3 ) ); // Return recursive call
 
-				return tmp;
+			return tmp;
 
-			} // Return complete Hilbert Curve.
+		} // Return complete Hilbert Curve.
 
 
-			return vec;
+		return vec;
 
-		}
-		/**
-   * Generates 3D-Coordinates in a very fast way.
-   *
-   * Based on work by:
-   * @link http://www.openprocessing.org/visuals/?visualID=15599
-   *
-   * @param center     Center of Hilbert curve.
-   * @param size       Total width of Hilbert curve.
-   * @param iterations Number of subdivisions.
-   * @param v0         Corner index -X, +Y, -Z.
-   * @param v1         Corner index -X, +Y, +Z.
-   * @param v2         Corner index -X, -Y, +Z.
-   * @param v3         Corner index -X, -Y, -Z.
-   * @param v4         Corner index +X, -Y, -Z.
-   * @param v5         Corner index +X, -Y, +Z.
-   * @param v6         Corner index +X, +Y, +Z.
-   * @param v7         Corner index +X, +Y, -Z.
-   */
-
-
-		static hilbert3D( center = new THREE.Vector3( 0, 0, 0 ), size = 10, iterations = 1, v0 = 0, v1 = 1, v2 = 2, v3 = 3, v4 = 4, v5 = 5, v6 = 6, v7 = 7 ) {
-
-			// Default Vars
-			const half = size / 2;
-			const vec_s = [ new THREE.Vector3( center.x - half, center.y + half, center.z - half ), new THREE.Vector3( center.x - half, center.y + half, center.z + half ), new THREE.Vector3( center.x - half, center.y - half, center.z + half ), new THREE.Vector3( center.x - half, center.y - half, center.z - half ), new THREE.Vector3( center.x + half, center.y - half, center.z - half ), new THREE.Vector3( center.x + half, center.y - half, center.z + half ), new THREE.Vector3( center.x + half, center.y + half, center.z + half ), new THREE.Vector3( center.x + half, center.y + half, center.z - half ) ];
-			const vec = [ vec_s[ v0 ], vec_s[ v1 ], vec_s[ v2 ], vec_s[ v3 ], vec_s[ v4 ], vec_s[ v5 ], vec_s[ v6 ], vec_s[ v7 ] ]; // Recurse iterations
-
-			if ( -- iterations >= 0 ) {
-
-				const tmp = [];
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 0 ], half, iterations, v0, v3, v4, v7, v6, v5, v2, v1 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 1 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 2 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 3 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 4 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 5 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 6 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
-				Array.prototype.push.apply( tmp, GeometryUtils.hilbert3D( vec[ 7 ], half, iterations, v6, v5, v2, v1, v0, v3, v4, v7 ) ); // Return recursive call
-
-				return tmp;
-
-			} // Return complete Hilbert Curve.
-
-
-			return vec;
-
-		}
-		/**
-   * Generates a Gosper curve (lying in the XY plane)
-   *
-   * https://gist.github.com/nitaku/6521802
-   *
-   * @param size The size of a single gosper island.
-   */
+	}
+	/**
+ * Generates 3D-Coordinates in a very fast way.
+ *
+ * Based on work by:
+ * @link http://www.openprocessing.org/visuals/?visualID=15599
+ *
+ * @param center     Center of Hilbert curve.
+ * @param size       Total width of Hilbert curve.
+ * @param iterations Number of subdivisions.
+ * @param v0         Corner index -X, +Y, -Z.
+ * @param v1         Corner index -X, +Y, +Z.
+ * @param v2         Corner index -X, -Y, +Z.
+ * @param v3         Corner index -X, -Y, -Z.
+ * @param v4         Corner index +X, -Y, -Z.
+ * @param v5         Corner index +X, -Y, +Z.
+ * @param v6         Corner index +X, +Y, +Z.
+ * @param v7         Corner index +X, +Y, -Z.
+ */
+
+
+	function hilbert3D( center = new THREE.Vector3( 0, 0, 0 ), size = 10, iterations = 1, v0 = 0, v1 = 1, v2 = 2, v3 = 3, v4 = 4, v5 = 5, v6 = 6, v7 = 7 ) {
+
+		// Default Vars
+		const half = size / 2;
+		const vec_s = [ new THREE.Vector3( center.x - half, center.y + half, center.z - half ), new THREE.Vector3( center.x - half, center.y + half, center.z + half ), new THREE.Vector3( center.x - half, center.y - half, center.z + half ), new THREE.Vector3( center.x - half, center.y - half, center.z - half ), new THREE.Vector3( center.x + half, center.y - half, center.z - half ), new THREE.Vector3( center.x + half, center.y - half, center.z + half ), new THREE.Vector3( center.x + half, center.y + half, center.z + half ), new THREE.Vector3( center.x + half, center.y + half, center.z - half ) ];
+		const vec = [ vec_s[ v0 ], vec_s[ v1 ], vec_s[ v2 ], vec_s[ v3 ], vec_s[ v4 ], vec_s[ v5 ], vec_s[ v6 ], vec_s[ v7 ] ]; // Recurse iterations
+
+		if ( -- iterations >= 0 ) {
+
+			const tmp = [];
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 0 ], half, iterations, v0, v3, v4, v7, v6, v5, v2, v1 ) );
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 1 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 2 ], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3 ) );
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 3 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 4 ], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5 ) );
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 5 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 6 ], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7 ) );
+			Array.prototype.push.apply( tmp, hilbert3D( vec[ 7 ], half, iterations, v6, v5, v2, v1, v0, v3, v4, v7 ) ); // Return recursive call
+
+			return tmp;
+
+		} // Return complete Hilbert Curve.
+
+
+		return vec;
 
+	}
+	/**
+ * Generates a Gosper curve (lying in the XY plane)
+ *
+ * https://gist.github.com/nitaku/6521802
+ *
+ * @param size The size of a single gosper island.
+ */
 
-		static gosper( size = 1 ) {
 
-			function fractalize( config ) {
+	function gosper( size = 1 ) {
 
-				let output;
-				let input = config.axiom;
+		function fractalize( config ) {
 
-				for ( let i = 0, il = config.steps; 0 <= il ? i < il : i > il; 0 <= il ? i ++ : i -- ) {
+			let output;
+			let input = config.axiom;
 
-					output = '';
+			for ( let i = 0, il = config.steps; 0 <= il ? i < il : i > il; 0 <= il ? i ++ : i -- ) {
 
-					for ( let j = 0, jl = input.length; j < jl; j ++ ) {
+				output = '';
 
-						const char = input[ j ];
+				for ( let j = 0, jl = input.length; j < jl; j ++ ) {
 
-						if ( char in config.rules ) {
+					const char = input[ j ];
 
-							output += config.rules[ char ];
+					if ( char in config.rules ) {
 
-						} else {
+						output += config.rules[ char ];
 
-							output += char;
+					} else {
 
-						}
+						output += char;
 
 					}
 
-					input = output;
-
 				}
 
-				return output;
+				input = output;
 
 			}
 
-			function toPoints( config ) {
+			return output;
 
-				let currX = 0,
-					currY = 0;
-				let angle = 0;
-				const path = [ 0, 0, 0 ];
-				const fractal = config.fractal;
+		}
 
-				for ( let i = 0, l = fractal.length; i < l; i ++ ) {
+		function toPoints( config ) {
 
-					const char = fractal[ i ];
+			let currX = 0,
+				currY = 0;
+			let angle = 0;
+			const path = [ 0, 0, 0 ];
+			const fractal = config.fractal;
 
-					if ( char === '+' ) {
+			for ( let i = 0, l = fractal.length; i < l; i ++ ) {
 
-						angle += config.angle;
+				const char = fractal[ i ];
 
-					} else if ( char === '-' ) {
+				if ( char === '+' ) {
 
-						angle -= config.angle;
+					angle += config.angle;
 
-					} else if ( char === 'F' ) {
+				} else if ( char === '-' ) {
 
-						currX += config.size * Math.cos( angle );
-						currY += - config.size * Math.sin( angle );
-						path.push( currX, currY, 0 );
+					angle -= config.angle;
 
-					}
+				} else if ( char === 'F' ) {
+
+					currX += config.size * Math.cos( angle );
+					currY += - config.size * Math.sin( angle );
+					path.push( currX, currY, 0 );
 
 				}
 
-				return path;
+			}
 
-			} //
+			return path;
 
+		} //
 
-			const gosper = fractalize( {
-				axiom: 'A',
-				steps: 4,
-				rules: {
-					A: 'A+BF++BF-FA--FAFA-BF+',
-					B: '-FA+BFBF++BF+FA--FA-B'
-				}
-			} );
-			const points = toPoints( {
-				fractal: gosper,
-				size: size,
-				angle: Math.PI / 3 // 60 degrees
 
-			} );
-			return points;
+		const gosper = fractalize( {
+			axiom: 'A',
+			steps: 4,
+			rules: {
+				A: 'A+BF++BF-FA--FAFA-BF+',
+				B: '-FA+BFBF++BF+FA--FA-B'
+			}
+		} );
+		const points = toPoints( {
+			fractal: gosper,
+			size: size,
+			angle: Math.PI / 3 // 60 degrees
 
-		}
+		} );
+		return points;
 
 	}
 
-	THREE.GeometryUtils = GeometryUtils;
+	THREE.GeometryUtils = {};
+	THREE.GeometryUtils.gosper = gosper;
+	THREE.GeometryUtils.hilbert2D = hilbert2D;
+	THREE.GeometryUtils.hilbert3D = hilbert3D;
 
 } )();

+ 107 - 0
examples/js/utils/PackedPhongMaterial.js

@@ -0,0 +1,107 @@
+( function () {
+
+	/**
+ * `PackedPhongMaterial` inherited from THREE.MeshPhongMaterial
+ *
+ * @param {Object} parameters
+ */
+
+	class PackedPhongMaterial extends THREE.MeshPhongMaterial {
+
+		constructor( parameters ) {
+
+			super();
+			this.defines = {};
+			this.type = 'PackedPhongMaterial';
+			this.uniforms = THREE.UniformsUtils.merge( [ THREE.ShaderLib.phong.uniforms, {
+				quantizeMatPos: {
+					value: null
+				},
+				quantizeMatUV: {
+					value: null
+				}
+			} ] );
+			this.vertexShader = [ '#define PHONG', 'varying vec3 vViewPosition;', '#ifndef FLAT_SHADED', 'varying vec3 vNormal;', '#endif', THREE.ShaderChunk.common, THREE.ShaderChunk.uv_pars_vertex, THREE.ShaderChunk.uv2_pars_vertex, THREE.ShaderChunk.displacementmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.fog_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, THREE.ShaderChunk.logdepthbuf_pars_vertex, THREE.ShaderChunk.clipping_planes_pars_vertex, `#ifdef USE_PACKED_NORMAL
+					#if USE_PACKED_NORMAL == 0
+						vec3 decodeNormal(vec3 packedNormal)
+						{
+							float x = packedNormal.x * 2.0 - 1.0;
+							float y = packedNormal.y * 2.0 - 1.0;
+							vec2 scth = vec2(sin(x * PI), cos(x * PI));
+							vec2 scphi = vec2(sqrt(1.0 - y * y), y);
+							return normalize( vec3(scth.y * scphi.x, scth.x * scphi.x, scphi.y) );
+						}
+					#endif
+
+					#if USE_PACKED_NORMAL == 1
+						vec3 decodeNormal(vec3 packedNormal)
+						{
+							vec3 v = vec3(packedNormal.xy, 1.0 - abs(packedNormal.x) - abs(packedNormal.y));
+							if (v.z < 0.0)
+							{
+								v.xy = (1.0 - abs(v.yx)) * vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
+							}
+							return normalize(v);
+						}
+					#endif
+
+					#if USE_PACKED_NORMAL == 2
+						vec3 decodeNormal(vec3 packedNormal)
+						{
+							vec3 v = (packedNormal * 2.0) - 1.0;
+							return normalize(v);
+						}
+					#endif
+				#endif`, `#ifdef USE_PACKED_POSITION
+					#if USE_PACKED_POSITION == 0
+						uniform mat4 quantizeMatPos;
+					#endif
+				#endif`, `#ifdef USE_PACKED_UV
+					#if USE_PACKED_UV == 1
+						uniform mat3 quantizeMatUV;
+					#endif
+				#endif`, `#ifdef USE_PACKED_UV
+					#if USE_PACKED_UV == 0
+						vec2 decodeUV(vec2 packedUV)
+						{
+							vec2 uv = (packedUV * 2.0) - 1.0;
+							return uv;
+						}
+					#endif
+
+					#if USE_PACKED_UV == 1
+						vec2 decodeUV(vec2 packedUV)
+						{
+							vec2 uv = ( vec3(packedUV, 1.0) * quantizeMatUV ).xy;
+							return uv;
+						}
+					#endif
+				#endif`, 'void main() {', THREE.ShaderChunk.uv_vertex, `#ifdef USE_UV
+					#ifdef USE_PACKED_UV
+						vUv = decodeUV(vUv);
+					#endif
+				#endif`, THREE.ShaderChunk.uv2_vertex, THREE.ShaderChunk.color_vertex, THREE.ShaderChunk.beginnormal_vertex, `#ifdef USE_PACKED_NORMAL
+					objectNormal = decodeNormal(objectNormal);
+				#endif
+
+				#ifdef USE_TANGENT
+					vec3 objectTangent = vec3( tangent.xyz );
+				#endif
+				`, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, '#ifndef FLAT_SHADED', '	vNormal = normalize( transformedNormal );', '#endif', THREE.ShaderChunk.begin_vertex, `#ifdef USE_PACKED_POSITION
+					#if USE_PACKED_POSITION == 0
+						transformed = ( vec4(transformed, 1.0) * quantizeMatPos ).xyz;
+					#endif
+				#endif`, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.displacementmap_vertex, THREE.ShaderChunk.project_vertex, THREE.ShaderChunk.logdepthbuf_vertex, THREE.ShaderChunk.clipping_planes_vertex, 'vViewPosition = - mvPosition.xyz;', THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.shadowmap_vertex, THREE.ShaderChunk.fog_vertex, '}' ].join( '\n' ); // Use the original THREE.MeshPhongMaterial's fragmentShader.
+
+			this.fragmentShader = [ '#define PHONG', 'uniform vec3 diffuse;', 'uniform vec3 emissive;', 'uniform vec3 specular;', 'uniform float shininess;', 'uniform float opacity;', THREE.ShaderChunk.common, THREE.ShaderChunk.packing, THREE.ShaderChunk.dithering_pars_fragment, THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.uv_pars_fragment, THREE.ShaderChunk.uv2_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.alphamap_pars_fragment, THREE.ShaderChunk.aomap_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.emissivemap_pars_fragment, THREE.ShaderChunk.envmap_common_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.cube_uv_reflection_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.bsdfs, THREE.ShaderChunk.lights_pars_begin, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, THREE.ShaderChunk.logdepthbuf_pars_fragment, THREE.ShaderChunk.clipping_planes_pars_fragment, 'void main() {', THREE.ShaderChunk.clipping_planes_fragment, 'vec4 diffuseColor = vec4( diffuse, opacity );', 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', 'vec3 totalEmissiveRadiance = emissive;', THREE.ShaderChunk.logdepthbuf_fragment, THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.alphamap_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.normal_fragment_begin, THREE.ShaderChunk.normal_fragment_maps, THREE.ShaderChunk.emissivemap_fragment, // accumulation
+				THREE.ShaderChunk.lights_phong_fragment, THREE.ShaderChunk.lights_fragment_begin, THREE.ShaderChunk.lights_fragment_maps, THREE.ShaderChunk.lights_fragment_end, // modulation
+				THREE.ShaderChunk.aomap_fragment, 'vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;', THREE.ShaderChunk.envmap_fragment, 'gl_FragColor = vec4( outgoingLight, diffuseColor.a );', THREE.ShaderChunk.tonemapping_fragment, THREE.ShaderChunk.encodings_fragment, THREE.ShaderChunk.fog_fragment, THREE.ShaderChunk.premultiplied_alpha_fragment, THREE.ShaderChunk.dithering_fragment, '}' ].join( '\n' );
+			this.setValues( parameters );
+
+		}
+
+	}
+
+	THREE.PackedPhongMaterial = PackedPhongMaterial;
+
+} )();

+ 34 - 34
examples/js/utils/SceneUtils.js

@@ -1,60 +1,60 @@
 ( function () {
 
-	class SceneUtils {
+	function createMeshesFromInstancedMesh( instancedMesh ) {
 
-		static createMeshesFromInstancedMesh( instancedMesh ) {
+		const group = new THREE.Group();
+		const count = instancedMesh.count;
+		const geometry = instancedMesh.geometry;
+		const material = instancedMesh.material;
 
-			const group = new THREE.Group();
-			const count = instancedMesh.count;
-			const geometry = instancedMesh.geometry;
-			const material = instancedMesh.material;
+		for ( let i = 0; i < count; i ++ ) {
 
-			for ( let i = 0; i < count; i ++ ) {
-
-				const mesh = new THREE.Mesh( geometry, material );
-				instancedMesh.getMatrixAt( i, mesh.matrix );
-				mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
-				group.add( mesh );
-
-			}
-
-			group.copy( instancedMesh );
-			group.updateMatrixWorld(); // ensure correct world matrices of meshes
-
-			return group;
+			const mesh = new THREE.Mesh( geometry, material );
+			instancedMesh.getMatrixAt( i, mesh.matrix );
+			mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
+			group.add( mesh );
 
 		}
 
-		static createMultiMaterialObject( geometry, materials ) {
+		group.copy( instancedMesh );
+		group.updateMatrixWorld(); // ensure correct world matrices of meshes
+
+		return group;
 
-			const group = new THREE.Group();
+	}
 
-			for ( let i = 0, l = materials.length; i < l; i ++ ) {
+	function createMultiMaterialObject( geometry, materials ) {
 
-				group.add( new THREE.Mesh( geometry, materials[ i ] ) );
+		const group = new THREE.Group();
 
-			}
+		for ( let i = 0, l = materials.length; i < l; i ++ ) {
 
-			return group;
+			group.add( new THREE.Mesh( geometry, materials[ i ] ) );
 
 		}
 
-		static detach( child, parent, scene ) {
+		return group;
 
-			console.warn( 'THREE.SceneUtils: detach() has been deprecated. Use scene.attach( child ) instead.' );
-			scene.attach( child );
+	}
 
-		}
+	function detach( child, parent, scene ) {
 
-		static attach( child, scene, parent ) {
+		console.warn( 'THREE.SceneUtils: detach() has been deprecated. Use scene.attach( child ) instead.' );
+		scene.attach( child );
 
-			console.warn( 'THREE.SceneUtils: attach() has been deprecated. Use parent.attach( child ) instead.' );
-			parent.attach( child );
+	}
 
-		}
+	function attach( child, scene, parent ) {
+
+		console.warn( 'THREE.SceneUtils: attach() has been deprecated. Use parent.attach( child ) instead.' );
+		parent.attach( child );
 
 	}
 
-	THREE.SceneUtils = SceneUtils;
+	THREE.SceneUtils = {};
+	THREE.SceneUtils.attach = attach;
+	THREE.SceneUtils.createMeshesFromInstancedMesh = createMeshesFromInstancedMesh;
+	THREE.SceneUtils.createMultiMaterialObject = createMultiMaterialObject;
+	THREE.SceneUtils.detach = detach;
 
 } )();

+ 290 - 283
examples/js/utils/SkeletonUtils.js

@@ -1,485 +1,481 @@
 ( function () {
 
-	class SkeletonUtils {
-
-		static retarget( target, source, options = {} ) {
-
-			const pos = new THREE.Vector3(),
-				quat = new THREE.Quaternion(),
-				scale = new THREE.Vector3(),
-				bindBoneMatrix = new THREE.Matrix4(),
-				relativeMatrix = new THREE.Matrix4(),
-				globalMatrix = new THREE.Matrix4();
-			options.preserveMatrix = options.preserveMatrix !== undefined ? options.preserveMatrix : true;
-			options.preservePosition = options.preservePosition !== undefined ? options.preservePosition : true;
-			options.preserveHipPosition = options.preserveHipPosition !== undefined ? options.preserveHipPosition : false;
-			options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
-			options.hip = options.hip !== undefined ? options.hip : 'hip';
-			options.names = options.names || {};
-			const sourceBones = source.isObject3D ? source.skeleton.bones : this.getBones( source ),
-				bones = target.isObject3D ? target.skeleton.bones : this.getBones( target );
-			let bindBones, bone, name, boneTo, bonesPosition; // reset bones
-
-			if ( target.isObject3D ) {
-
-				target.skeleton.pose();
+	function retarget( target, source, options = {} ) {
+
+		const pos = new THREE.Vector3(),
+			quat = new THREE.Quaternion(),
+			scale = new THREE.Vector3(),
+			bindBoneMatrix = new THREE.Matrix4(),
+			relativeMatrix = new THREE.Matrix4(),
+			globalMatrix = new THREE.Matrix4();
+		options.preserveMatrix = options.preserveMatrix !== undefined ? options.preserveMatrix : true;
+		options.preservePosition = options.preservePosition !== undefined ? options.preservePosition : true;
+		options.preserveHipPosition = options.preserveHipPosition !== undefined ? options.preserveHipPosition : false;
+		options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
+		options.hip = options.hip !== undefined ? options.hip : 'hip';
+		options.names = options.names || {};
+		const sourceBones = source.isObject3D ? source.skeleton.bones : getBones( source ),
+			bones = target.isObject3D ? target.skeleton.bones : getBones( target );
+		let bindBones, bone, name, boneTo, bonesPosition; // reset bones
+
+		if ( target.isObject3D ) {
 
-			} else {
+			target.skeleton.pose();
 
-				options.useTargetMatrix = true;
-				options.preserveMatrix = false;
+		} else {
 
-			}
+			options.useTargetMatrix = true;
+			options.preserveMatrix = false;
 
-			if ( options.preservePosition ) {
+		}
 
-				bonesPosition = [];
+		if ( options.preservePosition ) {
 
-				for ( let i = 0; i < bones.length; i ++ ) {
+			bonesPosition = [];
 
-					bonesPosition.push( bones[ i ].position.clone() );
+			for ( let i = 0; i < bones.length; i ++ ) {
 
-				}
+				bonesPosition.push( bones[ i ].position.clone() );
 
 			}
 
-			if ( options.preserveMatrix ) {
+		}
 
-				// reset matrix
-				target.updateMatrixWorld();
-				target.matrixWorld.identity(); // reset children matrix
+		if ( options.preserveMatrix ) {
 
-				for ( let i = 0; i < target.children.length; ++ i ) {
+			// reset matrix
+			target.updateMatrixWorld();
+			target.matrixWorld.identity(); // reset children matrix
 
-					target.children[ i ].updateMatrixWorld( true );
+			for ( let i = 0; i < target.children.length; ++ i ) {
 
-				}
+				target.children[ i ].updateMatrixWorld( true );
 
 			}
 
-			if ( options.offsets ) {
-
-				bindBones = [];
+		}
 
-				for ( let i = 0; i < bones.length; ++ i ) {
+		if ( options.offsets ) {
 
-					bone = bones[ i ];
-					name = options.names[ bone.name ] || bone.name;
+			bindBones = [];
 
-					if ( options.offsets && options.offsets[ name ] ) {
+			for ( let i = 0; i < bones.length; ++ i ) {
 
-						bone.matrix.multiply( options.offsets[ name ] );
-						bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
-						bone.updateMatrixWorld();
+				bone = bones[ i ];
+				name = options.names[ bone.name ] || bone.name;
 
-					}
+				if ( options.offsets && options.offsets[ name ] ) {
 
-					bindBones.push( bone.matrixWorld.clone() );
+					bone.matrix.multiply( options.offsets[ name ] );
+					bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+					bone.updateMatrixWorld();
 
 				}
 
-			}
+				bindBones.push( bone.matrixWorld.clone() );
 
-			for ( let i = 0; i < bones.length; ++ i ) {
+			}
 
-				bone = bones[ i ];
-				name = options.names[ bone.name ] || bone.name;
-				boneTo = this.getBoneByName( name, sourceBones );
-				globalMatrix.copy( bone.matrixWorld );
+		}
 
-				if ( boneTo ) {
+		for ( let i = 0; i < bones.length; ++ i ) {
 
-					boneTo.updateMatrixWorld();
+			bone = bones[ i ];
+			name = options.names[ bone.name ] || bone.name;
+			boneTo = getBoneByName( name, sourceBones );
+			globalMatrix.copy( bone.matrixWorld );
 
-					if ( options.useTargetMatrix ) {
+			if ( boneTo ) {
 
-						relativeMatrix.copy( boneTo.matrixWorld );
+				boneTo.updateMatrixWorld();
 
-					} else {
+				if ( options.useTargetMatrix ) {
 
-						relativeMatrix.copy( target.matrixWorld ).invert();
-						relativeMatrix.multiply( boneTo.matrixWorld );
+					relativeMatrix.copy( boneTo.matrixWorld );
 
-					} // ignore scale to extract rotation
+				} else {
 
+					relativeMatrix.copy( target.matrixWorld ).invert();
+					relativeMatrix.multiply( boneTo.matrixWorld );
 
-					scale.setFromMatrixScale( relativeMatrix );
-					relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) ); // apply to global matrix
+				} // ignore scale to extract rotation
 
-					globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
 
-					if ( target.isObject3D ) {
+				scale.setFromMatrixScale( relativeMatrix );
+				relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) ); // apply to global matrix
 
-						const boneIndex = bones.indexOf( bone ),
-							wBindMatrix = bindBones ? bindBones[ boneIndex ] : bindBoneMatrix.copy( target.skeleton.boneInverses[ boneIndex ] ).invert();
-						globalMatrix.multiply( wBindMatrix );
+				globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
 
-					}
+				if ( target.isObject3D ) {
 
-					globalMatrix.copyPosition( relativeMatrix );
+					const boneIndex = bones.indexOf( bone ),
+						wBindMatrix = bindBones ? bindBones[ boneIndex ] : bindBoneMatrix.copy( target.skeleton.boneInverses[ boneIndex ] ).invert();
+					globalMatrix.multiply( wBindMatrix );
 
 				}
 
-				if ( bone.parent && bone.parent.isBone ) {
+				globalMatrix.copyPosition( relativeMatrix );
 
-					bone.matrix.copy( bone.parent.matrixWorld ).invert();
-					bone.matrix.multiply( globalMatrix );
+			}
 
-				} else {
+			if ( bone.parent && bone.parent.isBone ) {
 
-					bone.matrix.copy( globalMatrix );
+				bone.matrix.copy( bone.parent.matrixWorld ).invert();
+				bone.matrix.multiply( globalMatrix );
 
-				}
+			} else {
 
-				if ( options.preserveHipPosition && name === options.hip ) {
+				bone.matrix.copy( globalMatrix );
 
-					bone.matrix.setPosition( pos.set( 0, bone.position.y, 0 ) );
+			}
 
-				}
+			if ( options.preserveHipPosition && name === options.hip ) {
 
-				bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
-				bone.updateMatrixWorld();
+				bone.matrix.setPosition( pos.set( 0, bone.position.y, 0 ) );
 
 			}
 
-			if ( options.preservePosition ) {
+			bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+			bone.updateMatrixWorld();
 
-				for ( let i = 0; i < bones.length; ++ i ) {
+		}
 
-					bone = bones[ i ];
-					name = options.names[ bone.name ] || bone.name;
+		if ( options.preservePosition ) {
 
-					if ( name !== options.hip ) {
+			for ( let i = 0; i < bones.length; ++ i ) {
+
+				bone = bones[ i ];
+				name = options.names[ bone.name ] || bone.name;
 
-						bone.position.copy( bonesPosition[ i ] );
+				if ( name !== options.hip ) {
 
-					}
+					bone.position.copy( bonesPosition[ i ] );
 
 				}
 
 			}
 
-			if ( options.preserveMatrix ) {
+		}
 
-				// restore matrix
-				target.updateMatrixWorld( true );
+		if ( options.preserveMatrix ) {
 
-			}
+			// restore matrix
+			target.updateMatrixWorld( true );
 
 		}
 
-		static retargetClip( target, source, clip, options = {} ) {
+	}
 
-			options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
-			options.fps = options.fps !== undefined ? options.fps : 30;
-			options.names = options.names || [];
+	function retargetClip( target, source, clip, options = {} ) {
 
-			if ( ! source.isObject3D ) {
+		options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
+		options.fps = options.fps !== undefined ? options.fps : 30;
+		options.names = options.names || [];
 
-				source = this.getHelperFromSkeleton( source );
+		if ( ! source.isObject3D ) {
 
-			}
+			source = getHelperFromSkeleton( source );
 
-			const numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
-				delta = 1 / options.fps,
-				convertedTracks = [],
-				mixer = new THREE.AnimationMixer( source ),
-				bones = this.getBones( target.skeleton ),
-				boneDatas = [];
-			let positionOffset, bone, boneTo, boneData, name;
-			mixer.clipAction( clip ).play();
-			mixer.update( 0 );
-			source.updateMatrixWorld();
-
-			for ( let i = 0; i < numFrames; ++ i ) {
+		}
 
-				const time = i * delta;
-				this.retarget( target, source, options );
+		const numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
+			delta = 1 / options.fps,
+			convertedTracks = [],
+			mixer = new THREE.AnimationMixer( source ),
+			bones = getBones( target.skeleton ),
+			boneDatas = [];
+		let positionOffset, bone, boneTo, boneData, name;
+		mixer.clipAction( clip ).play();
+		mixer.update( 0 );
+		source.updateMatrixWorld();
 
-				for ( let j = 0; j < bones.length; ++ j ) {
+		for ( let i = 0; i < numFrames; ++ i ) {
 
-					name = options.names[ bones[ j ].name ] || bones[ j ].name;
-					boneTo = this.getBoneByName( name, source.skeleton );
+			const time = i * delta;
+			retarget( target, source, options );
 
-					if ( boneTo ) {
+			for ( let j = 0; j < bones.length; ++ j ) {
 
-						bone = bones[ j ];
-						boneData = boneDatas[ j ] = boneDatas[ j ] || {
-							bone: bone
-						};
+				name = options.names[ bones[ j ].name ] || bones[ j ].name;
+				boneTo = getBoneByName( name, source.skeleton );
 
-						if ( options.hip === name ) {
+				if ( boneTo ) {
 
-							if ( ! boneData.pos ) {
+					bone = bones[ j ];
+					boneData = boneDatas[ j ] = boneDatas[ j ] || {
+						bone: bone
+					};
 
-								boneData.pos = {
-									times: new Float32Array( numFrames ),
-									values: new Float32Array( numFrames * 3 )
-								};
+					if ( options.hip === name ) {
 
-							}
+						if ( ! boneData.pos ) {
 
-							if ( options.useFirstFramePosition ) {
+							boneData.pos = {
+								times: new Float32Array( numFrames ),
+								values: new Float32Array( numFrames * 3 )
+							};
 
-								if ( i === 0 ) {
+						}
 
-									positionOffset = bone.position.clone();
+						if ( options.useFirstFramePosition ) {
 
-								}
+							if ( i === 0 ) {
 
-								bone.position.sub( positionOffset );
+								positionOffset = bone.position.clone();
 
 							}
 
-							boneData.pos.times[ i ] = time;
-							bone.position.toArray( boneData.pos.values, i * 3 );
+							bone.position.sub( positionOffset );
 
 						}
 
-						if ( ! boneData.quat ) {
+						boneData.pos.times[ i ] = time;
+						bone.position.toArray( boneData.pos.values, i * 3 );
 
-							boneData.quat = {
-								times: new Float32Array( numFrames ),
-								values: new Float32Array( numFrames * 4 )
-							};
+					}
 
-						}
+					if ( ! boneData.quat ) {
 
-						boneData.quat.times[ i ] = time;
-						bone.quaternion.toArray( boneData.quat.values, i * 4 );
+						boneData.quat = {
+							times: new Float32Array( numFrames ),
+							values: new Float32Array( numFrames * 4 )
+						};
 
 					}
 
-				}
+					boneData.quat.times[ i ] = time;
+					bone.quaternion.toArray( boneData.quat.values, i * 4 );
 
-				mixer.update( delta );
-				source.updateMatrixWorld();
+				}
 
 			}
 
-			for ( let i = 0; i < boneDatas.length; ++ i ) {
+			mixer.update( delta );
+			source.updateMatrixWorld();
 
-				boneData = boneDatas[ i ];
+		}
 
-				if ( boneData ) {
+		for ( let i = 0; i < boneDatas.length; ++ i ) {
 
-					if ( boneData.pos ) {
+			boneData = boneDatas[ i ];
 
-						convertedTracks.push( new THREE.VectorKeyframeTrack( '.bones[' + boneData.bone.name + '].position', boneData.pos.times, boneData.pos.values ) );
+			if ( boneData ) {
 
-					}
+				if ( boneData.pos ) {
 
-					convertedTracks.push( new THREE.QuaternionKeyframeTrack( '.bones[' + boneData.bone.name + '].quaternion', boneData.quat.times, boneData.quat.values ) );
+					convertedTracks.push( new THREE.VectorKeyframeTrack( '.bones[' + boneData.bone.name + '].position', boneData.pos.times, boneData.pos.values ) );
 
 				}
 
-			}
+				convertedTracks.push( new THREE.QuaternionKeyframeTrack( '.bones[' + boneData.bone.name + '].quaternion', boneData.quat.times, boneData.quat.values ) );
 
-			mixer.uncacheAction( clip );
-			return new THREE.AnimationClip( clip.name, - 1, convertedTracks );
+			}
 
 		}
 
-		static getHelperFromSkeleton( skeleton ) {
-
-			const source = new THREE.SkeletonHelper( skeleton.bones[ 0 ] );
-			source.skeleton = skeleton;
-			return source;
+		mixer.uncacheAction( clip );
+		return new THREE.AnimationClip( clip.name, - 1, convertedTracks );
 
-		}
+	}
 
-		static getSkeletonOffsets( target, source, options = {} ) {
+	function getHelperFromSkeleton( skeleton ) {
 
-			const targetParentPos = new THREE.Vector3(),
-				targetPos = new THREE.Vector3(),
-				sourceParentPos = new THREE.Vector3(),
-				sourcePos = new THREE.Vector3(),
-				targetDir = new THREE.Vector2(),
-				sourceDir = new THREE.Vector2();
-			options.hip = options.hip !== undefined ? options.hip : 'hip';
-			options.names = options.names || {};
+		const source = new THREE.SkeletonHelper( skeleton.bones[ 0 ] );
+		source.skeleton = skeleton;
+		return source;
 
-			if ( ! source.isObject3D ) {
+	}
 
-				source = this.getHelperFromSkeleton( source );
+	function getSkeletonOffsets( target, source, options = {} ) {
 
-			}
+		const targetParentPos = new THREE.Vector3(),
+			targetPos = new THREE.Vector3(),
+			sourceParentPos = new THREE.Vector3(),
+			sourcePos = new THREE.Vector3(),
+			targetDir = new THREE.Vector2(),
+			sourceDir = new THREE.Vector2();
+		options.hip = options.hip !== undefined ? options.hip : 'hip';
+		options.names = options.names || {};
 
-			const nameKeys = Object.keys( options.names ),
-				nameValues = Object.values( options.names ),
-				sourceBones = source.isObject3D ? source.skeleton.bones : this.getBones( source ),
-				bones = target.isObject3D ? target.skeleton.bones : this.getBones( target ),
-				offsets = [];
-			let bone, boneTo, name, i;
-			target.skeleton.pose();
+		if ( ! source.isObject3D ) {
 
-			for ( i = 0; i < bones.length; ++ i ) {
+			source = getHelperFromSkeleton( source );
 
-				bone = bones[ i ];
-				name = options.names[ bone.name ] || bone.name;
-				boneTo = this.getBoneByName( name, sourceBones );
-
-				if ( boneTo && name !== options.hip ) {
-
-					const boneParent = this.getNearestBone( bone.parent, nameKeys ),
-						boneToParent = this.getNearestBone( boneTo.parent, nameValues );
-					boneParent.updateMatrixWorld();
-					boneToParent.updateMatrixWorld();
-					targetParentPos.setFromMatrixPosition( boneParent.matrixWorld );
-					targetPos.setFromMatrixPosition( bone.matrixWorld );
-					sourceParentPos.setFromMatrixPosition( boneToParent.matrixWorld );
-					sourcePos.setFromMatrixPosition( boneTo.matrixWorld );
-					targetDir.subVectors( new THREE.Vector2( targetPos.x, targetPos.y ), new THREE.Vector2( targetParentPos.x, targetParentPos.y ) ).normalize();
-					sourceDir.subVectors( new THREE.Vector2( sourcePos.x, sourcePos.y ), new THREE.Vector2( sourceParentPos.x, sourceParentPos.y ) ).normalize();
-					const laterialAngle = targetDir.angle() - sourceDir.angle();
-					const offset = new THREE.Matrix4().makeRotationFromEuler( new THREE.Euler( 0, 0, laterialAngle ) );
-					bone.matrix.multiply( offset );
-					bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
-					bone.updateMatrixWorld();
-					offsets[ name ] = offset;
+		}
 
-				}
+		const nameKeys = Object.keys( options.names ),
+			nameValues = Object.values( options.names ),
+			sourceBones = source.isObject3D ? source.skeleton.bones : getBones( source ),
+			bones = target.isObject3D ? target.skeleton.bones : getBones( target ),
+			offsets = [];
+		let bone, boneTo, name, i;
+		target.skeleton.pose();
+
+		for ( i = 0; i < bones.length; ++ i ) {
+
+			bone = bones[ i ];
+			name = options.names[ bone.name ] || bone.name;
+			boneTo = getBoneByName( name, sourceBones );
+
+			if ( boneTo && name !== options.hip ) {
+
+				const boneParent = getNearestBone( bone.parent, nameKeys ),
+					boneToParent = getNearestBone( boneTo.parent, nameValues );
+				boneParent.updateMatrixWorld();
+				boneToParent.updateMatrixWorld();
+				targetParentPos.setFromMatrixPosition( boneParent.matrixWorld );
+				targetPos.setFromMatrixPosition( bone.matrixWorld );
+				sourceParentPos.setFromMatrixPosition( boneToParent.matrixWorld );
+				sourcePos.setFromMatrixPosition( boneTo.matrixWorld );
+				targetDir.subVectors( new THREE.Vector2( targetPos.x, targetPos.y ), new THREE.Vector2( targetParentPos.x, targetParentPos.y ) ).normalize();
+				sourceDir.subVectors( new THREE.Vector2( sourcePos.x, sourcePos.y ), new THREE.Vector2( sourceParentPos.x, sourceParentPos.y ) ).normalize();
+				const laterialAngle = targetDir.angle() - sourceDir.angle();
+				const offset = new THREE.Matrix4().makeRotationFromEuler( new THREE.Euler( 0, 0, laterialAngle ) );
+				bone.matrix.multiply( offset );
+				bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
+				bone.updateMatrixWorld();
+				offsets[ name ] = offset;
 
 			}
 
-			return offsets;
-
 		}
 
-		static renameBones( skeleton, names ) {
+		return offsets;
 
-			const bones = this.getBones( skeleton );
+	}
 
-			for ( let i = 0; i < bones.length; ++ i ) {
+	function renameBones( skeleton, names ) {
 
-				const bone = bones[ i ];
+		const bones = getBones( skeleton );
 
-				if ( names[ bone.name ] ) {
+		for ( let i = 0; i < bones.length; ++ i ) {
 
-					bone.name = names[ bone.name ];
+			const bone = bones[ i ];
 
-				}
+			if ( names[ bone.name ] ) {
 
-			}
+				bone.name = names[ bone.name ];
 
-			return this;
+			}
 
 		}
 
-		static getBones( skeleton ) {
+		return this;
 
-			return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
+	}
 
-		}
+	function getBones( skeleton ) {
 
-		static getBoneByName( name, skeleton ) {
+		return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
 
-			for ( let i = 0, bones = this.getBones( skeleton ); i < bones.length; i ++ ) {
+	}
 
-				if ( name === bones[ i ].name ) return bones[ i ];
+	function getBoneByName( name, skeleton ) {
 
-			}
+		for ( let i = 0, bones = getBones( skeleton ); i < bones.length; i ++ ) {
 
-		}
+			if ( name === bones[ i ].name ) return bones[ i ];
 
-		static getNearestBone( bone, names ) {
+		}
 
-			while ( bone.isBone ) {
+	}
 
-				if ( names.indexOf( bone.name ) !== - 1 ) {
+	function getNearestBone( bone, names ) {
 
-					return bone;
+		while ( bone.isBone ) {
 
-				}
+			if ( names.indexOf( bone.name ) !== - 1 ) {
 
-				bone = bone.parent;
+				return bone;
 
 			}
 
+			bone = bone.parent;
+
 		}
 
-		static findBoneTrackData( name, tracks ) {
+	}
 
-			const regexp = /\[(.*)\]\.(.*)/,
-				result = {
-					name: name
-				};
+	function findBoneTrackData( name, tracks ) {
 
-			for ( let i = 0; i < tracks.length; ++ i ) {
+		const regexp = /\[(.*)\]\.(.*)/,
+			result = {
+				name: name
+			};
 
-				// 1 is track name
-				// 2 is track type
-				const trackData = regexp.exec( tracks[ i ].name );
+		for ( let i = 0; i < tracks.length; ++ i ) {
 
-				if ( trackData && name === trackData[ 1 ] ) {
+			// 1 is track name
+			// 2 is track type
+			const trackData = regexp.exec( tracks[ i ].name );
 
-					result[ trackData[ 2 ] ] = i;
+			if ( trackData && name === trackData[ 1 ] ) {
 
-				}
+				result[ trackData[ 2 ] ] = i;
 
 			}
 
-			return result;
-
 		}
 
-		static getEqualsBonesNames( skeleton, targetSkeleton ) {
+		return result;
 
-			const sourceBones = this.getBones( skeleton ),
-				targetBones = this.getBones( targetSkeleton ),
-				bones = [];
+	}
 
-			search: for ( let i = 0; i < sourceBones.length; i ++ ) {
+	function getEqualsBonesNames( skeleton, targetSkeleton ) {
 
-				const boneName = sourceBones[ i ].name;
+		const sourceBones = getBones( skeleton ),
+			targetBones = getBones( targetSkeleton ),
+			bones = [];
 
-				for ( let j = 0; j < targetBones.length; j ++ ) {
+		search: for ( let i = 0; i < sourceBones.length; i ++ ) {
 
-					if ( boneName === targetBones[ j ].name ) {
+			const boneName = sourceBones[ i ].name;
 
-						bones.push( boneName );
-						continue search;
+			for ( let j = 0; j < targetBones.length; j ++ ) {
 
-					}
+				if ( boneName === targetBones[ j ].name ) {
+
+					bones.push( boneName );
+					continue search;
 
 				}
 
 			}
 
-			return bones;
-
 		}
 
-		static clone( source ) {
+		return bones;
+
+	}
 
-			const sourceLookup = new Map();
-			const cloneLookup = new Map();
-			const clone = source.clone();
-			parallelTraverse( source, clone, function ( sourceNode, clonedNode ) {
+	function clone( source ) {
 
-				sourceLookup.set( clonedNode, sourceNode );
-				cloneLookup.set( sourceNode, clonedNode );
+		const sourceLookup = new Map();
+		const cloneLookup = new Map();
+		const clone = source.clone();
+		parallelTraverse( source, clone, function ( sourceNode, clonedNode ) {
 
-			} );
-			clone.traverse( function ( node ) {
+			sourceLookup.set( clonedNode, sourceNode );
+			cloneLookup.set( sourceNode, clonedNode );
 
-				if ( ! node.isSkinnedMesh ) return;
-				const clonedMesh = node;
-				const sourceMesh = sourceLookup.get( node );
-				const sourceBones = sourceMesh.skeleton.bones;
-				clonedMesh.skeleton = sourceMesh.skeleton.clone();
-				clonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );
-				clonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {
+		} );
+		clone.traverse( function ( node ) {
 
-					return cloneLookup.get( bone );
+			if ( ! node.isSkinnedMesh ) return;
+			const clonedMesh = node;
+			const sourceMesh = sourceLookup.get( node );
+			const sourceBones = sourceMesh.skeleton.bones;
+			clonedMesh.skeleton = sourceMesh.skeleton.clone();
+			clonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );
+			clonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {
 
-				} );
-				clonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );
+				return cloneLookup.get( bone );
 
 			} );
-			return clone;
+			clonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );
 
-		}
+		} );
+		return clone;
 
 	}
 
@@ -495,6 +491,17 @@
 
 	}
 
-	THREE.SkeletonUtils = SkeletonUtils;
+	THREE.SkeletonUtils = {};
+	THREE.SkeletonUtils.clone = clone;
+	THREE.SkeletonUtils.findBoneTrackData = findBoneTrackData;
+	THREE.SkeletonUtils.getBoneByName = getBoneByName;
+	THREE.SkeletonUtils.getBones = getBones;
+	THREE.SkeletonUtils.getEqualsBonesNames = getEqualsBonesNames;
+	THREE.SkeletonUtils.getHelperFromSkeleton = getHelperFromSkeleton;
+	THREE.SkeletonUtils.getNearestBone = getNearestBone;
+	THREE.SkeletonUtils.getSkeletonOffsets = getSkeletonOffsets;
+	THREE.SkeletonUtils.renameBones = renameBones;
+	THREE.SkeletonUtils.retarget = retarget;
+	THREE.SkeletonUtils.retargetClip = retargetClip;
 
 } )();

+ 115 - 0
examples/js/utils/WorkerPool.js

@@ -0,0 +1,115 @@
+( function () {
+
+	/**
+ * @author Deepkolos / https://github.com/deepkolos
+ */
+	class WorkerPool {
+
+		constructor( pool = 4 ) {
+
+			this.pool = pool;
+			this.queue = [];
+			this.workers = [];
+			this.workersResolve = [];
+			this.workerStatus = 0;
+
+		}
+
+		_initWorker( workerId ) {
+
+			if ( ! this.workers[ workerId ] ) {
+
+				const worker = this.workerCreator();
+				worker.addEventListener( 'message', this._onMessage.bind( this, workerId ) );
+				this.workers[ workerId ] = worker;
+
+			}
+
+		}
+
+		_getIdleWorker() {
+
+			for ( let i = 0; i < this.pool; i ++ ) if ( ! ( this.workerStatus & 1 << i ) ) return i;
+
+			return - 1;
+
+		}
+
+		_onMessage( workerId, msg ) {
+
+			const resolve = this.workersResolve[ workerId ];
+			resolve && resolve( msg );
+
+			if ( this.queue.length ) {
+
+				const {
+					resolve,
+					msg,
+					transfer
+				} = this.queue.shift();
+				this.workersResolve[ workerId ] = resolve;
+				this.workers[ workerId ].postMessage( msg, transfer );
+
+			} else {
+
+				this.workerStatus ^= 1 << workerId;
+
+			}
+
+		}
+
+		setWorkerCreator( workerCreator ) {
+
+			this.workerCreator = workerCreator;
+
+		}
+
+		setWorkerLimit( pool ) {
+
+			this.pool = pool;
+
+		}
+
+		postMessage( msg, transfer ) {
+
+			return new Promise( resolve => {
+
+				const workerId = this._getIdleWorker();
+
+				if ( workerId !== - 1 ) {
+
+					this._initWorker( workerId );
+
+					this.workerStatus |= 1 << workerId;
+					this.workersResolve[ workerId ] = resolve;
+					this.workers[ workerId ].postMessage( msg, transfer );
+
+				} else {
+
+					this.queue.push( {
+						resolve,
+						msg,
+						transfer
+					} );
+
+				}
+
+			} );
+
+		}
+
+		dispose() {
+
+			this.workers.forEach( worker => worker.terminate() );
+			this.workersResolve.length = 0;
+			this.workers.length = 0;
+			this.queue.length = 0;
+			this.workerStatus = 0;
+
+		}
+
+	}
+
+	THREE.WorkerPool = WorkerPool;
+
+} )();

+ 5 - 5
examples/jsm/csm/CSMShader.js

@@ -28,7 +28,7 @@ IncidentLight directLight;
 
 		pointLight = pointLights[ i ];
 
-		getPointDirectLightIrradiance( pointLight, geometry, directLight );
+		getPointLightInfo( pointLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
 		pointLightShadow = pointLightShadows[ i ];
@@ -54,7 +54,7 @@ IncidentLight directLight;
 
 		spotLight = spotLights[ i ];
 
-		getSpotDirectLightIrradiance( spotLight, geometry, directLight );
+		getSpotLightInfo( spotLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
 		spotLightShadow = spotLightShadows[ i ];
@@ -88,7 +88,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 		// NOTE: Depth gets larger away from the camera.
 		// cascade.x is closer, cascade.y is further
@@ -134,7 +134,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 
@@ -165,7 +165,7 @@ IncidentLight directLight;
 
 		directionalLight = directionalLights[ i ];
 
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		directionalLightShadow = directionalLightShadows[ i ];

+ 1 - 1
examples/jsm/curves/NURBSCurve.js

@@ -3,7 +3,7 @@ import {
 	Vector3,
 	Vector4
 } from '../../../build/three.module.js';
-import { NURBSUtils } from '../curves/NURBSUtils.js';
+import * as NURBSUtils from '../curves/NURBSUtils.js';
 
 /**
  * NURBS curve object

+ 1 - 1
examples/jsm/curves/NURBSSurface.js

@@ -1,7 +1,7 @@
 import {
 	Vector4
 } from '../../../build/three.module.js';
-import { NURBSUtils } from '../curves/NURBSUtils.js';
+import * as NURBSUtils from '../curves/NURBSUtils.js';
 
 /**
  * NURBS surface object

+ 281 - 273
examples/jsm/curves/NURBSUtils.js

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

+ 26 - 2
examples/jsm/interactive/SelectionBox.js

@@ -1,6 +1,8 @@
 import {
 	Frustum,
-	Vector3
+	Vector3,
+	Matrix4,
+	Quaternion,
 } from '../../../build/three.module.js';
 
 /**
@@ -27,6 +29,10 @@ const _vectemp1 = new Vector3();
 const _vectemp2 = new Vector3();
 const _vectemp3 = new Vector3();
 
+const _matrix = new Matrix4();
+const _quaternion = new Quaternion();
+const _scale = new Vector3();
+
 class SelectionBox {
 
 	constructor( camera, scene, deep = Number.MAX_VALUE ) {
@@ -36,6 +42,7 @@ class SelectionBox {
 		this.startPoint = new Vector3();
 		this.endPoint = new Vector3();
 		this.collection = [];
+		this.instances = {};
 		this.deep = deep;
 
 	}
@@ -167,7 +174,24 @@ class SelectionBox {
 
 		if ( object.isMesh || object.isLine || object.isPoints ) {
 
-			if ( object.material !== undefined ) {
+			if ( object.isInstancedMesh ) {
+
+				this.instances[ object.uuid ] = [];
+
+				for ( let instanceId = 0; instanceId < object.count; instanceId ++ ) {
+
+					object.getMatrixAt( instanceId, _matrix );
+					_matrix.decompose( _center, _quaternion, _scale );
+
+					if ( frustum.containsPoint( _center ) ) {
+
+						this.instances[ object.uuid ].push( instanceId );
+
+					}
+
+				}
+
+			} else {
 
 				if ( object.geometry.boundingSphere === null ) object.geometry.computeBoundingSphere();
 

File diff suppressed because it is too large
+ 0 - 114
examples/jsm/libs/zstddec.module.js


+ 222 - 62
examples/jsm/lines/LineMaterial.js

@@ -1,11 +1,3 @@
-import {
-	ShaderLib,
-	ShaderMaterial,
-	UniformsLib,
-	UniformsUtils,
-	Vector2
-} from '../../../build/three.module.js';
-
 /**
  * parameters = {
  *  color: <hex>,
@@ -13,21 +5,28 @@ import {
  *  dashed: <boolean>,
  *  dashScale: <float>,
  *  dashSize: <float>,
- *  dashOffset: <float>,
  *  gapSize: <float>,
  *  resolution: <Vector2>, // to be set by renderer
  * }
  */
 
+import {
+	ShaderLib,
+	ShaderMaterial,
+	UniformsLib,
+	UniformsUtils,
+	Vector2
+} from "../../../build/three.module.js";
+
+
 UniformsLib.line = {
 
+	worldUnits: { value: 1 },
 	linewidth: { value: 1 },
 	resolution: { value: new Vector2( 1, 1 ) },
 	dashScale: { value: 1 },
 	dashSize: { value: 1 },
-	dashOffset: { value: 0 },
-	gapSize: { value: 1 }, // todo FIX - maybe change to totalSize
-	opacity: { value: 1 }
+	gapSize: { value: 1 } // todo FIX - maybe change to totalSize
 
 };
 
@@ -39,7 +38,8 @@ ShaderLib[ 'line' ] = {
 		UniformsLib.line
 	] ),
 
-	vertexShader: /* glsl */`
+	vertexShader:
+		/* glsl */`
 		#include <common>
 		#include <color_pars_vertex>
 		#include <fog_pars_vertex>
@@ -56,6 +56,9 @@ ShaderLib[ 'line' ] = {
 		attribute vec3 instanceColorEnd;
 
 		varying vec2 vUv;
+		varying vec4 worldPos;
+		varying vec3 worldStart;
+		varying vec3 worldEnd;
 
 		#ifdef USE_DASH
 
@@ -103,6 +106,9 @@ ShaderLib[ 'line' ] = {
 			vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
 			vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
 
+			worldStart = start.xyz;
+			worldEnd = end.xyz;
+
 			// 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
@@ -129,50 +135,108 @@ ShaderLib[ 'line' ] = {
 			vec4 clipEnd = projectionMatrix * end;
 
 			// ndc space
-			vec2 ndcStart = clipStart.xy / clipStart.w;
-			vec2 ndcEnd = clipEnd.xy / clipEnd.w;
+			vec3 ndcStart = clipStart.xyz / clipStart.w;
+			vec3 ndcEnd = clipEnd.xyz / clipEnd.w;
 
 			// direction
-			vec2 dir = ndcEnd - ndcStart;
+			vec2 dir = ndcEnd.xy - ndcStart.xy;
 
 			// account for clip-space aspect ratio
 			dir.x *= aspect;
 			dir = normalize( dir );
 
-			// perpendicular to dir
-			vec2 offset = vec2( dir.y, - dir.x );
+			#ifdef WORLD_UNITS
 
-			// undo aspect ratio adjustment
-			dir.x /= aspect;
-			offset.x /= aspect;
+				// get the offset direction as perpendicular to the view vector
+				vec3 worldDir = normalize( end.xyz - start.xyz );
+				vec3 offset;
+				if ( position.y < 0.5 ) {
 
-			// sign flip
-			if ( position.x < 0.0 ) offset *= - 1.0;
+					offset = normalize( cross( start.xyz, worldDir ) );
 
-			// endcaps
-			if ( position.y < 0.0 ) {
+				} else {
 
-				offset += - dir;
+					offset = normalize( cross( end.xyz, worldDir ) );
 
-			} else if ( position.y > 1.0 ) {
+				}
 
-				offset += dir;
+				// 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
 
-			// adjust for linewidth
-			offset *= linewidth;
+					// 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 clip-space to screen-space conversion // maybe resolution should be based on viewport ...
-			offset /= resolution.y;
+				// adjust for linewidth
+				offset *= linewidth * 0.5;
 
-			// select end
-			vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;
+				// set the world position
+				worldPos = ( position.y < 0.5 ) ? start : end;
+				worldPos.xyz += offset;
 
-			// back to clip space
-			offset *= clip.w;
+				// project the worldpos
+				vec4 clip = projectionMatrix * worldPos;
 
-			clip.xy += offset;
+				// shift the depth of the projected points so the line
+				// segements 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;
 
@@ -182,21 +246,26 @@ ShaderLib[ 'line' ] = {
 			#include <clipping_planes_vertex>
 			#include <fog_vertex>
 
-		}`,
+		}
+		`,
 
-	fragmentShader: /* glsl */`
+	fragmentShader:
+		/* glsl */`
 		uniform vec3 diffuse;
 		uniform float opacity;
+		uniform float linewidth;
 
 		#ifdef USE_DASH
 
 			uniform float dashSize;
-			uniform float dashOffset;
 			uniform float gapSize;
 
 		#endif
 
 		varying float vLineDistance;
+		varying vec4 worldPos;
+		varying vec3 worldStart;
+		varying vec3 worldEnd;
 
 		#include <common>
 		#include <color_pars_fragment>
@@ -206,6 +275,35 @@ ShaderLib[ 'line' ] = {
 
 		varying vec2 vUv;
 
+		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>
@@ -214,54 +312,90 @@ ShaderLib[ 'line' ] = {
 
 				if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
 
-				if ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
+				if ( mod( vLineDistance, dashSize + gapSize ) > dashSize ) discard; // todo - FIX
 
 			#endif
 
-			float alpha = 1.0;
+			float alpha = opacity;
 
-			#ifdef ALPHA_TO_COVERAGE
+			#ifdef WORLD_UNITS
 
-			// 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 );
+				// 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 );
 
-			if ( abs( vUv.y ) > 1.0 ) {
+				vec3 p1 = worldStart + lineDir * params.x;
+				vec3 p2 = rayEnd * params.y;
+				vec3 delta = p1 - p2;
+				float len = length( delta );
+				float norm = len / linewidth;
 
-				alpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );
+				#ifndef USE_DASH
 
-			}
+					#ifdef 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
 
-			if ( abs( vUv.y ) > 1.0 ) {
+				#ifdef ALPHA_TO_COVERAGE
 
-				float a = vUv.x;
-				float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;
-				float len2 = a * a + b * b;
+					// 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 ( len2 > 1.0 ) discard;
+					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, opacity * alpha );
+			vec4 diffuseColor = vec4( diffuse, alpha );
 
 			#include <logdepthbuf_fragment>
 			#include <color_fragment>
 
-			gl_FragColor = diffuseColor;
+			gl_FragColor = vec4( diffuseColor.rgb, alpha );
 
 			#include <tonemapping_fragment>
 			#include <encodings_fragment>
 			#include <fog_fragment>
 			#include <premultiplied_alpha_fragment>
 
-		}`
-
+		}
+		`
 };
 
 class LineMaterial extends ShaderMaterial {
@@ -301,6 +435,32 @@ class LineMaterial extends ShaderMaterial {
 
 			},
 
+			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,

+ 5 - 11
examples/jsm/loaders/FBXLoader.js

@@ -624,6 +624,7 @@ class FBXTreeParser {
 						parameters.map.encoding = sRGBEncoding;
 
 					}
+
 					break;
 
 				case 'DisplacementColor':
@@ -637,6 +638,7 @@ class FBXTreeParser {
 						parameters.emissiveMap.encoding = sRGBEncoding;
 
 					}
+
 					break;
 
 				case 'NormalMap':
@@ -652,6 +654,7 @@ class FBXTreeParser {
 						parameters.envMap.encoding = sRGBEncoding;
 
 					}
+
 					break;
 
 				case 'SpecularColor':
@@ -661,6 +664,7 @@ class FBXTreeParser {
 						parameters.specularMap.encoding = sRGBEncoding;
 
 					}
+
 					break;
 
 				case 'TransparentColor':
@@ -696,17 +700,7 @@ class FBXTreeParser {
 
 		}
 
-		const texture = textureMap.get( id );
-
-		if ( texture.image !== undefined ) {
-
-			return texture;
-
-		} else {
-
-			return undefined;
-
-		}
+		return textureMap.get( id );
 
 	}
 

+ 25 - 4
examples/jsm/loaders/GLTFLoader.js

@@ -45,6 +45,7 @@ import {
 	Points,
 	PointsMaterial,
 	PropertyBinding,
+	Quaternion,
 	QuaternionKeyframeTrack,
 	RGBFormat,
 	RepeatWrapping,
@@ -1405,9 +1406,9 @@ class GLTFMeshStandardSGMaterial extends MeshStandardMaterial {
 			'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );',
 			'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );',
 			'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );',
-			'material.specularRoughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.',
-			'material.specularRoughness += geometryRoughness;',
-			'material.specularRoughness = min( material.specularRoughness, 1.0 );',
+			'material.roughness = max( 1.0 - glossinessFactor, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.',
+			'material.roughness += geometryRoughness;',
+			'material.roughness = min( material.roughness, 1.0 );',
 			'material.specularColor = specularFactor;',
 		].join( '\n' );
 
@@ -1777,6 +1778,23 @@ GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) {
 
 };
 
+const _q = new 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 ************/
 /*********************************/
@@ -3100,6 +3118,7 @@ class GLTFParser {
 
 		} else {
 
+			materialParams.format = RGBFormat;
 			materialParams.transparent = false;
 
 			if ( alphaMode === ALPHA_MODES.MASK ) {
@@ -3615,7 +3634,9 @@ class GLTFParser {
 							// representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
 							// must be divided by three to get the interpolant's sampleSize argument.
 
-							return new GLTFCubicSplineInterpolant( this.times, this.values, this.getValueSize() / 3, result );
+							const interpolantType = ( this instanceof QuaternionKeyframeTrack ) ? GLTFCubicSplineQuaternionInterpolant : GLTFCubicSplineInterpolant;
+
+							return new interpolantType( this.times, this.values, this.getValueSize() / 3, result );
 
 						};
 

+ 2175 - 628
examples/jsm/loaders/IFCLoader.js

@@ -2,882 +2,2429 @@ import {
 	IFCRELAGGREGATES,
 	IFCRELCONTAINEDINSPATIALSTRUCTURE,
 	IFCRELDEFINESBYPROPERTIES,
+	IFCRELASSOCIATESMATERIAL,
 	IFCRELDEFINESBYTYPE,
 	IFCPROJECT,
-	IfcAPI,
-} from "./ifc/web-ifc-api.js";
+	IfcAPI
+} from './ifc/web-ifc-api.js';
 import {
 	BufferAttribute,
-	BufferGeometry,
 	Mesh,
 	Matrix4,
+	BufferGeometry,
 	Color,
 	MeshLambertMaterial,
 	DoubleSide,
-	Group,
 	Loader,
-	FileLoader,
-} from "../../../build/three.module.js";
-import { BufferGeometryUtils } from "../utils/BufferGeometryUtils.js";
+	FileLoader
+} from '../../../build/three.module.js';
+import { mergeBufferGeometries } from '../utils/BufferGeometryUtils.js';
+
+const IdAttrName = 'expressID';
+const merge = ( geoms, createGroups = false ) => {
+
+	return mergeBufferGeometries( geoms, createGroups );
 
-const IdAttrName = "expressID";
-const merge = (geoms, createGroups = false) => {
-	return BufferGeometryUtils.mergeBufferGeometries(geoms, createGroups);
 };
-const newFloatAttr = (data, size) => {
-	return new BufferAttribute(new Float32Array(data), size);
+
+const newFloatAttr = ( data, size ) => {
+
+	return new BufferAttribute( new Float32Array( data ), size );
+
 };
-const newIntAttr = (data, size) => {
-	return new BufferAttribute(new Uint32Array(data), size);
+
+const newIntAttr = ( data, size ) => {
+
+	return new BufferAttribute( new Uint32Array( data ), size );
+
 };
-const DEFAULT = "default";
+
+const DEFAULT = 'default';
 const PropsNames = {
 	aggregates: {
 		name: IFCRELAGGREGATES,
-		relating: "RelatingObject",
-		related: "RelatedObjects",
-		key: "children",
+		relating: 'RelatingObject',
+		related: 'RelatedObjects',
+		key: 'children'
 	},
 	spatial: {
 		name: IFCRELCONTAINEDINSPATIALSTRUCTURE,
-		relating: "RelatingStructure",
-		related: "RelatedElements",
-		key: "children",
+		relating: 'RelatingStructure',
+		related: 'RelatedElements',
+		key: 'children'
 	},
 	psets: {
 		name: IFCRELDEFINESBYPROPERTIES,
-		relating: "RelatingPropertyDefinition",
-		related: "RelatedObjects",
-		key: "hasPsets",
+		relating: 'RelatingPropertyDefinition',
+		related: 'RelatedObjects',
+		key: 'hasPsets'
+	},
+	materials: {
+		name: IFCRELASSOCIATESMATERIAL,
+		relating: 'RelatingMaterial',
+		related: 'RelatedObjects',
+		key: 'hasMaterial'
 	},
 	type: {
 		name: IFCRELDEFINESBYTYPE,
-		relating: "RelatingType",
-		related: "RelatedObjects",
-		key: "hasType",
-	},
+		relating: 'RelatingType',
+		related: 'RelatedObjects',
+		key: 'hasType'
+	}
 };
 
 class IFCParser {
-	constructor(state) {
-		this.currentID = -1;
+
+	constructor( state, BVH ) {
+
 		this.state = state;
-	}
+		this.BVH = BVH;
+		this.loadedModels = 0;
+		this.currentWebIfcID = - 1;
+		this.currentModelID = - 1;
 
-	async parse(buffer) {
-		if (this.state.api.wasmModule === undefined) await this.state.api.Init();
-		this.currentID = this.newIfcModel(buffer);
-		return this.loadAllGeometry();
 	}
 
-	initializeMeshBVH(computeBoundsTree, disposeBoundsTree, acceleratedRaycast) {
-		this.computeBoundsTree = computeBoundsTree;
-		this.disposeBoundsTree = disposeBoundsTree;
-		this.acceleratedRaycast = acceleratedRaycast;
-		this.setupThreeMeshBVH();
-	}
+	async parse( buffer ) {
 
-	setupThreeMeshBVH() {
-		if (
-			!this.computeBoundsTree ||
-			!this.disposeBoundsTree ||
-			!this.acceleratedRaycast
-		)
-			return;
-		BufferGeometry.prototype.computeBoundsTree = this.computeBoundsTree;
-		BufferGeometry.prototype.disposeBoundsTree = this.disposeBoundsTree;
-		Mesh.prototype.raycast = this.acceleratedRaycast;
-	}
+		if ( this.state.api.wasmModule === undefined )
+			await this.state.api.Init();
+		this.newIfcModel( buffer );
+		this.loadedModels ++;
+		return this.loadAllGeometry();
 
-	applyThreeMeshBVH(geometry) {
-		if (this.computeBoundsTree) geometry.computeBoundsTree();
 	}
 
-	newIfcModel(buffer) {
-		const data = new Uint8Array(buffer);
-		const modelID = this.state.api.OpenModel(data);
-		this.state.models[modelID] = {
-			modelID,
+	newIfcModel( buffer ) {
+
+		const data = new Uint8Array( buffer );
+		this.currentWebIfcID = this.state.api.OpenModel( data, this.state.webIfcSettings );
+		this.currentModelID = this.state.useJSON ? this.loadedModels : this.currentWebIfcID;
+		this.state.models[ this.currentModelID ] = {
+			modelID: this.currentModelID,
 			mesh: {},
 			items: {},
 			types: {},
+			jsonData: {}
 		};
-		return modelID;
+
 	}
 
 	loadAllGeometry() {
+
 		this.saveAllPlacedGeometriesByMaterial();
 		return this.generateAllGeometriesByMaterial();
+
 	}
 
 	generateAllGeometriesByMaterial() {
+
 		const { geometry, materials } = this.getGeometryAndMaterials();
-		this.applyThreeMeshBVH(geometry);
-		const mesh = new Mesh(geometry, materials);
-		mesh.modelID = this.currentID;
-		this.state.models[this.currentID].mesh = mesh;
+		this.BVH.applyThreeMeshBVH( geometry );
+		const mesh = new Mesh( geometry, materials );
+		mesh.modelID = this.currentModelID;
+		this.state.models[ this.currentModelID ].mesh = mesh;
 		return mesh;
+
 	}
 
 	getGeometryAndMaterials() {
-		const items = this.state.models[this.currentID].items;
+
+		const items = this.state.models[ this.currentModelID ].items;
 		const mergedByMaterial = [];
 		const materials = [];
-		for (let materialID in items) {
-			materials.push(items[materialID].material);
-			const geometries = Object.values(items[materialID].geometries);
-			mergedByMaterial.push(merge(geometries));
+		for ( const materialID in items ) {
+
+			materials.push( items[ materialID ].material );
+			const geometries = Object.values( items[ materialID ].geometries );
+			mergedByMaterial.push( merge( geometries ) );
+
 		}
-		const geometry = merge(mergedByMaterial, true);
+
+		const geometry = merge( mergedByMaterial, true );
 		return {
 			geometry,
-			materials,
+			materials
 		};
+
 	}
 
 	saveAllPlacedGeometriesByMaterial() {
-		const flatMeshes = this.state.api.LoadAllGeometry(this.currentID);
-		for (let i = 0; i < flatMeshes.size(); i++) {
-			const flatMesh = flatMeshes.get(i);
+
+		const flatMeshes = this.state.api.LoadAllGeometry( this.currentWebIfcID );
+		for ( let i = 0; i < flatMeshes.size(); i ++ ) {
+
+			const flatMesh = flatMeshes.get( i );
 			const placedGeom = flatMesh.geometries;
-			for (let j = 0; j < placedGeom.size(); j++) {
-				this.savePlacedGeometry(placedGeom.get(j), flatMesh.expressID);
+			for ( let j = 0; j < placedGeom.size(); j ++ ) {
+
+				this.savePlacedGeometry( placedGeom.get( j ), flatMesh.expressID );
+
 			}
+
 		}
+
 	}
 
-	savePlacedGeometry(placedGeometry, id) {
-		const geometry = this.getBufferGeometry(placedGeometry);
+	savePlacedGeometry( placedGeometry, id ) {
+
+		const geometry = this.getBufferGeometry( placedGeometry );
 		geometry.computeVertexNormals();
-		const matrix = this.getMeshMatrix(placedGeometry.flatTransformation);
-		geometry.applyMatrix4(matrix);
-		this.saveGeometryByMaterial(geometry, placedGeometry, id);
+		const matrix = this.getMeshMatrix( placedGeometry.flatTransformation );
+		geometry.applyMatrix4( matrix );
+		this.saveGeometryByMaterial( geometry, placedGeometry, id );
+
 	}
 
-	getBufferGeometry(placed) {
-		const geometry = this.state.api.GetGeometry(
-			this.currentID,
-			placed.geometryExpressID
-		);
-		const vertexData = this.getVertices(geometry);
-		const indices = this.getIndices(geometry);
-		const { vertices, normals } = this.extractVertexData(vertexData);
-		return this.ifcGeomToBufferGeom(vertices, normals, indices);
+	getBufferGeometry( placed ) {
+
+		const geometry = this.state.api.GetGeometry( this.currentWebIfcID, placed.geometryExpressID );
+		const vertexData = this.getVertices( geometry );
+		const indices = this.getIndices( geometry );
+		const { vertices, normals } = this.extractVertexData( vertexData );
+		return this.ifcGeomToBufferGeom( vertices, normals, indices );
+
 	}
 
-	getVertices(geometry) {
+	getVertices( geometry ) {
+
 		const vData = geometry.GetVertexData();
 		const vDataSize = geometry.GetVertexDataSize();
-		return this.state.api.GetVertexArray(vData, vDataSize);
+		return this.state.api.GetVertexArray( vData, vDataSize );
+
 	}
 
-	getIndices(geometry) {
+	getIndices( geometry ) {
+
 		const iData = geometry.GetIndexData();
 		const iDataSize = geometry.GetIndexDataSize();
-		return this.state.api.GetIndexArray(iData, iDataSize);
+		return this.state.api.GetIndexArray( iData, iDataSize );
+
 	}
 
-	getMeshMatrix(matrix) {
+	getMeshMatrix( matrix ) {
+
 		const mat = new Matrix4();
-		mat.fromArray(matrix);
+		mat.fromArray( matrix );
 		return mat;
+
 	}
 
-	ifcGeomToBufferGeom(vertices, normals, indexData) {
+	ifcGeomToBufferGeom( vertices, normals, indexData ) {
+
 		const geometry = new BufferGeometry();
-		geometry.setAttribute("position", newFloatAttr(vertices, 3));
-		geometry.setAttribute("normal", newFloatAttr(normals, 3));
-		geometry.setIndex(new BufferAttribute(indexData, 1));
+		geometry.setAttribute( 'position', newFloatAttr( vertices, 3 ) );
+		geometry.setAttribute( 'normal', newFloatAttr( normals, 3 ) );
+		geometry.setIndex( new BufferAttribute( indexData, 1 ) );
 		return geometry;
+
 	}
 
-	extractVertexData(vertexData) {
+	extractVertexData( vertexData ) {
+
 		const vertices = [];
 		const normals = [];
 		let isNormalData = false;
-		for (let i = 0; i < vertexData.length; i++) {
-			isNormalData ? normals.push(vertexData[i]) : vertices.push(vertexData[i]);
-			if ((i + 1) % 3 == 0) isNormalData = !isNormalData;
+		for ( let i = 0; i < vertexData.length; i ++ ) {
+
+			isNormalData ? normals.push( vertexData[ i ] ) : vertices.push( vertexData[ i ] );
+			if ( ( i + 1 ) % 3 == 0 )
+				isNormalData = ! isNormalData;
+
 		}
+
 		return {
 			vertices,
-			normals,
+			normals
 		};
+
 	}
 
-	saveGeometryByMaterial(geom, placedGeom, id) {
+	saveGeometryByMaterial( geom, placedGeom, id ) {
+
 		const color = placedGeom.color;
 		const colorID = `${color.x}${color.y}${color.z}${color.w}`;
-		this.storeGeometryAttribute(id, geom);
-		this.createMaterial(colorID, color);
-		const item = this.state.models[this.currentID].items[colorID];
-		const currentGeom = item.geometries[id];
-		if (!currentGeom) return (item.geometries[id] = geom);
-		const merged = merge([currentGeom, geom]);
-		item.geometries[id] = merged;
+		this.storeGeometryAttribute( id, geom );
+		this.createMaterial( colorID, color );
+		const item = this.state.models[ this.currentModelID ].items[ colorID ];
+		const currentGeom = item.geometries[ id ];
+		if ( ! currentGeom )
+			return ( item.geometries[ id ] = geom );
+		const merged = merge( [ currentGeom, geom ] );
+		item.geometries[ id ] = merged;
+
 	}
 
-	storeGeometryAttribute(id, geometry) {
+	storeGeometryAttribute( id, geometry ) {
+
 		const size = geometry.attributes.position.count;
-		const idAttribute = new Array(size).fill(id);
-		geometry.setAttribute(IdAttrName, newIntAttr(idAttribute, 1));
+		const idAttribute = new Array( size ).fill( id );
+		geometry.setAttribute( IdAttrName, newIntAttr( idAttribute, 1 ) );
+
 	}
 
-	createMaterial(colorID, color) {
-		const items = this.state.models[this.currentID].items;
-		if (items[colorID]) return;
-		const col = new Color(color.x, color.y, color.z);
-		const newMaterial = new MeshLambertMaterial({
+	createMaterial( colorID, color ) {
+
+		const items = this.state.models[ this.currentModelID ].items;
+		if ( items[ colorID ] )
+			return;
+		const col = new Color( color.x, color.y, color.z );
+		const newMaterial = new MeshLambertMaterial( {
 			color: col,
-			side: DoubleSide,
-		});
+			side: DoubleSide
+		} );
 		newMaterial.transparent = color.w !== 1;
-		if (newMaterial.transparent) newMaterial.opacity = color.w;
-		items[colorID] = {
+		if ( newMaterial.transparent )
+			newMaterial.opacity = color.w;
+		items[ colorID ] = {
 			material: newMaterial,
-			geometries: {},
+			geometries: {}
 		};
+
 	}
+
 }
 
 class SubsetManager {
-	constructor(state) {
-		this.state = state;
+
+	constructor( state, BVH ) {
+
 		this.selected = {};
+		this.state = state;
+		this.BVH = BVH;
+
 	}
 
-	getSubset(modelID, material) {
-		const currentMat = this.matIDNoConfig(modelID, material);
-		if (!this.selected[currentMat]) return null;
-		return this.selected[currentMat].mesh;
+	getSubset( modelID, material ) {
+
+		const currentMat = this.matIDNoConfig( modelID, material );
+		if ( ! this.selected[ currentMat ] )
+			return null;
+		return this.selected[ currentMat ].mesh;
+
 	}
 
-	removeSubset(modelID, scene, material) {
-		const currentMat = this.matIDNoConfig(modelID, material);
-		if (!this.selected[currentMat]) return;
-		if (scene) scene.remove(this.selected[currentMat].mesh);
-		delete this.selected[currentMat];
+	removeSubset( modelID, parent, material ) {
+
+		const currentMat = this.matIDNoConfig( modelID, material );
+		if ( ! this.selected[ currentMat ] )
+			return;
+		if ( parent )
+			parent.remove( this.selected[ currentMat ].mesh );
+		delete this.selected[ currentMat ];
+
 	}
 
-	createSubset(config) {
-		if (!this.isConfigValid(config)) return;
-		if (this.isPreviousSelection(config)) return;
-		if (this.isEasySelection(config))
-			return this.addToPreviousSelection(config);
-		this.updatePreviousSelection(config.scene, config);
-		return this.createSelectionInScene(config);
+	createSubset( config ) {
+
+		if ( ! this.isConfigValid( config ) )
+			return;
+		if ( this.isPreviousSelection( config ) )
+			return;
+		if ( this.isEasySelection( config ) )
+			return this.addToPreviousSelection( config );
+		this.updatePreviousSelection( config.scene, config );
+		return this.createSelectionInScene( config );
+
 	}
 
-	createSelectionInScene(config) {
-		const filtered = this.filter(config);
-		const { geomsByMaterial, materials } = this.getGeomAndMat(filtered);
-		const hasDefaultMaterial = this.matID(config) == DEFAULT;
-		const geometry = merge(geomsByMaterial, hasDefaultMaterial);
-		const mats = hasDefaultMaterial ? materials : config.material;
-		const mesh = new Mesh(geometry, mats);
-		this.selected[this.matID(config)].mesh = mesh;
+	createSelectionInScene( config ) {
+
+		const filtered = this.filter( config );
+		const { geomsByMaterial, materials } = this.getGeomAndMat( filtered );
+		const isDefMaterial = this.isDefaultMat( config );
+		const geometry = this.getMergedGeometry( geomsByMaterial, isDefMaterial );
+		const mats = isDefMaterial ? materials : config.material;
+		this.BVH.applyThreeMeshBVH( geometry );
+		const mesh = new Mesh( geometry, mats );
+		this.selected[ this.matID( config ) ].mesh = mesh;
 		mesh.modelID = config.modelID;
-		config.scene.add(mesh);
+		config.scene.add( mesh );
 		return mesh;
+
 	}
 
-	isConfigValid(config) {
-		return (
-			this.isValid(config.scene) &&
-			this.isValid(config.modelID) &&
-			this.isValid(config.ids) &&
-			this.isValid(config.removePrevious)
-		);
+	getMergedGeometry( geomsByMaterial, hasDefaultMaterial ) {
+
+		return geomsByMaterial.length > 0
+			? merge( geomsByMaterial, hasDefaultMaterial )
+			: new BufferGeometry();
+
+	}
+
+	isConfigValid( config ) {
+
+		return ( this.isValid( config.scene ) &&
+			this.isValid( config.modelID ) &&
+			this.isValid( config.ids ) &&
+			this.isValid( config.removePrevious ) );
+
 	}
 
-	isValid(item) {
+	isValid( item ) {
+
 		return item != undefined && item != null;
+
 	}
 
-	getGeomAndMat(filtered) {
+	getGeomAndMat( filtered ) {
+
 		const geomsByMaterial = [];
 		const materials = [];
-		for (let matID in filtered) {
-			const geoms = Object.values(filtered[matID].geometries);
-			if (!geoms.length) continue;
-			materials.push(filtered[matID].material);
-			if (geoms.length > 1) geomsByMaterial.push(merge(geoms));
-			else geomsByMaterial.push(...geoms);
+		for ( const matID in filtered ) {
+
+			const geoms = Object.values( filtered[ matID ].geometries );
+			if ( ! geoms.length )
+				continue;
+			materials.push( filtered[ matID ].material );
+			if ( geoms.length > 1 )
+				geomsByMaterial.push( merge( geoms ) );
+			else
+				geomsByMaterial.push( ...geoms );
+
 		}
+
 		return {
 			geomsByMaterial,
-			materials,
+			materials
 		};
+
 	}
 
-	updatePreviousSelection(scene, config) {
-		const previous = this.selected[this.matID(config)];
-		if (!previous) return this.newSelectionGroup(config);
-		scene.remove(previous.mesh);
+	updatePreviousSelection( parent, config ) {
+
+		const previous = this.selected[ this.matID( config ) ];
+		if ( ! previous )
+			return this.newSelectionGroup( config );
+		parent.remove( previous.mesh );
 		config.removePrevious
-			? (previous.ids = new Set(config.ids))
-			: config.ids.forEach((id) => previous.ids.add(id));
+			? ( previous.ids = new Set( config.ids ) )
+			: config.ids.forEach( ( id ) => previous.ids.add( id ) );
+
 	}
 
-	newSelectionGroup(config) {
-		this.selected[this.matID(config)] = {
-			ids: new Set(config.ids),
-			mesh: {},
+	newSelectionGroup( config ) {
+
+		this.selected[ this.matID( config ) ] = {
+			ids: new Set( config.ids ),
+			mesh: {}
 		};
+
 	}
 
-	isPreviousSelection(config) {
-		if (!this.selected[this.matID(config)]) return false;
-		if (this.containsIds(config)) return true;
-		const previousIds = this.selected[this.matID(config)].ids;
-		return JSON.stringify(config.ids) === JSON.stringify(previousIds);
+	isPreviousSelection( config ) {
+
+		if ( ! this.selected[ this.matID( config ) ] )
+			return false;
+		if ( this.containsIds( config ) )
+			return true;
+		const previousIds = this.selected[ this.matID( config ) ].ids;
+		return JSON.stringify( config.ids ) === JSON.stringify( previousIds );
+
 	}
 
-	containsIds(config) {
+	containsIds( config ) {
+
 		const newIds = config.ids;
-		const previous = Array.from(this.selected[this.matID(config)].ids);
-		return newIds.every(
-			(
-				(i) => (v) =>
-					(i = previous.indexOf(v, i) + 1)
-			)(0)
-		);
-	}
-
-	addToPreviousSelection(config) {
-		const previous = this.selected[this.matID(config)];
-		const filtered = this.filter(config);
-		const geometries = Object.values(filtered)
-			.map((i) => Object.values(i.geometries))
-			.flat();
+		const previous = Array.from( this.selected[ this.matID( config ) ].ids );
+		return newIds.every( ( i => v => ( i = previous.indexOf( v, i ) + 1 ) )( 0 ) );
+
+	}
+
+	addToPreviousSelection( config ) {
+
+		const previous = this.selected[ this.matID( config ) ];
+		const filtered = this.filter( config );
+		const geometries = Object.values( filtered ).map( ( i ) => Object.values( i.geometries ) ).flat();
 		const previousGeom = previous.mesh.geometry;
-		previous.mesh.geometry = merge([previousGeom, ...geometries]);
-		config.ids.forEach((id) => previous.ids.add(id));
+		previous.mesh.geometry = merge( [ previousGeom, ...geometries ] );
+		config.ids.forEach( ( id ) => previous.ids.add( id ) );
+
 	}
 
-	filter(config) {
-		const items = this.state.models[config.modelID].items;
+	filter( config ) {
+
+		const ids = this.selected[ this.matID( config ) ].ids;
+		const items = this.state.models[ config.modelID ].items;
 		const filtered = {};
-		for (let matID in items) {
-			filtered[matID] = {
-				material: items[matID].material,
-				geometries: this.filterGeometries(
-					new Set(config.ids),
-					items[matID].geometries
-				),
+		for ( const matID in items ) {
+
+			filtered[ matID ] = {
+				material: items[ matID ].material,
+				geometries: this.filterGeometries( ids, items[ matID ].geometries )
 			};
+
 		}
+
 		return filtered;
+
 	}
 
-	filterGeometries(selectedIDs, geometries) {
-		const ids = Array.from(selectedIDs);
-		return Object.keys(geometries)
-			.filter((key) => ids.includes(parseInt(key, 10)))
-			.reduce((obj, key) => {
+	filterGeometries( selectedIDs, geometries ) {
+
+		const ids = Array.from( selectedIDs );
+		return Object.keys( geometries )
+			.filter( ( key ) => ids.includes( parseInt( key, 10 ) ) )
+			.reduce( ( obj, key ) => {
+
 				return {
 					...obj,
-					[key]: geometries[key],
+					[ key ]: geometries[ key ]
 				};
-			}, {});
+
+			}, {} );
+
 	}
 
-	isEasySelection(config) {
-		const matID = this.matID(config);
-		const def = this.matIDNoConfig(config.modelID);
-		if (!config.removePrevious && matID != def && this.selected[matID])
+	isEasySelection( config ) {
+
+		const matID = this.matID( config );
+		if ( ! config.removePrevious && ! this.isDefaultMat( config ) && this.selected[ matID ] )
 			return true;
+
+	}
+
+	isDefaultMat( config ) {
+
+		return this.matIDNoConfig( config.modelID ) === this.matID( config );
+
 	}
 
-	matID(config) {
-		if (!config.material) return DEFAULT;
-		const name = config.material.uuid || DEFAULT;
-		return name.concat(" - ").concat(config.modelID.toString());
+	matID( config ) {
+
+		let name;
+		if ( ! config.material )
+			name = DEFAULT;
+		else
+			name = config.material.uuid || DEFAULT;
+		return name.concat( ' - ' ).concat( config.modelID.toString() );
+
 	}
 
-	matIDNoConfig(modelID, material) {
+	matIDNoConfig( modelID, material ) {
+
 		let name = DEFAULT;
-		if (material) name = material.uuid;
-		return name.concat(" - ").concat(modelID.toString());
+		if ( material )
+			name = material.uuid;
+		return name.concat( ' - ' ).concat( modelID.toString() );
+
 	}
+
 }
 
 const IfcElements = {
-	103090709: "IFCPROJECT",
-	4097777520: "IFCSITE",
-	4031249490: "IFCBUILDING",
-	3124254112: "IFCBUILDINGSTOREY",
-	3856911033: "IFCSPACE",
-	25142252: "IFCCONTROLLER",
-	32344328: "IFCBOILER",
-	76236018: "IFCLAMP",
-	90941305: "IFCPUMP",
-	177149247: "IFCAIRTERMINALBOX",
-	182646315: "IFCFLOWINSTRUMENT",
-	263784265: "IFCFURNISHINGELEMENT",
-	264262732: "IFCELECTRICGENERATOR",
-	277319702: "IFCAUDIOVISUALAPPLIANCE",
-	310824031: "IFCPIPEFITTING",
-	331165859: "IFCSTAIR",
-	342316401: "IFCDUCTFITTING",
-	377706215: "IFCMECHANICALFASTENER",
-	395920057: "IFCDOOR",
-	402227799: "IFCELECTRICMOTOR",
-	413509423: "IFCSYSTEMFURNITUREELEMENT",
-	484807127: "IFCEVAPORATOR",
-	486154966: "IFCWINDOWSTANDARDCASE",
-	629592764: "IFCLIGHTFIXTURE",
-	630975310: "IFCUNITARYCONTROLELEMENT",
-	635142910: "IFCCABLECARRIERFITTING",
-	639361253: "IFCCOIL",
-	647756555: "IFCFASTENER",
-	707683696: "IFCFLOWSTORAGEDEVICE",
-	738039164: "IFCPROTECTIVEDEVICE",
-	753842376: "IFCBEAM",
-	812556717: "IFCTANK",
-	819412036: "IFCFILTER",
-	843113511: "IFCCOLUMN",
-	862014818: "IFCELECTRICDISTRIBUTIONBOARD",
-	900683007: "IFCFOOTING",
-	905975707: "IFCCOLUMNSTANDARDCASE",
-	926996030: "IFCVOIDINGFEATURE",
-	979691226: "IFCREINFORCINGBAR",
-	987401354: "IFCFLOWSEGMENT",
-	1003880860: "IFCELECTRICTIMECONTROL",
-	1051757585: "IFCCABLEFITTING",
-	1052013943: "IFCDISTRIBUTIONCHAMBERELEMENT",
-	1062813311: "IFCDISTRIBUTIONCONTROLELEMENT",
-	1073191201: "IFCMEMBER",
-	1095909175: "IFCBUILDINGELEMENTPROXY",
-	1156407060: "IFCPLATESTANDARDCASE",
-	1162798199: "IFCSWITCHINGDEVICE",
-	1329646415: "IFCSHADINGDEVICE",
-	1335981549: "IFCDISCRETEACCESSORY",
-	1360408905: "IFCDUCTSILENCER",
-	1404847402: "IFCSTACKTERMINAL",
-	1426591983: "IFCFIRESUPPRESSIONTERMINAL",
-	1437502449: "IFCMEDICALDEVICE",
-	1509553395: "IFCFURNITURE",
-	1529196076: "IFCSLAB",
-	1620046519: "IFCTRANSPORTELEMENT",
-	1634111441: "IFCAIRTERMINAL",
-	1658829314: "IFCENERGYCONVERSIONDEVICE",
-	1677625105: "IFCCIVILELEMENT",
-	1687234759: "IFCPILE",
-	1904799276: "IFCELECTRICAPPLIANCE",
-	1911478936: "IFCMEMBERSTANDARDCASE",
-	1945004755: "IFCDISTRIBUTIONELEMENT",
-	1973544240: "IFCCOVERING",
-	1999602285: "IFCSPACEHEATER",
-	2016517767: "IFCROOF",
-	2056796094: "IFCAIRTOAIRHEATRECOVERY",
-	2058353004: "IFCFLOWCONTROLLER",
-	2068733104: "IFCHUMIDIFIER",
-	2176052936: "IFCJUNCTIONBOX",
-	2188021234: "IFCFLOWMETER",
-	2223149337: "IFCFLOWTERMINAL",
-	2262370178: "IFCRAILING",
-	2272882330: "IFCCONDENSER",
-	2295281155: "IFCPROTECTIVEDEVICETRIPPINGUNIT",
-	2320036040: "IFCREINFORCINGMESH",
-	2347447852: "IFCTENDONANCHOR",
-	2391383451: "IFCVIBRATIONISOLATOR",
-	2391406946: "IFCWALL",
-	2474470126: "IFCMOTORCONNECTION",
-	2769231204: "IFCVIRTUALELEMENT",
-	2814081492: "IFCENGINE",
-	2906023776: "IFCBEAMSTANDARDCASE",
-	2938176219: "IFCBURNER",
-	2979338954: "IFCBUILDINGELEMENTPART",
-	3024970846: "IFCRAMP",
-	3026737570: "IFCTUBEBUNDLE",
-	3027962421: "IFCSLABSTANDARDCASE",
-	3040386961: "IFCDISTRIBUTIONFLOWELEMENT",
-	3053780830: "IFCSANITARYTERMINAL",
-	3079942009: "IFCOPENINGSTANDARDCASE",
-	3087945054: "IFCALARM",
-	3101698114: "IFCSURFACEFEATURE",
-	3127900445: "IFCSLABELEMENTEDCASE",
-	3132237377: "IFCFLOWMOVINGDEVICE",
-	3171933400: "IFCPLATE",
-	3221913625: "IFCCOMMUNICATIONSAPPLIANCE",
-	3242481149: "IFCDOORSTANDARDCASE",
-	3283111854: "IFCRAMPFLIGHT",
-	3296154744: "IFCCHIMNEY",
-	3304561284: "IFCWINDOW",
-	3310460725: "IFCELECTRICFLOWSTORAGEDEVICE",
-	3319311131: "IFCHEATEXCHANGER",
-	3415622556: "IFCFAN",
-	3420628829: "IFCSOLARDEVICE",
-	3493046030: "IFCGEOGRAPHICELEMENT",
-	3495092785: "IFCCURTAINWALL",
-	3508470533: "IFCFLOWTREATMENTDEVICE",
-	3512223829: "IFCWALLSTANDARDCASE",
-	3518393246: "IFCDUCTSEGMENT",
-	3571504051: "IFCCOMPRESSOR",
-	3588315303: "IFCOPENINGELEMENT",
-	3612865200: "IFCPIPESEGMENT",
-	3640358203: "IFCCOOLINGTOWER",
-	3651124850: "IFCPROJECTIONELEMENT",
-	3694346114: "IFCOUTLET",
-	3747195512: "IFCEVAPORATIVECOOLER",
-	3758799889: "IFCCABLECARRIERSEGMENT",
-	3824725483: "IFCTENDON",
-	3825984169: "IFCTRANSFORMER",
-	3902619387: "IFCCHILLER",
-	4074379575: "IFCDAMPER",
-	4086658281: "IFCSENSOR",
-	4123344466: "IFCELEMENTASSEMBLY",
-	4136498852: "IFCCOOLEDBEAM",
-	4156078855: "IFCWALLELEMENTEDCASE",
-	4175244083: "IFCINTERCEPTOR",
-	4207607924: "IFCVALVE",
-	4217484030: "IFCCABLESEGMENT",
-	4237592921: "IFCWASTETERMINAL",
-	4252922144: "IFCSTAIRFLIGHT",
-	4278956645: "IFCFLOWFITTING",
-	4288193352: "IFCACTUATOR",
-	4292641817: "IFCUNITARYEQUIPMENT",
+	103090709: 'IFCPROJECT',
+	4097777520: 'IFCSITE',
+	4031249490: 'IFCBUILDING',
+	3124254112: 'IFCBUILDINGSTOREY',
+	3856911033: 'IFCSPACE',
+	1674181508: 'IFCANNOTATION',
+	25142252: 'IFCCONTROLLER',
+	32344328: 'IFCBOILER',
+	76236018: 'IFCLAMP',
+	90941305: 'IFCPUMP',
+	177149247: 'IFCAIRTERMINALBOX',
+	182646315: 'IFCFLOWINSTRUMENT',
+	263784265: 'IFCFURNISHINGELEMENT',
+	264262732: 'IFCELECTRICGENERATOR',
+	277319702: 'IFCAUDIOVISUALAPPLIANCE',
+	310824031: 'IFCPIPEFITTING',
+	331165859: 'IFCSTAIR',
+	342316401: 'IFCDUCTFITTING',
+	377706215: 'IFCMECHANICALFASTENER',
+	395920057: 'IFCDOOR',
+	402227799: 'IFCELECTRICMOTOR',
+	413509423: 'IFCSYSTEMFURNITUREELEMENT',
+	484807127: 'IFCEVAPORATOR',
+	486154966: 'IFCWINDOWSTANDARDCASE',
+	629592764: 'IFCLIGHTFIXTURE',
+	630975310: 'IFCUNITARYCONTROLELEMENT',
+	635142910: 'IFCCABLECARRIERFITTING',
+	639361253: 'IFCCOIL',
+	647756555: 'IFCFASTENER',
+	707683696: 'IFCFLOWSTORAGEDEVICE',
+	738039164: 'IFCPROTECTIVEDEVICE',
+	753842376: 'IFCBEAM',
+	812556717: 'IFCTANK',
+	819412036: 'IFCFILTER',
+	843113511: 'IFCCOLUMN',
+	862014818: 'IFCELECTRICDISTRIBUTIONBOARD',
+	900683007: 'IFCFOOTING',
+	905975707: 'IFCCOLUMNSTANDARDCASE',
+	926996030: 'IFCVOIDINGFEATURE',
+	979691226: 'IFCREINFORCINGBAR',
+	987401354: 'IFCFLOWSEGMENT',
+	1003880860: 'IFCELECTRICTIMECONTROL',
+	1051757585: 'IFCCABLEFITTING',
+	1052013943: 'IFCDISTRIBUTIONCHAMBERELEMENT',
+	1062813311: 'IFCDISTRIBUTIONCONTROLELEMENT',
+	1073191201: 'IFCMEMBER',
+	1095909175: 'IFCBUILDINGELEMENTPROXY',
+	1156407060: 'IFCPLATESTANDARDCASE',
+	1162798199: 'IFCSWITCHINGDEVICE',
+	1329646415: 'IFCSHADINGDEVICE',
+	1335981549: 'IFCDISCRETEACCESSORY',
+	1360408905: 'IFCDUCTSILENCER',
+	1404847402: 'IFCSTACKTERMINAL',
+	1426591983: 'IFCFIRESUPPRESSIONTERMINAL',
+	1437502449: 'IFCMEDICALDEVICE',
+	1509553395: 'IFCFURNITURE',
+	1529196076: 'IFCSLAB',
+	1620046519: 'IFCTRANSPORTELEMENT',
+	1634111441: 'IFCAIRTERMINAL',
+	1658829314: 'IFCENERGYCONVERSIONDEVICE',
+	1677625105: 'IFCCIVILELEMENT',
+	1687234759: 'IFCPILE',
+	1904799276: 'IFCELECTRICAPPLIANCE',
+	1911478936: 'IFCMEMBERSTANDARDCASE',
+	1945004755: 'IFCDISTRIBUTIONELEMENT',
+	1973544240: 'IFCCOVERING',
+	1999602285: 'IFCSPACEHEATER',
+	2016517767: 'IFCROOF',
+	2056796094: 'IFCAIRTOAIRHEATRECOVERY',
+	2058353004: 'IFCFLOWCONTROLLER',
+	2068733104: 'IFCHUMIDIFIER',
+	2176052936: 'IFCJUNCTIONBOX',
+	2188021234: 'IFCFLOWMETER',
+	2223149337: 'IFCFLOWTERMINAL',
+	2262370178: 'IFCRAILING',
+	2272882330: 'IFCCONDENSER',
+	2295281155: 'IFCPROTECTIVEDEVICETRIPPINGUNIT',
+	2320036040: 'IFCREINFORCINGMESH',
+	2347447852: 'IFCTENDONANCHOR',
+	2391383451: 'IFCVIBRATIONISOLATOR',
+	2391406946: 'IFCWALL',
+	2474470126: 'IFCMOTORCONNECTION',
+	2769231204: 'IFCVIRTUALELEMENT',
+	2814081492: 'IFCENGINE',
+	2906023776: 'IFCBEAMSTANDARDCASE',
+	2938176219: 'IFCBURNER',
+	2979338954: 'IFCBUILDINGELEMENTPART',
+	3024970846: 'IFCRAMP',
+	3026737570: 'IFCTUBEBUNDLE',
+	3027962421: 'IFCSLABSTANDARDCASE',
+	3040386961: 'IFCDISTRIBUTIONFLOWELEMENT',
+	3053780830: 'IFCSANITARYTERMINAL',
+	3079942009: 'IFCOPENINGSTANDARDCASE',
+	3087945054: 'IFCALARM',
+	3101698114: 'IFCSURFACEFEATURE',
+	3127900445: 'IFCSLABELEMENTEDCASE',
+	3132237377: 'IFCFLOWMOVINGDEVICE',
+	3171933400: 'IFCPLATE',
+	3221913625: 'IFCCOMMUNICATIONSAPPLIANCE',
+	3242481149: 'IFCDOORSTANDARDCASE',
+	3283111854: 'IFCRAMPFLIGHT',
+	3296154744: 'IFCCHIMNEY',
+	3304561284: 'IFCWINDOW',
+	3310460725: 'IFCELECTRICFLOWSTORAGEDEVICE',
+	3319311131: 'IFCHEATEXCHANGER',
+	3415622556: 'IFCFAN',
+	3420628829: 'IFCSOLARDEVICE',
+	3493046030: 'IFCGEOGRAPHICELEMENT',
+	3495092785: 'IFCCURTAINWALL',
+	3508470533: 'IFCFLOWTREATMENTDEVICE',
+	3512223829: 'IFCWALLSTANDARDCASE',
+	3518393246: 'IFCDUCTSEGMENT',
+	3571504051: 'IFCCOMPRESSOR',
+	3588315303: 'IFCOPENINGELEMENT',
+	3612865200: 'IFCPIPESEGMENT',
+	3640358203: 'IFCCOOLINGTOWER',
+	3651124850: 'IFCPROJECTIONELEMENT',
+	3694346114: 'IFCOUTLET',
+	3747195512: 'IFCEVAPORATIVECOOLER',
+	3758799889: 'IFCCABLECARRIERSEGMENT',
+	3824725483: 'IFCTENDON',
+	3825984169: 'IFCTRANSFORMER',
+	3902619387: 'IFCCHILLER',
+	4074379575: 'IFCDAMPER',
+	4086658281: 'IFCSENSOR',
+	4123344466: 'IFCELEMENTASSEMBLY',
+	4136498852: 'IFCCOOLEDBEAM',
+	4156078855: 'IFCWALLELEMENTEDCASE',
+	4175244083: 'IFCINTERCEPTOR',
+	4207607924: 'IFCVALVE',
+	4217484030: 'IFCCABLESEGMENT',
+	4237592921: 'IFCWASTETERMINAL',
+	4252922144: 'IFCSTAIRFLIGHT',
+	4278956645: 'IFCFLOWFITTING',
+	4288193352: 'IFCACTUATOR',
+	4292641817: 'IFCUNITARYEQUIPMENT',
+	3009204131: 'IFCGRID'
+};
+
+const IfcTypesMap = {
+	3821786052: 'IFCACTIONREQUEST',
+	2296667514: 'IFCACTOR',
+	3630933823: 'IFCACTORROLE',
+	4288193352: 'IFCACTUATOR',
+	2874132201: 'IFCACTUATORTYPE',
+	618182010: 'IFCADDRESS',
+	1635779807: 'IFCADVANCEDBREP',
+	2603310189: 'IFCADVANCEDBREPWITHVOIDS',
+	3406155212: 'IFCADVANCEDFACE',
+	1634111441: 'IFCAIRTERMINAL',
+	177149247: 'IFCAIRTERMINALBOX',
+	1411407467: 'IFCAIRTERMINALBOXTYPE',
+	3352864051: 'IFCAIRTERMINALTYPE',
+	2056796094: 'IFCAIRTOAIRHEATRECOVERY',
+	1871374353: 'IFCAIRTOAIRHEATRECOVERYTYPE',
+	3087945054: 'IFCALARM',
+	3001207471: 'IFCALARMTYPE',
+	325726236: 'IFCALIGNMENT',
+	749761778: 'IFCALIGNMENT2DHORIZONTAL',
+	3199563722: 'IFCALIGNMENT2DHORIZONTALSEGMENT',
+	2483840362: 'IFCALIGNMENT2DSEGMENT',
+	3379348081: 'IFCALIGNMENT2DVERSEGCIRCULARARC',
+	3239324667: 'IFCALIGNMENT2DVERSEGLINE',
+	4263986512: 'IFCALIGNMENT2DVERSEGPARABOLICARC',
+	53199957: 'IFCALIGNMENT2DVERTICAL',
+	2029264950: 'IFCALIGNMENT2DVERTICALSEGMENT',
+	3512275521: 'IFCALIGNMENTCURVE',
+	1674181508: 'IFCANNOTATION',
+	669184980: 'IFCANNOTATIONFILLAREA',
+	639542469: 'IFCAPPLICATION',
+	411424972: 'IFCAPPLIEDVALUE',
+	130549933: 'IFCAPPROVAL',
+	3869604511: 'IFCAPPROVALRELATIONSHIP',
+	3798115385: 'IFCARBITRARYCLOSEDPROFILEDEF',
+	1310608509: 'IFCARBITRARYOPENPROFILEDEF',
+	2705031697: 'IFCARBITRARYPROFILEDEFWITHVOIDS',
+	3460190687: 'IFCASSET',
+	3207858831: 'IFCASYMMETRICISHAPEPROFILEDEF',
+	277319702: 'IFCAUDIOVISUALAPPLIANCE',
+	1532957894: 'IFCAUDIOVISUALAPPLIANCETYPE',
+	4261334040: 'IFCAXIS1PLACEMENT',
+	3125803723: 'IFCAXIS2PLACEMENT2D',
+	2740243338: 'IFCAXIS2PLACEMENT3D',
+	1967976161: 'IFCBSPLINECURVE',
+	2461110595: 'IFCBSPLINECURVEWITHKNOTS',
+	2887950389: 'IFCBSPLINESURFACE',
+	167062518: 'IFCBSPLINESURFACEWITHKNOTS',
+	753842376: 'IFCBEAM',
+	2906023776: 'IFCBEAMSTANDARDCASE',
+	819618141: 'IFCBEAMTYPE',
+	4196446775: 'IFCBEARING',
+	3649138523: 'IFCBEARINGTYPE',
+	616511568: 'IFCBLOBTEXTURE',
+	1334484129: 'IFCBLOCK',
+	32344328: 'IFCBOILER',
+	231477066: 'IFCBOILERTYPE',
+	3649129432: 'IFCBOOLEANCLIPPINGRESULT',
+	2736907675: 'IFCBOOLEANRESULT',
+	4037036970: 'IFCBOUNDARYCONDITION',
+	1136057603: 'IFCBOUNDARYCURVE',
+	1560379544: 'IFCBOUNDARYEDGECONDITION',
+	3367102660: 'IFCBOUNDARYFACECONDITION',
+	1387855156: 'IFCBOUNDARYNODECONDITION',
+	2069777674: 'IFCBOUNDARYNODECONDITIONWARPING',
+	1260505505: 'IFCBOUNDEDCURVE',
+	4182860854: 'IFCBOUNDEDSURFACE',
+	2581212453: 'IFCBOUNDINGBOX',
+	2713105998: 'IFCBOXEDHALFSPACE',
+	644574406: 'IFCBRIDGE',
+	963979645: 'IFCBRIDGEPART',
+	4031249490: 'IFCBUILDING',
+	3299480353: 'IFCBUILDINGELEMENT',
+	2979338954: 'IFCBUILDINGELEMENTPART',
+	39481116: 'IFCBUILDINGELEMENTPARTTYPE',
+	1095909175: 'IFCBUILDINGELEMENTPROXY',
+	1909888760: 'IFCBUILDINGELEMENTPROXYTYPE',
+	1950629157: 'IFCBUILDINGELEMENTTYPE',
+	3124254112: 'IFCBUILDINGSTOREY',
+	1177604601: 'IFCBUILDINGSYSTEM',
+	2938176219: 'IFCBURNER',
+	2188180465: 'IFCBURNERTYPE',
+	2898889636: 'IFCCSHAPEPROFILEDEF',
+	635142910: 'IFCCABLECARRIERFITTING',
+	395041908: 'IFCCABLECARRIERFITTINGTYPE',
+	3758799889: 'IFCCABLECARRIERSEGMENT',
+	3293546465: 'IFCCABLECARRIERSEGMENTTYPE',
+	1051757585: 'IFCCABLEFITTING',
+	2674252688: 'IFCCABLEFITTINGTYPE',
+	4217484030: 'IFCCABLESEGMENT',
+	1285652485: 'IFCCABLESEGMENTTYPE',
+	3999819293: 'IFCCAISSONFOUNDATION',
+	3203706013: 'IFCCAISSONFOUNDATIONTYPE',
+	1123145078: 'IFCCARTESIANPOINT',
+	574549367: 'IFCCARTESIANPOINTLIST',
+	1675464909: 'IFCCARTESIANPOINTLIST2D',
+	2059837836: 'IFCCARTESIANPOINTLIST3D',
+	59481748: 'IFCCARTESIANTRANSFORMATIONOPERATOR',
+	3749851601: 'IFCCARTESIANTRANSFORMATIONOPERATOR2D',
+	3486308946: 'IFCCARTESIANTRANSFORMATIONOPERATOR2DNONUNIFORM',
+	3331915920: 'IFCCARTESIANTRANSFORMATIONOPERATOR3D',
+	1416205885: 'IFCCARTESIANTRANSFORMATIONOPERATOR3DNONUNIFORM',
+	3150382593: 'IFCCENTERLINEPROFILEDEF',
+	3902619387: 'IFCCHILLER',
+	2951183804: 'IFCCHILLERTYPE',
+	3296154744: 'IFCCHIMNEY',
+	2197970202: 'IFCCHIMNEYTYPE',
+	2611217952: 'IFCCIRCLE',
+	2937912522: 'IFCCIRCLEHOLLOWPROFILEDEF',
+	1383045692: 'IFCCIRCLEPROFILEDEF',
+	1062206242: 'IFCCIRCULARARCSEGMENT2D',
+	1677625105: 'IFCCIVILELEMENT',
+	3893394355: 'IFCCIVILELEMENTTYPE',
+	747523909: 'IFCCLASSIFICATION',
+	647927063: 'IFCCLASSIFICATIONREFERENCE',
+	2205249479: 'IFCCLOSEDSHELL',
+	639361253: 'IFCCOIL',
+	2301859152: 'IFCCOILTYPE',
+	776857604: 'IFCCOLOURRGB',
+	3285139300: 'IFCCOLOURRGBLIST',
+	3264961684: 'IFCCOLOURSPECIFICATION',
+	843113511: 'IFCCOLUMN',
+	905975707: 'IFCCOLUMNSTANDARDCASE',
+	300633059: 'IFCCOLUMNTYPE',
+	3221913625: 'IFCCOMMUNICATIONSAPPLIANCE',
+	400855858: 'IFCCOMMUNICATIONSAPPLIANCETYPE',
+	2542286263: 'IFCCOMPLEXPROPERTY',
+	3875453745: 'IFCCOMPLEXPROPERTYTEMPLATE',
+	3732776249: 'IFCCOMPOSITECURVE',
+	15328376: 'IFCCOMPOSITECURVEONSURFACE',
+	2485617015: 'IFCCOMPOSITECURVESEGMENT',
+	1485152156: 'IFCCOMPOSITEPROFILEDEF',
+	3571504051: 'IFCCOMPRESSOR',
+	3850581409: 'IFCCOMPRESSORTYPE',
+	2272882330: 'IFCCONDENSER',
+	2816379211: 'IFCCONDENSERTYPE',
+	2510884976: 'IFCCONIC',
+	370225590: 'IFCCONNECTEDFACESET',
+	1981873012: 'IFCCONNECTIONCURVEGEOMETRY',
+	2859738748: 'IFCCONNECTIONGEOMETRY',
+	45288368: 'IFCCONNECTIONPOINTECCENTRICITY',
+	2614616156: 'IFCCONNECTIONPOINTGEOMETRY',
+	2732653382: 'IFCCONNECTIONSURFACEGEOMETRY',
+	775493141: 'IFCCONNECTIONVOLUMEGEOMETRY',
+	1959218052: 'IFCCONSTRAINT',
+	3898045240: 'IFCCONSTRUCTIONEQUIPMENTRESOURCE',
+	2185764099: 'IFCCONSTRUCTIONEQUIPMENTRESOURCETYPE',
+	1060000209: 'IFCCONSTRUCTIONMATERIALRESOURCE',
+	4105962743: 'IFCCONSTRUCTIONMATERIALRESOURCETYPE',
+	488727124: 'IFCCONSTRUCTIONPRODUCTRESOURCE',
+	1525564444: 'IFCCONSTRUCTIONPRODUCTRESOURCETYPE',
+	2559216714: 'IFCCONSTRUCTIONRESOURCE',
+	2574617495: 'IFCCONSTRUCTIONRESOURCETYPE',
+	3419103109: 'IFCCONTEXT',
+	3050246964: 'IFCCONTEXTDEPENDENTUNIT',
+	3293443760: 'IFCCONTROL',
+	25142252: 'IFCCONTROLLER',
+	578613899: 'IFCCONTROLLERTYPE',
+	2889183280: 'IFCCONVERSIONBASEDUNIT',
+	2713554722: 'IFCCONVERSIONBASEDUNITWITHOFFSET',
+	4136498852: 'IFCCOOLEDBEAM',
+	335055490: 'IFCCOOLEDBEAMTYPE',
+	3640358203: 'IFCCOOLINGTOWER',
+	2954562838: 'IFCCOOLINGTOWERTYPE',
+	1785450214: 'IFCCOORDINATEOPERATION',
+	1466758467: 'IFCCOORDINATEREFERENCESYSTEM',
+	3895139033: 'IFCCOSTITEM',
+	1419761937: 'IFCCOSTSCHEDULE',
+	602808272: 'IFCCOSTVALUE',
+	1973544240: 'IFCCOVERING',
+	1916426348: 'IFCCOVERINGTYPE',
+	3295246426: 'IFCCREWRESOURCE',
+	1815067380: 'IFCCREWRESOURCETYPE',
+	2506170314: 'IFCCSGPRIMITIVE3D',
+	2147822146: 'IFCCSGSOLID',
+	539742890: 'IFCCURRENCYRELATIONSHIP',
+	3495092785: 'IFCCURTAINWALL',
+	1457835157: 'IFCCURTAINWALLTYPE',
+	2601014836: 'IFCCURVE',
+	2827736869: 'IFCCURVEBOUNDEDPLANE',
+	2629017746: 'IFCCURVEBOUNDEDSURFACE',
+	1186437898: 'IFCCURVESEGMENT2D',
+	3800577675: 'IFCCURVESTYLE',
+	1105321065: 'IFCCURVESTYLEFONT',
+	2367409068: 'IFCCURVESTYLEFONTANDSCALING',
+	3510044353: 'IFCCURVESTYLEFONTPATTERN',
+	1213902940: 'IFCCYLINDRICALSURFACE',
+	4074379575: 'IFCDAMPER',
+	3961806047: 'IFCDAMPERTYPE',
+	3426335179: 'IFCDEEPFOUNDATION',
+	1306400036: 'IFCDEEPFOUNDATIONTYPE',
+	3632507154: 'IFCDERIVEDPROFILEDEF',
+	1765591967: 'IFCDERIVEDUNIT',
+	1045800335: 'IFCDERIVEDUNITELEMENT',
+	2949456006: 'IFCDIMENSIONALEXPONENTS',
+	32440307: 'IFCDIRECTION',
+	1335981549: 'IFCDISCRETEACCESSORY',
+	2635815018: 'IFCDISCRETEACCESSORYTYPE',
+	1945343521: 'IFCDISTANCEEXPRESSION',
+	1052013943: 'IFCDISTRIBUTIONCHAMBERELEMENT',
+	1599208980: 'IFCDISTRIBUTIONCHAMBERELEMENTTYPE',
+	562808652: 'IFCDISTRIBUTIONCIRCUIT',
+	1062813311: 'IFCDISTRIBUTIONCONTROLELEMENT',
+	2063403501: 'IFCDISTRIBUTIONCONTROLELEMENTTYPE',
+	1945004755: 'IFCDISTRIBUTIONELEMENT',
+	3256556792: 'IFCDISTRIBUTIONELEMENTTYPE',
+	3040386961: 'IFCDISTRIBUTIONFLOWELEMENT',
+	3849074793: 'IFCDISTRIBUTIONFLOWELEMENTTYPE',
+	3041715199: 'IFCDISTRIBUTIONPORT',
+	3205830791: 'IFCDISTRIBUTIONSYSTEM',
+	1154170062: 'IFCDOCUMENTINFORMATION',
+	770865208: 'IFCDOCUMENTINFORMATIONRELATIONSHIP',
+	3732053477: 'IFCDOCUMENTREFERENCE',
+	395920057: 'IFCDOOR',
+	2963535650: 'IFCDOORLININGPROPERTIES',
+	1714330368: 'IFCDOORPANELPROPERTIES',
+	3242481149: 'IFCDOORSTANDARDCASE',
+	526551008: 'IFCDOORSTYLE',
+	2323601079: 'IFCDOORTYPE',
+	445594917: 'IFCDRAUGHTINGPREDEFINEDCOLOUR',
+	4006246654: 'IFCDRAUGHTINGPREDEFINEDCURVEFONT',
+	342316401: 'IFCDUCTFITTING',
+	869906466: 'IFCDUCTFITTINGTYPE',
+	3518393246: 'IFCDUCTSEGMENT',
+	3760055223: 'IFCDUCTSEGMENTTYPE',
+	1360408905: 'IFCDUCTSILENCER',
+	2030761528: 'IFCDUCTSILENCERTYPE',
+	3900360178: 'IFCEDGE',
+	476780140: 'IFCEDGECURVE',
+	1472233963: 'IFCEDGELOOP',
+	1904799276: 'IFCELECTRICAPPLIANCE',
+	663422040: 'IFCELECTRICAPPLIANCETYPE',
+	862014818: 'IFCELECTRICDISTRIBUTIONBOARD',
+	2417008758: 'IFCELECTRICDISTRIBUTIONBOARDTYPE',
+	3310460725: 'IFCELECTRICFLOWSTORAGEDEVICE',
+	3277789161: 'IFCELECTRICFLOWSTORAGEDEVICETYPE',
+	264262732: 'IFCELECTRICGENERATOR',
+	1534661035: 'IFCELECTRICGENERATORTYPE',
+	402227799: 'IFCELECTRICMOTOR',
+	1217240411: 'IFCELECTRICMOTORTYPE',
+	1003880860: 'IFCELECTRICTIMECONTROL',
+	712377611: 'IFCELECTRICTIMECONTROLTYPE',
+	1758889154: 'IFCELEMENT',
+	4123344466: 'IFCELEMENTASSEMBLY',
+	2397081782: 'IFCELEMENTASSEMBLYTYPE',
+	1623761950: 'IFCELEMENTCOMPONENT',
+	2590856083: 'IFCELEMENTCOMPONENTTYPE',
+	1883228015: 'IFCELEMENTQUANTITY',
+	339256511: 'IFCELEMENTTYPE',
+	2777663545: 'IFCELEMENTARYSURFACE',
+	1704287377: 'IFCELLIPSE',
+	2835456948: 'IFCELLIPSEPROFILEDEF',
+	1658829314: 'IFCENERGYCONVERSIONDEVICE',
+	2107101300: 'IFCENERGYCONVERSIONDEVICETYPE',
+	2814081492: 'IFCENGINE',
+	132023988: 'IFCENGINETYPE',
+	3747195512: 'IFCEVAPORATIVECOOLER',
+	3174744832: 'IFCEVAPORATIVECOOLERTYPE',
+	484807127: 'IFCEVAPORATOR',
+	3390157468: 'IFCEVAPORATORTYPE',
+	4148101412: 'IFCEVENT',
+	211053100: 'IFCEVENTTIME',
+	4024345920: 'IFCEVENTTYPE',
+	297599258: 'IFCEXTENDEDPROPERTIES',
+	4294318154: 'IFCEXTERNALINFORMATION',
+	3200245327: 'IFCEXTERNALREFERENCE',
+	1437805879: 'IFCEXTERNALREFERENCERELATIONSHIP',
+	1209101575: 'IFCEXTERNALSPATIALELEMENT',
+	2853485674: 'IFCEXTERNALSPATIALSTRUCTUREELEMENT',
+	2242383968: 'IFCEXTERNALLYDEFINEDHATCHSTYLE',
+	1040185647: 'IFCEXTERNALLYDEFINEDSURFACESTYLE',
+	3548104201: 'IFCEXTERNALLYDEFINEDTEXTFONT',
+	477187591: 'IFCEXTRUDEDAREASOLID',
+	2804161546: 'IFCEXTRUDEDAREASOLIDTAPERED',
+	2556980723: 'IFCFACE',
+	2047409740: 'IFCFACEBASEDSURFACEMODEL',
+	1809719519: 'IFCFACEBOUND',
+	803316827: 'IFCFACEOUTERBOUND',
+	3008276851: 'IFCFACESURFACE',
+	807026263: 'IFCFACETEDBREP',
+	3737207727: 'IFCFACETEDBREPWITHVOIDS',
+	24185140: 'IFCFACILITY',
+	1310830890: 'IFCFACILITYPART',
+	4219587988: 'IFCFAILURECONNECTIONCONDITION',
+	3415622556: 'IFCFAN',
+	346874300: 'IFCFANTYPE',
+	647756555: 'IFCFASTENER',
+	2489546625: 'IFCFASTENERTYPE',
+	2827207264: 'IFCFEATUREELEMENT',
+	2143335405: 'IFCFEATUREELEMENTADDITION',
+	1287392070: 'IFCFEATUREELEMENTSUBTRACTION',
+	738692330: 'IFCFILLAREASTYLE',
+	374418227: 'IFCFILLAREASTYLEHATCHING',
+	315944413: 'IFCFILLAREASTYLETILES',
+	819412036: 'IFCFILTER',
+	1810631287: 'IFCFILTERTYPE',
+	1426591983: 'IFCFIRESUPPRESSIONTERMINAL',
+	4222183408: 'IFCFIRESUPPRESSIONTERMINALTYPE',
+	2652556860: 'IFCFIXEDREFERENCESWEPTAREASOLID',
+	2058353004: 'IFCFLOWCONTROLLER',
+	3907093117: 'IFCFLOWCONTROLLERTYPE',
+	4278956645: 'IFCFLOWFITTING',
+	3198132628: 'IFCFLOWFITTINGTYPE',
+	182646315: 'IFCFLOWINSTRUMENT',
+	4037862832: 'IFCFLOWINSTRUMENTTYPE',
+	2188021234: 'IFCFLOWMETER',
+	3815607619: 'IFCFLOWMETERTYPE',
+	3132237377: 'IFCFLOWMOVINGDEVICE',
+	1482959167: 'IFCFLOWMOVINGDEVICETYPE',
+	987401354: 'IFCFLOWSEGMENT',
+	1834744321: 'IFCFLOWSEGMENTTYPE',
+	707683696: 'IFCFLOWSTORAGEDEVICE',
+	1339347760: 'IFCFLOWSTORAGEDEVICETYPE',
+	2223149337: 'IFCFLOWTERMINAL',
+	2297155007: 'IFCFLOWTERMINALTYPE',
+	3508470533: 'IFCFLOWTREATMENTDEVICE',
+	3009222698: 'IFCFLOWTREATMENTDEVICETYPE',
+	900683007: 'IFCFOOTING',
+	1893162501: 'IFCFOOTINGTYPE',
+	263784265: 'IFCFURNISHINGELEMENT',
+	4238390223: 'IFCFURNISHINGELEMENTTYPE',
+	1509553395: 'IFCFURNITURE',
+	1268542332: 'IFCFURNITURETYPE',
+	3493046030: 'IFCGEOGRAPHICELEMENT',
+	4095422895: 'IFCGEOGRAPHICELEMENTTYPE',
+	987898635: 'IFCGEOMETRICCURVESET',
+	3448662350: 'IFCGEOMETRICREPRESENTATIONCONTEXT',
+	2453401579: 'IFCGEOMETRICREPRESENTATIONITEM',
+	4142052618: 'IFCGEOMETRICREPRESENTATIONSUBCONTEXT',
+	3590301190: 'IFCGEOMETRICSET',
+	3009204131: 'IFCGRID',
+	852622518: 'IFCGRIDAXIS',
+	178086475: 'IFCGRIDPLACEMENT',
+	2706460486: 'IFCGROUP',
+	812098782: 'IFCHALFSPACESOLID',
+	3319311131: 'IFCHEATEXCHANGER',
+	1251058090: 'IFCHEATEXCHANGERTYPE',
+	2068733104: 'IFCHUMIDIFIER',
+	1806887404: 'IFCHUMIDIFIERTYPE',
+	1484403080: 'IFCISHAPEPROFILEDEF',
+	3905492369: 'IFCIMAGETEXTURE',
+	3570813810: 'IFCINDEXEDCOLOURMAP',
+	2571569899: 'IFCINDEXEDPOLYCURVE',
+	178912537: 'IFCINDEXEDPOLYGONALFACE',
+	2294589976: 'IFCINDEXEDPOLYGONALFACEWITHVOIDS',
+	1437953363: 'IFCINDEXEDTEXTUREMAP',
+	2133299955: 'IFCINDEXEDTRIANGLETEXTUREMAP',
+	4175244083: 'IFCINTERCEPTOR',
+	3946677679: 'IFCINTERCEPTORTYPE',
+	3113134337: 'IFCINTERSECTIONCURVE',
+	2391368822: 'IFCINVENTORY',
+	3741457305: 'IFCIRREGULARTIMESERIES',
+	3020489413: 'IFCIRREGULARTIMESERIESVALUE',
+	2176052936: 'IFCJUNCTIONBOX',
+	4288270099: 'IFCJUNCTIONBOXTYPE',
+	572779678: 'IFCLSHAPEPROFILEDEF',
+	3827777499: 'IFCLABORRESOURCE',
+	428585644: 'IFCLABORRESOURCETYPE',
+	1585845231: 'IFCLAGTIME',
+	76236018: 'IFCLAMP',
+	1051575348: 'IFCLAMPTYPE',
+	2655187982: 'IFCLIBRARYINFORMATION',
+	3452421091: 'IFCLIBRARYREFERENCE',
+	4162380809: 'IFCLIGHTDISTRIBUTIONDATA',
+	629592764: 'IFCLIGHTFIXTURE',
+	1161773419: 'IFCLIGHTFIXTURETYPE',
+	1566485204: 'IFCLIGHTINTENSITYDISTRIBUTION',
+	1402838566: 'IFCLIGHTSOURCE',
+	125510826: 'IFCLIGHTSOURCEAMBIENT',
+	2604431987: 'IFCLIGHTSOURCEDIRECTIONAL',
+	4266656042: 'IFCLIGHTSOURCEGONIOMETRIC',
+	1520743889: 'IFCLIGHTSOURCEPOSITIONAL',
+	3422422726: 'IFCLIGHTSOURCESPOT',
+	1281925730: 'IFCLINE',
+	3092502836: 'IFCLINESEGMENT2D',
+	388784114: 'IFCLINEARPLACEMENT',
+	1154579445: 'IFCLINEARPOSITIONINGELEMENT',
+	2624227202: 'IFCLOCALPLACEMENT',
+	1008929658: 'IFCLOOP',
+	1425443689: 'IFCMANIFOLDSOLIDBREP',
+	3057273783: 'IFCMAPCONVERSION',
+	2347385850: 'IFCMAPPEDITEM',
+	1838606355: 'IFCMATERIAL',
+	1847130766: 'IFCMATERIALCLASSIFICATIONRELATIONSHIP',
+	3708119000: 'IFCMATERIALCONSTITUENT',
+	2852063980: 'IFCMATERIALCONSTITUENTSET',
+	760658860: 'IFCMATERIALDEFINITION',
+	2022407955: 'IFCMATERIALDEFINITIONREPRESENTATION',
+	248100487: 'IFCMATERIALLAYER',
+	3303938423: 'IFCMATERIALLAYERSET',
+	1303795690: 'IFCMATERIALLAYERSETUSAGE',
+	1847252529: 'IFCMATERIALLAYERWITHOFFSETS',
+	2199411900: 'IFCMATERIALLIST',
+	2235152071: 'IFCMATERIALPROFILE',
+	164193824: 'IFCMATERIALPROFILESET',
+	3079605661: 'IFCMATERIALPROFILESETUSAGE',
+	3404854881: 'IFCMATERIALPROFILESETUSAGETAPERING',
+	552965576: 'IFCMATERIALPROFILEWITHOFFSETS',
+	3265635763: 'IFCMATERIALPROPERTIES',
+	853536259: 'IFCMATERIALRELATIONSHIP',
+	1507914824: 'IFCMATERIALUSAGEDEFINITION',
+	2597039031: 'IFCMEASUREWITHUNIT',
+	377706215: 'IFCMECHANICALFASTENER',
+	2108223431: 'IFCMECHANICALFASTENERTYPE',
+	1437502449: 'IFCMEDICALDEVICE',
+	1114901282: 'IFCMEDICALDEVICETYPE',
+	1073191201: 'IFCMEMBER',
+	1911478936: 'IFCMEMBERSTANDARDCASE',
+	3181161470: 'IFCMEMBERTYPE',
+	3368373690: 'IFCMETRIC',
+	2998442950: 'IFCMIRROREDPROFILEDEF',
+	2706619895: 'IFCMONETARYUNIT',
+	2474470126: 'IFCMOTORCONNECTION',
+	977012517: 'IFCMOTORCONNECTIONTYPE',
+	1918398963: 'IFCNAMEDUNIT',
+	3888040117: 'IFCOBJECT',
+	219451334: 'IFCOBJECTDEFINITION',
+	3701648758: 'IFCOBJECTPLACEMENT',
+	2251480897: 'IFCOBJECTIVE',
+	4143007308: 'IFCOCCUPANT',
+	590820931: 'IFCOFFSETCURVE',
+	3388369263: 'IFCOFFSETCURVE2D',
+	3505215534: 'IFCOFFSETCURVE3D',
+	2485787929: 'IFCOFFSETCURVEBYDISTANCES',
+	2665983363: 'IFCOPENSHELL',
+	3588315303: 'IFCOPENINGELEMENT',
+	3079942009: 'IFCOPENINGSTANDARDCASE',
+	4251960020: 'IFCORGANIZATION',
+	1411181986: 'IFCORGANIZATIONRELATIONSHIP',
+	643959842: 'IFCORIENTATIONEXPRESSION',
+	1029017970: 'IFCORIENTEDEDGE',
+	144952367: 'IFCOUTERBOUNDARYCURVE',
+	3694346114: 'IFCOUTLET',
+	2837617999: 'IFCOUTLETTYPE',
+	1207048766: 'IFCOWNERHISTORY',
+	2529465313: 'IFCPARAMETERIZEDPROFILEDEF',
+	2519244187: 'IFCPATH',
+	1682466193: 'IFCPCURVE',
+	2382730787: 'IFCPERFORMANCEHISTORY',
+	3566463478: 'IFCPERMEABLECOVERINGPROPERTIES',
+	3327091369: 'IFCPERMIT',
+	2077209135: 'IFCPERSON',
+	101040310: 'IFCPERSONANDORGANIZATION',
+	3021840470: 'IFCPHYSICALCOMPLEXQUANTITY',
+	2483315170: 'IFCPHYSICALQUANTITY',
+	2226359599: 'IFCPHYSICALSIMPLEQUANTITY',
+	1687234759: 'IFCPILE',
+	1158309216: 'IFCPILETYPE',
+	310824031: 'IFCPIPEFITTING',
+	804291784: 'IFCPIPEFITTINGTYPE',
+	3612865200: 'IFCPIPESEGMENT',
+	4231323485: 'IFCPIPESEGMENTTYPE',
+	597895409: 'IFCPIXELTEXTURE',
+	2004835150: 'IFCPLACEMENT',
+	603570806: 'IFCPLANARBOX',
+	1663979128: 'IFCPLANAREXTENT',
+	220341763: 'IFCPLANE',
+	3171933400: 'IFCPLATE',
+	1156407060: 'IFCPLATESTANDARDCASE',
+	4017108033: 'IFCPLATETYPE',
+	2067069095: 'IFCPOINT',
+	4022376103: 'IFCPOINTONCURVE',
+	1423911732: 'IFCPOINTONSURFACE',
+	2924175390: 'IFCPOLYLOOP',
+	2775532180: 'IFCPOLYGONALBOUNDEDHALFSPACE',
+	2839578677: 'IFCPOLYGONALFACESET',
+	3724593414: 'IFCPOLYLINE',
+	3740093272: 'IFCPORT',
+	1946335990: 'IFCPOSITIONINGELEMENT',
+	3355820592: 'IFCPOSTALADDRESS',
+	759155922: 'IFCPREDEFINEDCOLOUR',
+	2559016684: 'IFCPREDEFINEDCURVEFONT',
+	3727388367: 'IFCPREDEFINEDITEM',
+	3778827333: 'IFCPREDEFINEDPROPERTIES',
+	3967405729: 'IFCPREDEFINEDPROPERTYSET',
+	1775413392: 'IFCPREDEFINEDTEXTFONT',
+	677532197: 'IFCPRESENTATIONITEM',
+	2022622350: 'IFCPRESENTATIONLAYERASSIGNMENT',
+	1304840413: 'IFCPRESENTATIONLAYERWITHSTYLE',
+	3119450353: 'IFCPRESENTATIONSTYLE',
+	2417041796: 'IFCPRESENTATIONSTYLEASSIGNMENT',
+	2744685151: 'IFCPROCEDURE',
+	569719735: 'IFCPROCEDURETYPE',
+	2945172077: 'IFCPROCESS',
+	4208778838: 'IFCPRODUCT',
+	673634403: 'IFCPRODUCTDEFINITIONSHAPE',
+	2095639259: 'IFCPRODUCTREPRESENTATION',
+	3958567839: 'IFCPROFILEDEF',
+	2802850158: 'IFCPROFILEPROPERTIES',
+	103090709: 'IFCPROJECT',
+	653396225: 'IFCPROJECTLIBRARY',
+	2904328755: 'IFCPROJECTORDER',
+	3843373140: 'IFCPROJECTEDCRS',
+	3651124850: 'IFCPROJECTIONELEMENT',
+	2598011224: 'IFCPROPERTY',
+	986844984: 'IFCPROPERTYABSTRACTION',
+	871118103: 'IFCPROPERTYBOUNDEDVALUE',
+	1680319473: 'IFCPROPERTYDEFINITION',
+	148025276: 'IFCPROPERTYDEPENDENCYRELATIONSHIP',
+	4166981789: 'IFCPROPERTYENUMERATEDVALUE',
+	3710013099: 'IFCPROPERTYENUMERATION',
+	2752243245: 'IFCPROPERTYLISTVALUE',
+	941946838: 'IFCPROPERTYREFERENCEVALUE',
+	1451395588: 'IFCPROPERTYSET',
+	3357820518: 'IFCPROPERTYSETDEFINITION',
+	492091185: 'IFCPROPERTYSETTEMPLATE',
+	3650150729: 'IFCPROPERTYSINGLEVALUE',
+	110355661: 'IFCPROPERTYTABLEVALUE',
+	3521284610: 'IFCPROPERTYTEMPLATE',
+	1482703590: 'IFCPROPERTYTEMPLATEDEFINITION',
+	738039164: 'IFCPROTECTIVEDEVICE',
+	2295281155: 'IFCPROTECTIVEDEVICETRIPPINGUNIT',
+	655969474: 'IFCPROTECTIVEDEVICETRIPPINGUNITTYPE',
+	1842657554: 'IFCPROTECTIVEDEVICETYPE',
+	3219374653: 'IFCPROXY',
+	90941305: 'IFCPUMP',
+	2250791053: 'IFCPUMPTYPE',
+	2044713172: 'IFCQUANTITYAREA',
+	2093928680: 'IFCQUANTITYCOUNT',
+	931644368: 'IFCQUANTITYLENGTH',
+	2090586900: 'IFCQUANTITYSET',
+	3252649465: 'IFCQUANTITYTIME',
+	2405470396: 'IFCQUANTITYVOLUME',
+	825690147: 'IFCQUANTITYWEIGHT',
+	2262370178: 'IFCRAILING',
+	2893384427: 'IFCRAILINGTYPE',
+	3024970846: 'IFCRAMP',
+	3283111854: 'IFCRAMPFLIGHT',
+	2324767716: 'IFCRAMPFLIGHTTYPE',
+	1469900589: 'IFCRAMPTYPE',
+	1232101972: 'IFCRATIONALBSPLINECURVEWITHKNOTS',
+	683857671: 'IFCRATIONALBSPLINESURFACEWITHKNOTS',
+	2770003689: 'IFCRECTANGLEHOLLOWPROFILEDEF',
+	3615266464: 'IFCRECTANGLEPROFILEDEF',
+	2798486643: 'IFCRECTANGULARPYRAMID',
+	3454111270: 'IFCRECTANGULARTRIMMEDSURFACE',
+	3915482550: 'IFCRECURRENCEPATTERN',
+	2433181523: 'IFCREFERENCE',
+	4021432810: 'IFCREFERENT',
+	3413951693: 'IFCREGULARTIMESERIES',
+	1580146022: 'IFCREINFORCEMENTBARPROPERTIES',
+	3765753017: 'IFCREINFORCEMENTDEFINITIONPROPERTIES',
+	979691226: 'IFCREINFORCINGBAR',
+	2572171363: 'IFCREINFORCINGBARTYPE',
+	3027567501: 'IFCREINFORCINGELEMENT',
+	964333572: 'IFCREINFORCINGELEMENTTYPE',
+	2320036040: 'IFCREINFORCINGMESH',
+	2310774935: 'IFCREINFORCINGMESHTYPE',
+	160246688: 'IFCRELAGGREGATES',
+	3939117080: 'IFCRELASSIGNS',
+	1683148259: 'IFCRELASSIGNSTOACTOR',
+	2495723537: 'IFCRELASSIGNSTOCONTROL',
+	1307041759: 'IFCRELASSIGNSTOGROUP',
+	1027710054: 'IFCRELASSIGNSTOGROUPBYFACTOR',
+	4278684876: 'IFCRELASSIGNSTOPROCESS',
+	2857406711: 'IFCRELASSIGNSTOPRODUCT',
+	205026976: 'IFCRELASSIGNSTORESOURCE',
+	1865459582: 'IFCRELASSOCIATES',
+	4095574036: 'IFCRELASSOCIATESAPPROVAL',
+	919958153: 'IFCRELASSOCIATESCLASSIFICATION',
+	2728634034: 'IFCRELASSOCIATESCONSTRAINT',
+	982818633: 'IFCRELASSOCIATESDOCUMENT',
+	3840914261: 'IFCRELASSOCIATESLIBRARY',
+	2655215786: 'IFCRELASSOCIATESMATERIAL',
+	826625072: 'IFCRELCONNECTS',
+	1204542856: 'IFCRELCONNECTSELEMENTS',
+	3945020480: 'IFCRELCONNECTSPATHELEMENTS',
+	4201705270: 'IFCRELCONNECTSPORTTOELEMENT',
+	3190031847: 'IFCRELCONNECTSPORTS',
+	2127690289: 'IFCRELCONNECTSSTRUCTURALACTIVITY',
+	1638771189: 'IFCRELCONNECTSSTRUCTURALMEMBER',
+	504942748: 'IFCRELCONNECTSWITHECCENTRICITY',
+	3678494232: 'IFCRELCONNECTSWITHREALIZINGELEMENTS',
+	3242617779: 'IFCRELCONTAINEDINSPATIALSTRUCTURE',
+	886880790: 'IFCRELCOVERSBLDGELEMENTS',
+	2802773753: 'IFCRELCOVERSSPACES',
+	2565941209: 'IFCRELDECLARES',
+	2551354335: 'IFCRELDECOMPOSES',
+	693640335: 'IFCRELDEFINES',
+	1462361463: 'IFCRELDEFINESBYOBJECT',
+	4186316022: 'IFCRELDEFINESBYPROPERTIES',
+	307848117: 'IFCRELDEFINESBYTEMPLATE',
+	781010003: 'IFCRELDEFINESBYTYPE',
+	3940055652: 'IFCRELFILLSELEMENT',
+	279856033: 'IFCRELFLOWCONTROLELEMENTS',
+	427948657: 'IFCRELINTERFERESELEMENTS',
+	3268803585: 'IFCRELNESTS',
+	1441486842: 'IFCRELPOSITIONS',
+	750771296: 'IFCRELPROJECTSELEMENT',
+	1245217292: 'IFCRELREFERENCEDINSPATIALSTRUCTURE',
+	4122056220: 'IFCRELSEQUENCE',
+	366585022: 'IFCRELSERVICESBUILDINGS',
+	3451746338: 'IFCRELSPACEBOUNDARY',
+	3523091289: 'IFCRELSPACEBOUNDARY1STLEVEL',
+	1521410863: 'IFCRELSPACEBOUNDARY2NDLEVEL',
+	1401173127: 'IFCRELVOIDSELEMENT',
+	478536968: 'IFCRELATIONSHIP',
+	816062949: 'IFCREPARAMETRISEDCOMPOSITECURVESEGMENT',
+	1076942058: 'IFCREPRESENTATION',
+	3377609919: 'IFCREPRESENTATIONCONTEXT',
+	3008791417: 'IFCREPRESENTATIONITEM',
+	1660063152: 'IFCREPRESENTATIONMAP',
+	2914609552: 'IFCRESOURCE',
+	2943643501: 'IFCRESOURCEAPPROVALRELATIONSHIP',
+	1608871552: 'IFCRESOURCECONSTRAINTRELATIONSHIP',
+	2439245199: 'IFCRESOURCELEVELRELATIONSHIP',
+	1042787934: 'IFCRESOURCETIME',
+	1856042241: 'IFCREVOLVEDAREASOLID',
+	3243963512: 'IFCREVOLVEDAREASOLIDTAPERED',
+	4158566097: 'IFCRIGHTCIRCULARCONE',
+	3626867408: 'IFCRIGHTCIRCULARCYLINDER',
+	2016517767: 'IFCROOF',
+	2781568857: 'IFCROOFTYPE',
+	2341007311: 'IFCROOT',
+	2778083089: 'IFCROUNDEDRECTANGLEPROFILEDEF',
+	448429030: 'IFCSIUNIT',
+	3053780830: 'IFCSANITARYTERMINAL',
+	1768891740: 'IFCSANITARYTERMINALTYPE',
+	1054537805: 'IFCSCHEDULINGTIME',
+	2157484638: 'IFCSEAMCURVE',
+	2042790032: 'IFCSECTIONPROPERTIES',
+	4165799628: 'IFCSECTIONREINFORCEMENTPROPERTIES',
+	1862484736: 'IFCSECTIONEDSOLID',
+	1290935644: 'IFCSECTIONEDSOLIDHORIZONTAL',
+	1509187699: 'IFCSECTIONEDSPINE',
+	4086658281: 'IFCSENSOR',
+	1783015770: 'IFCSENSORTYPE',
+	1329646415: 'IFCSHADINGDEVICE',
+	4074543187: 'IFCSHADINGDEVICETYPE',
+	867548509: 'IFCSHAPEASPECT',
+	3982875396: 'IFCSHAPEMODEL',
+	4240577450: 'IFCSHAPEREPRESENTATION',
+	4124623270: 'IFCSHELLBASEDSURFACEMODEL',
+	3692461612: 'IFCSIMPLEPROPERTY',
+	3663146110: 'IFCSIMPLEPROPERTYTEMPLATE',
+	4097777520: 'IFCSITE',
+	1529196076: 'IFCSLAB',
+	3127900445: 'IFCSLABELEMENTEDCASE',
+	3027962421: 'IFCSLABSTANDARDCASE',
+	2533589738: 'IFCSLABTYPE',
+	2609359061: 'IFCSLIPPAGECONNECTIONCONDITION',
+	3420628829: 'IFCSOLARDEVICE',
+	1072016465: 'IFCSOLARDEVICETYPE',
+	723233188: 'IFCSOLIDMODEL',
+	3856911033: 'IFCSPACE',
+	1999602285: 'IFCSPACEHEATER',
+	1305183839: 'IFCSPACEHEATERTYPE',
+	3812236995: 'IFCSPACETYPE',
+	1412071761: 'IFCSPATIALELEMENT',
+	710998568: 'IFCSPATIALELEMENTTYPE',
+	2706606064: 'IFCSPATIALSTRUCTUREELEMENT',
+	3893378262: 'IFCSPATIALSTRUCTUREELEMENTTYPE',
+	463610769: 'IFCSPATIALZONE',
+	2481509218: 'IFCSPATIALZONETYPE',
+	451544542: 'IFCSPHERE',
+	4015995234: 'IFCSPHERICALSURFACE',
+	1404847402: 'IFCSTACKTERMINAL',
+	3112655638: 'IFCSTACKTERMINALTYPE',
+	331165859: 'IFCSTAIR',
+	4252922144: 'IFCSTAIRFLIGHT',
+	1039846685: 'IFCSTAIRFLIGHTTYPE',
+	338393293: 'IFCSTAIRTYPE',
+	682877961: 'IFCSTRUCTURALACTION',
+	3544373492: 'IFCSTRUCTURALACTIVITY',
+	2515109513: 'IFCSTRUCTURALANALYSISMODEL',
+	1179482911: 'IFCSTRUCTURALCONNECTION',
+	2273995522: 'IFCSTRUCTURALCONNECTIONCONDITION',
+	1004757350: 'IFCSTRUCTURALCURVEACTION',
+	4243806635: 'IFCSTRUCTURALCURVECONNECTION',
+	214636428: 'IFCSTRUCTURALCURVEMEMBER',
+	2445595289: 'IFCSTRUCTURALCURVEMEMBERVARYING',
+	2757150158: 'IFCSTRUCTURALCURVEREACTION',
+	3136571912: 'IFCSTRUCTURALITEM',
+	1807405624: 'IFCSTRUCTURALLINEARACTION',
+	2162789131: 'IFCSTRUCTURALLOAD',
+	385403989: 'IFCSTRUCTURALLOADCASE',
+	3478079324: 'IFCSTRUCTURALLOADCONFIGURATION',
+	1252848954: 'IFCSTRUCTURALLOADGROUP',
+	1595516126: 'IFCSTRUCTURALLOADLINEARFORCE',
+	609421318: 'IFCSTRUCTURALLOADORRESULT',
+	2668620305: 'IFCSTRUCTURALLOADPLANARFORCE',
+	2473145415: 'IFCSTRUCTURALLOADSINGLEDISPLACEMENT',
+	1973038258: 'IFCSTRUCTURALLOADSINGLEDISPLACEMENTDISTORTION',
+	1597423693: 'IFCSTRUCTURALLOADSINGLEFORCE',
+	1190533807: 'IFCSTRUCTURALLOADSINGLEFORCEWARPING',
+	2525727697: 'IFCSTRUCTURALLOADSTATIC',
+	3408363356: 'IFCSTRUCTURALLOADTEMPERATURE',
+	530289379: 'IFCSTRUCTURALMEMBER',
+	1621171031: 'IFCSTRUCTURALPLANARACTION',
+	2082059205: 'IFCSTRUCTURALPOINTACTION',
+	734778138: 'IFCSTRUCTURALPOINTCONNECTION',
+	1235345126: 'IFCSTRUCTURALPOINTREACTION',
+	3689010777: 'IFCSTRUCTURALREACTION',
+	2986769608: 'IFCSTRUCTURALRESULTGROUP',
+	3657597509: 'IFCSTRUCTURALSURFACEACTION',
+	1975003073: 'IFCSTRUCTURALSURFACECONNECTION',
+	3979015343: 'IFCSTRUCTURALSURFACEMEMBER',
+	2218152070: 'IFCSTRUCTURALSURFACEMEMBERVARYING',
+	603775116: 'IFCSTRUCTURALSURFACEREACTION',
+	2830218821: 'IFCSTYLEMODEL',
+	3958052878: 'IFCSTYLEDITEM',
+	3049322572: 'IFCSTYLEDREPRESENTATION',
+	148013059: 'IFCSUBCONTRACTRESOURCE',
+	4095615324: 'IFCSUBCONTRACTRESOURCETYPE',
+	2233826070: 'IFCSUBEDGE',
+	2513912981: 'IFCSURFACE',
+	699246055: 'IFCSURFACECURVE',
+	2028607225: 'IFCSURFACECURVESWEPTAREASOLID',
+	3101698114: 'IFCSURFACEFEATURE',
+	2809605785: 'IFCSURFACEOFLINEAREXTRUSION',
+	4124788165: 'IFCSURFACEOFREVOLUTION',
+	2934153892: 'IFCSURFACEREINFORCEMENTAREA',
+	1300840506: 'IFCSURFACESTYLE',
+	3303107099: 'IFCSURFACESTYLELIGHTING',
+	1607154358: 'IFCSURFACESTYLEREFRACTION',
+	1878645084: 'IFCSURFACESTYLERENDERING',
+	846575682: 'IFCSURFACESTYLESHADING',
+	1351298697: 'IFCSURFACESTYLEWITHTEXTURES',
+	626085974: 'IFCSURFACETEXTURE',
+	2247615214: 'IFCSWEPTAREASOLID',
+	1260650574: 'IFCSWEPTDISKSOLID',
+	1096409881: 'IFCSWEPTDISKSOLIDPOLYGONAL',
+	230924584: 'IFCSWEPTSURFACE',
+	1162798199: 'IFCSWITCHINGDEVICE',
+	2315554128: 'IFCSWITCHINGDEVICETYPE',
+	2254336722: 'IFCSYSTEM',
+	413509423: 'IFCSYSTEMFURNITUREELEMENT',
+	1580310250: 'IFCSYSTEMFURNITUREELEMENTTYPE',
+	3071757647: 'IFCTSHAPEPROFILEDEF',
+	985171141: 'IFCTABLE',
+	2043862942: 'IFCTABLECOLUMN',
+	531007025: 'IFCTABLEROW',
+	812556717: 'IFCTANK',
+	5716631: 'IFCTANKTYPE',
+	3473067441: 'IFCTASK',
+	1549132990: 'IFCTASKTIME',
+	2771591690: 'IFCTASKTIMERECURRING',
+	3206491090: 'IFCTASKTYPE',
+	912023232: 'IFCTELECOMADDRESS',
+	3824725483: 'IFCTENDON',
+	2347447852: 'IFCTENDONANCHOR',
+	3081323446: 'IFCTENDONANCHORTYPE',
+	3663046924: 'IFCTENDONCONDUIT',
+	2281632017: 'IFCTENDONCONDUITTYPE',
+	2415094496: 'IFCTENDONTYPE',
+	2387106220: 'IFCTESSELLATEDFACESET',
+	901063453: 'IFCTESSELLATEDITEM',
+	4282788508: 'IFCTEXTLITERAL',
+	3124975700: 'IFCTEXTLITERALWITHEXTENT',
+	1447204868: 'IFCTEXTSTYLE',
+	1983826977: 'IFCTEXTSTYLEFONTMODEL',
+	2636378356: 'IFCTEXTSTYLEFORDEFINEDFONT',
+	1640371178: 'IFCTEXTSTYLETEXTMODEL',
+	280115917: 'IFCTEXTURECOORDINATE',
+	1742049831: 'IFCTEXTURECOORDINATEGENERATOR',
+	2552916305: 'IFCTEXTUREMAP',
+	1210645708: 'IFCTEXTUREVERTEX',
+	3611470254: 'IFCTEXTUREVERTEXLIST',
+	1199560280: 'IFCTIMEPERIOD',
+	3101149627: 'IFCTIMESERIES',
+	581633288: 'IFCTIMESERIESVALUE',
+	1377556343: 'IFCTOPOLOGICALREPRESENTATIONITEM',
+	1735638870: 'IFCTOPOLOGYREPRESENTATION',
+	1935646853: 'IFCTOROIDALSURFACE',
+	3825984169: 'IFCTRANSFORMER',
+	1692211062: 'IFCTRANSFORMERTYPE',
+	2595432518: 'IFCTRANSITIONCURVESEGMENT2D',
+	1620046519: 'IFCTRANSPORTELEMENT',
+	2097647324: 'IFCTRANSPORTELEMENTTYPE',
+	2715220739: 'IFCTRAPEZIUMPROFILEDEF',
+	2916149573: 'IFCTRIANGULATEDFACESET',
+	1229763772: 'IFCTRIANGULATEDIRREGULARNETWORK',
+	3593883385: 'IFCTRIMMEDCURVE',
+	3026737570: 'IFCTUBEBUNDLE',
+	1600972822: 'IFCTUBEBUNDLETYPE',
+	1628702193: 'IFCTYPEOBJECT',
+	3736923433: 'IFCTYPEPROCESS',
+	2347495698: 'IFCTYPEPRODUCT',
+	3698973494: 'IFCTYPERESOURCE',
+	427810014: 'IFCUSHAPEPROFILEDEF',
+	180925521: 'IFCUNITASSIGNMENT',
+	630975310: 'IFCUNITARYCONTROLELEMENT',
+	3179687236: 'IFCUNITARYCONTROLELEMENTTYPE',
+	4292641817: 'IFCUNITARYEQUIPMENT',
+	1911125066: 'IFCUNITARYEQUIPMENTTYPE',
+	4207607924: 'IFCVALVE',
+	728799441: 'IFCVALVETYPE',
+	1417489154: 'IFCVECTOR',
+	2799835756: 'IFCVERTEX',
+	2759199220: 'IFCVERTEXLOOP',
+	1907098498: 'IFCVERTEXPOINT',
+	1530820697: 'IFCVIBRATIONDAMPER',
+	3956297820: 'IFCVIBRATIONDAMPERTYPE',
+	2391383451: 'IFCVIBRATIONISOLATOR',
+	3313531582: 'IFCVIBRATIONISOLATORTYPE',
+	2769231204: 'IFCVIRTUALELEMENT',
+	891718957: 'IFCVIRTUALGRIDINTERSECTION',
+	926996030: 'IFCVOIDINGFEATURE',
+	2391406946: 'IFCWALL',
+	4156078855: 'IFCWALLELEMENTEDCASE',
+	3512223829: 'IFCWALLSTANDARDCASE',
+	1898987631: 'IFCWALLTYPE',
+	4237592921: 'IFCWASTETERMINAL',
+	1133259667: 'IFCWASTETERMINALTYPE',
+	3304561284: 'IFCWINDOW',
+	336235671: 'IFCWINDOWLININGPROPERTIES',
+	512836454: 'IFCWINDOWPANELPROPERTIES',
+	486154966: 'IFCWINDOWSTANDARDCASE',
+	1299126871: 'IFCWINDOWSTYLE',
+	4009809668: 'IFCWINDOWTYPE',
+	4088093105: 'IFCWORKCALENDAR',
+	1028945134: 'IFCWORKCONTROL',
+	4218914973: 'IFCWORKPLAN',
+	3342526732: 'IFCWORKSCHEDULE',
+	1236880293: 'IFCWORKTIME',
+	2543172580: 'IFCZSHAPEPROFILEDEF',
+	1033361043: 'IFCZONE',
 };
 
 class PropertyManager {
-	constructor(state) {
+
+	constructor( state ) {
+
 		this.state = state;
+
 	}
 
-	getExpressId(geometry, faceIndex) {
-		if (!geometry.index) return;
+	getExpressId( geometry, faceIndex ) {
+
+		if ( ! geometry.index )
+			return;
 		const geoIndex = geometry.index.array;
-		return geometry.attributes[IdAttrName].getX(geoIndex[3 * faceIndex]);
-	}
+		return geometry.attributes[ IdAttrName ].getX( geoIndex[ 3 * faceIndex ] );
 
-	getItemProperties(modelID, id, recursive = false) {
-		return this.state.api.GetLine(modelID, id, recursive);
 	}
 
-	getAllItemsOfType(modelID, type, verbose) {
-		const items = [];
-		const lines = this.state.api.GetLineIDsWithType(modelID, type);
-		for (let i = 0; i < lines.size(); i++) items.push(lines.get(i));
-		if (verbose) return items.map((id) => this.state.api.GetLine(modelID, id));
-		return items;
-	}
+	getItemProperties( modelID, id, recursive = false ) {
+
+		return this.state.useJSON ?
+			{
+				...this.state.models[ modelID ].jsonData[ id ]
+			} :
+			this.state.api.GetLine( modelID, id, recursive );
 
-	getPropertySets(modelID, elementID, recursive = false) {
-		const propSetIds = this.getAllRelatedItemsOfType(
-			modelID,
-			elementID,
-			PropsNames.psets
-		);
-		return propSetIds.map((id) =>
-			this.state.api.GetLine(modelID, id, recursive)
-		);
-	}
-
-	getTypeProperties(modelID, elementID, recursive = false) {
-		const typeId = this.getAllRelatedItemsOfType(
-			modelID,
-			elementID,
-			PropsNames.type
-		);
-		return typeId.map((id) => this.state.api.GetLine(modelID, id, recursive));
-	}
-
-	getSpatialStructure(modelID) {
-		const chunks = this.getSpatialTreeChunks(modelID);
-		const projectID = this.state.api
-			.GetLineIDsWithType(modelID, IFCPROJECT)
-			.get(0);
-		const project = this.newIfcProject(projectID);
-		this.getSpatialNode(modelID, project, chunks);
-		return project;
 	}
 
-	newIfcProject(id) {
-		return {
-			expressID: id,
-			type: "IFCPROJECT",
-			children: [],
-		};
+	getAllItemsOfType( modelID, type, verbose ) {
+
+		return this.state.useJSON ?
+			this.getAllItemsOfTypeJSON( modelID, type, verbose ) :
+			this.getAllItemsOfTypeWebIfcAPI( modelID, type, verbose );
+
 	}
 
-	getSpatialTreeChunks(modelID) {
-		const treeChunks = {};
-		this.getChunks(modelID, treeChunks, PropsNames.aggregates);
-		this.getChunks(modelID, treeChunks, PropsNames.spatial);
-		return treeChunks;
+	getPropertySets( modelID, elementID, recursive = false ) {
+
+		return this.state.useJSON ?
+			this.getPropertyJSON( modelID, elementID, recursive, PropsNames.psets ) :
+			this.getPropertyWebIfcAPI( modelID, elementID, recursive, PropsNames.psets );
+
 	}
 
-	getChunks(modelID, chunks, propNames) {
-		const relation = this.state.api.GetLineIDsWithType(modelID, propNames.name);
-		for (let i = 0; i < relation.size(); i++) {
-			const rel = this.state.api.GetLine(modelID, relation.get(i), false);
-			const relating = rel[propNames.relating].value;
-			const related = rel[propNames.related].map((r) => r.value);
-			if (chunks[relating] == undefined) {
-				chunks[relating] = related;
-			} else {
-				chunks[relating] = chunks[relating].concat(related);
-			}
-		}
+	getTypeProperties( modelID, elementID, recursive = false ) {
+
+		return this.state.useJSON ?
+			this.getPropertyJSON( modelID, elementID, recursive, PropsNames.type ) :
+			this.getPropertyWebIfcAPI( modelID, elementID, recursive, PropsNames.type );
+
 	}
 
-	getSpatialNode(modelID, node, treeChunks) {
-		this.getChildren(modelID, node, treeChunks, PropsNames.aggregates);
-		this.getChildren(modelID, node, treeChunks, PropsNames.spatial);
+	getMaterialsProperties( modelID, elementID, recursive = false ) {
+
+		return this.state.useJSON ?
+			this.getPropertyJSON( modelID, elementID, recursive, PropsNames.materials ) :
+			this.getPropertyWebIfcAPI( modelID, elementID, recursive, PropsNames.materials );
+
 	}
 
-	getChildren(modelID, node, treeChunks, propNames) {
-		const children = treeChunks[node.expressID];
-		if (children == undefined || children == null) return;
-		const prop = propNames.key;
-		node[prop] = children.map((child) => {
-			const node = this.newNode(modelID, child);
-			this.getSpatialNode(modelID, node, treeChunks);
-			return node;
-		});
+	getSpatialStructure( modelID ) {
+
+		return this.state.useJSON ?
+			this.getSpatialStructureJSON( modelID ) :
+			this.getSpatialStructureWebIfcAPI( modelID );
+
 	}
 
-	newNode(modelID, id) {
-		const typeID = this.state.models[modelID].types[id].toString();
-		const typeName = IfcElements[typeID];
+	getSpatialStructureJSON( modelID ) {
+
+		const chunks = this.getSpatialTreeChunks( modelID );
+		const projectID = this.getAllItemsOfTypeJSON( modelID, IFCPROJECT, false )[ 0 ];
+		const project = this.newIfcProject( projectID );
+		this.getSpatialNode( modelID, project, chunks );
 		return {
-			expressID: id,
-			type: typeName,
-			children: [],
+			...project
 		};
-	}
 
-	getAllRelatedItemsOfType(modelID, id, propNames) {
-		const lines = this.state.api.GetLineIDsWithType(modelID, propNames.name);
-		const IDs = [];
-		for (let i = 0; i < lines.size(); i++) {
-			const rel = this.state.api.GetLine(modelID, lines.get(i));
-			const isRelated = this.isRelated(id, rel, propNames);
-			if (isRelated) this.getRelated(rel, propNames, IDs);
-		}
-		return IDs;
 	}
 
-	getRelated(rel, propNames, IDs) {
-		const element = rel[propNames.relating];
-		if (!Array.isArray(element)) IDs.push(element.value);
-		else element.forEach((ele) => IDs.push(ele.value));
-	}
+	getSpatialStructureWebIfcAPI( modelID ) {
 
-	isRelated(id, rel, propNames) {
-		const relatedItems = rel[propNames.related];
-		if (Array.isArray(relatedItems)) {
-			const values = relatedItems.map((item) => item.value);
-			return values.includes(id);
-		}
-		return relatedItems.value === id;
-	}
-}
+		const chunks = this.getSpatialTreeChunks( modelID );
+		const projectID = this.state.api.GetLineIDsWithType( modelID, IFCPROJECT ).get( 0 );
+		const project = this.newIfcProject( projectID );
+		this.getSpatialNode( modelID, project, chunks );
+		return project;
 
-class TypeManager {
-	constructor(state) {
-		this.state = state;
 	}
 
-	getAllTypes() {
-		for (let modelID in this.state.models) {
-			const types = this.state.models[modelID].types;
-			if (Object.keys(types).length == 0)
-				this.getAllTypesOfModel(parseInt(modelID));
+	getAllItemsOfTypeJSON( modelID, type, verbose ) {
+
+		const data = this.state.models[ modelID ].jsonData;
+		const typeName = IfcTypesMap[ type ];
+		if ( ! typeName ) {
+
+			throw new Error( `Type not found: ${type}` );
+
 		}
-	}
 
-	getAllTypesOfModel(modelID) {
-		this.state.models[modelID].types;
-		const elements = Object.keys(IfcElements).map((e) => parseInt(e));
-		const types = this.state.models[modelID].types;
-		elements.forEach((type) => {
-			const lines = this.state.api.GetLineIDsWithType(modelID, type);
-			for (let i = 0; i < lines.size(); i++) types[lines.get(i)] = type;
-		});
+		return this.filterJSONItemsByType( data, typeName, verbose );
+
 	}
-}
 
-let modelIdCounter = 0;
+	filterJSONItemsByType( data, typeName, verbose ) {
 
-class IFCModel extends Group {
-	constructor(mesh, ifc) {
-		super();
-		this.mesh = mesh;
-		this.ifc = ifc;
-		this.modelID = modelIdCounter++;
-	}
+		const result = [];
+		Object.keys( data ).forEach( key => {
 
-	setWasmPath(path) {
-		this.ifc.setWasmPath(path);
-	}
+			const numKey = parseInt( key );
+			if ( data[ numKey ].type.toUpperCase() === typeName ) {
 
-	close(scene) {
-		this.ifc.close(this.modelID, scene);
-	}
+				result.push( verbose ? {
+					...data[ numKey ]
+				} : numKey );
 
-	getExpressId(geometry, faceIndex) {
-		return this.ifc.getExpressId(geometry, faceIndex);
-	}
+			}
 
-	getAllItemsOfType(type, verbose) {
-		return this.ifc.getAllItemsOfType(this.modelID, type, verbose);
-	}
+		} );
+		return result;
 
-	getItemProperties(id, recursive = false) {
-		return this.ifc.getItemProperties(this.modelID, id, recursive);
 	}
 
-	getPropertySets(id, recursive = false) {
-		return this.ifc.getPropertySets(this.modelID, id, recursive);
-	}
+	getItemsByIDJSON( modelID, ids ) {
 
-	getTypeProperties(id, recursive = false) {
-		return this.ifc.getTypeProperties(this.modelID, id, recursive);
-	}
+		const data = this.state.models[ modelID ].jsonData;
+		const result = [];
+		ids.forEach( id => result.push( {
+			...data[ id ]
+		} ) );
+		return result;
 
-	getIfcType(id) {
-		return this.ifc.getIfcType(this.modelID, id);
 	}
 
-	getSpatialStructure() {
-		return this.ifc.getSpatialStructure(this.modelID);
-	}
+	getPropertyJSON( modelID, elementID, recursive = false, propName ) {
 
-	getSubset(material) {
-		return this.ifc.getSubset(this.modelID, material);
-	}
+		const resultIDs = this.getAllRelatedItemsOfTypeJSON( modelID, elementID, propName );
+		const result = this.getItemsByIDJSON( modelID, resultIDs );
+		if ( recursive ) {
 
-	removeSubset(scene, material) {
-		this.ifc.removeSubset(this.modelID, scene, material);
-	}
+			result.forEach( result => this.getJSONReferencesRecursively( modelID, result ) );
 
-	createSubset(config) {
-		const modelConfig = {
-			...config,
-			modelID: this.modelID,
-		};
-		return this.ifc.createSubset(modelConfig);
-	}
-}
+		}
 
-class IFCManager {
-	constructor() {
-		this.state = {
-			models: [],
-			api: new IfcAPI(),
-		};
-		this.parser = new IFCParser(this.state);
-		this.subsets = new SubsetManager(this.state);
-		this.properties = new PropertyManager(this.state);
-		this.types = new TypeManager(this.state);
-	}
+		return result;
 
-	async parse(buffer) {
-		const mesh = await this.parser.parse(buffer);
-		this.types.getAllTypes();
-		return new IFCModel(mesh, this);
 	}
 
-	setWasmPath(path) {
-		this.state.api.SetWasmPath(path);
-	}
+	getJSONReferencesRecursively( modelID, jsonObject ) {
 
-	setupThreeMeshBVH(computeBoundsTree, disposeBoundsTree, acceleratedRaycast) {
-		this.parser.initializeMeshBVH(
-			computeBoundsTree,
-			disposeBoundsTree,
-			acceleratedRaycast
-		);
-	}
+		if ( jsonObject == undefined )
+			return;
+		const keys = Object.keys( jsonObject );
+		for ( let i = 0; i < keys.length; i ++ ) {
 
-	close(modelID, scene) {
-		this.state.api.CloseModel(modelID);
-		if (scene) scene.remove(this.state.models[modelID].mesh);
-		delete this.state.models[modelID];
-	}
+			const key = keys[ i ];
+			this.getJSONItem( modelID, jsonObject, key );
 
-	getExpressId(geometry, faceIndex) {
-		return this.properties.getExpressId(geometry, faceIndex);
-	}
+		}
 
-	getAllItemsOfType(modelID, type, verbose) {
-		return this.properties.getAllItemsOfType(modelID, type, verbose);
 	}
 
-	getItemProperties(modelID, id, recursive = false) {
-		return this.properties.getItemProperties(modelID, id, recursive);
-	}
+	getJSONItem( modelID, jsonObject, key ) {
 
-	getPropertySets(modelID, id, recursive = false) {
-		return this.properties.getPropertySets(modelID, id, recursive);
-	}
+		if ( Array.isArray( jsonObject[ key ] ) ) {
 
-	getTypeProperties(modelID, id, recursive = false) {
-		return this.properties.getTypeProperties(modelID, id, recursive);
-	}
+			return this.getMultipleJSONItems( modelID, jsonObject, key );
 
-	getIfcType(modelID, id) {
-		const typeID = this.state.models[modelID].types[id];
-		return IfcElements[typeID.toString()];
-	}
+		}
 
-	getSpatialStructure(modelID) {
-		return this.properties.getSpatialStructure(modelID);
-	}
+		if ( jsonObject[ key ] && jsonObject[ key ].type === 5 ) {
 
-	getSubset(modelID, material) {
-		return this.subsets.getSubset(modelID, material);
-	}
+			jsonObject[ key ] = this.getItemsByIDJSON( modelID, [ jsonObject[ key ].value ] )[ 0 ];
+			this.getJSONReferencesRecursively( modelID, jsonObject[ key ] );
+
+		}
 
-	removeSubset(modelID, scene, material) {
-		this.subsets.removeSubset(modelID, scene, material);
 	}
 
-	createSubset(config) {
-		return this.subsets.createSubset(config);
+	getMultipleJSONItems( modelID, jsonObject, key ) {
+
+		jsonObject[ key ] = jsonObject[ key ].map( ( item ) => {
+
+			if ( item.type === 5 ) {
+
+				item = this.getItemsByIDJSON( modelID, [ item.value ] )[ 0 ];
+				this.getJSONReferencesRecursively( modelID, item );
+
+			}
+
+			return item;
+
+		} );
+
 	}
-}
 
-class IFCLoader extends Loader {
-	constructor(manager) {
-		super(manager);
-		this.ifcManager = new IFCManager();
+	getPropertyWebIfcAPI( modelID, elementID, recursive = false, propName ) {
+
+		const propSetIds = this.getAllRelatedItemsOfTypeWebIfcAPI( modelID, elementID, propName );
+		return propSetIds.map( ( id ) => this.state.api.GetLine( modelID, id, recursive ) );
+
 	}
 
-	load(url, onLoad, onProgress, onError) {
-		const scope = this;
-		const loader = new FileLoader(scope.manager);
-		loader.setPath(scope.path);
-		loader.setResponseType("arraybuffer");
-		loader.setRequestHeader(scope.requestHeader);
-		loader.setWithCredentials(scope.withCredentials);
-		loader.load(
-			url,
-			async function (buffer) {
-				try {
-					if (typeof buffer == "string") {
-						throw new Error("IFC files must be given as a buffer!");
-					}
-					onLoad(await scope.parse(buffer));
-				} catch (e) {
-					if (onError) {
-						onError(e);
-					} else {
-						console.error(e);
-					}
-					scope.manager.itemError(url);
-				}
-			},
-			onProgress,
-			onError
-		);
+	getAllItemsOfTypeWebIfcAPI( modelID, type, verbose ) {
+
+		const items = [];
+		const lines = this.state.api.GetLineIDsWithType( modelID, type );
+		for ( let i = 0; i < lines.size(); i ++ )
+			items.push( lines.get( i ) );
+		if ( verbose )
+			return items.map( ( id ) => this.state.api.GetLine( modelID, id ) );
+		return items;
+
 	}
 
-	parse(buffer) {
-		return this.ifcManager.parse(buffer);
+	newIfcProject( id ) {
+
+		return {
+			expressID: id,
+			type: 'IFCPROJECT',
+			children: []
+		};
+
 	}
+
+	getSpatialTreeChunks( modelID ) {
+
+		const treeChunks = {};
+		const json = this.state.useJSON;
+		if ( json ) {
+
+			this.getChunksJSON( modelID, treeChunks, PropsNames.aggregates );
+			this.getChunksJSON( modelID, treeChunks, PropsNames.spatial );
+
+		} else {
+
+			this.getChunksWebIfcAPI( modelID, treeChunks, PropsNames.aggregates );
+			this.getChunksWebIfcAPI( modelID, treeChunks, PropsNames.spatial );
+
+		}
+
+		return treeChunks;
+
+	}
+
+	getChunksJSON( modelID, chunks, propNames ) {
+
+		const relation = this.getAllItemsOfTypeJSON( modelID, propNames.name, true );
+		relation.forEach( rel => {
+
+			this.saveChunk( chunks, propNames, rel );
+
+		} );
+
+	}
+
+	getChunksWebIfcAPI( modelID, chunks, propNames ) {
+
+		const relation = this.state.api.GetLineIDsWithType( modelID, propNames.name );
+		for ( let i = 0; i < relation.size(); i ++ ) {
+
+			const rel = this.state.api.GetLine( modelID, relation.get( i ), false );
+			this.saveChunk( chunks, propNames, rel );
+
+		}
+
+	}
+
+	saveChunk( chunks, propNames, rel ) {
+
+		const relating = rel[ propNames.relating ].value;
+		const related = rel[ propNames.related ].map( ( r ) => r.value );
+		if ( chunks[ relating ] == undefined ) {
+
+			chunks[ relating ] = related;
+
+		} else {
+
+			chunks[ relating ] = chunks[ relating ].concat( related );
+
+		}
+
+	}
+
+	getSpatialNode( modelID, node, treeChunks ) {
+
+		this.getChildren( modelID, node, treeChunks, PropsNames.aggregates );
+		this.getChildren( modelID, node, treeChunks, PropsNames.spatial );
+
+	}
+
+	getChildren( modelID, node, treeChunks, propNames ) {
+
+		const children = treeChunks[ node.expressID ];
+		if ( children == undefined )
+			return;
+		const prop = propNames.key;
+		node[ prop ] = children.map( ( child ) => {
+
+			const node = this.newNode( modelID, child );
+			this.getSpatialNode( modelID, node, treeChunks );
+			return node;
+
+		} );
+
+	}
+
+	newNode( modelID, id ) {
+
+		const typeName = this.getNodeType( modelID, id );
+		return {
+			expressID: id,
+			type: typeName,
+			children: []
+		};
+
+	}
+
+	getNodeType( modelID, id ) {
+
+		if ( this.state.useJSON )
+			return this.state.models[ modelID ].jsonData[ id ].type;
+		const typeID = this.state.models[ modelID ].types[ id ];
+		return IfcElements[ typeID ];
+
+	}
+
+	getAllRelatedItemsOfTypeJSON( modelID, id, propNames ) {
+
+		const lines = this.getAllItemsOfTypeJSON( modelID, propNames.name, true );
+		const IDs = [];
+		lines.forEach( line => {
+
+			const isRelated = this.isRelated( id, line, propNames );
+			if ( isRelated )
+				this.getRelated( line, propNames, IDs );
+
+		} );
+		return IDs;
+
+	}
+
+	getAllRelatedItemsOfTypeWebIfcAPI( modelID, id, propNames ) {
+
+		const lines = this.state.api.GetLineIDsWithType( modelID, propNames.name );
+		const IDs = [];
+		for ( let i = 0; i < lines.size(); i ++ ) {
+
+			const rel = this.state.api.GetLine( modelID, lines.get( i ) );
+			const isRelated = this.isRelated( id, rel, propNames );
+			if ( isRelated )
+				this.getRelated( rel, propNames, IDs );
+
+		}
+
+		return IDs;
+
+	}
+
+	getRelated( rel, propNames, IDs ) {
+
+		const element = rel[ propNames.relating ];
+		if ( ! Array.isArray( element ) )
+			IDs.push( element.value );
+		else
+			element.forEach( ( ele ) => IDs.push( ele.value ) );
+
+	}
+
+	isRelated( id, rel, propNames ) {
+
+		const relatedItems = rel[ propNames.related ];
+		if ( Array.isArray( relatedItems ) ) {
+
+			const values = relatedItems.map( ( item ) => item.value );
+			return values.includes( id );
+
+		}
+
+		return relatedItems.value === id;
+
+	}
+
+}
+
+class TypeManager {
+
+	constructor( state ) {
+
+		this.state = state;
+
+	}
+
+	getAllTypes() {
+
+		for ( const modelID in this.state.models ) {
+
+			const types = this.state.models[ modelID ].types;
+			if ( Object.keys( types ).length == 0 )
+				this.getAllTypesOfModel( parseInt( modelID ) );
+
+		}
+
+	}
+
+	getAllTypesOfModel( modelID ) {
+
+		this.state.models[ modelID ].types;
+		const elements = Object.keys( IfcElements ).map( ( e ) => parseInt( e ) );
+		const types = this.state.models[ modelID ].types;
+		elements.forEach( ( type ) => {
+
+			const lines = this.state.api.GetLineIDsWithType( modelID, type );
+			for ( let i = 0; i < lines.size(); i ++ )
+				types[ lines.get( i ) ] = type;
+
+		} );
+
+	}
+
+}
+
+let modelIdCounter = 0;
+const nullIfcManagerErrorMessage = 'IfcManager is null!';
+
+class IFCModel extends Mesh {
+
+	constructor() {
+
+		super( ...arguments );
+		this.modelID = modelIdCounter ++;
+		this.ifcManager = null;
+		this.mesh = this;
+
+	}
+
+	setIFCManager( manager ) {
+
+		this.ifcManager = manager;
+
+	}
+
+	setWasmPath( path ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		this.ifcManager.setWasmPath( path );
+
+	}
+
+	close( scene ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		this.ifcManager.close( this.modelID, scene );
+
+	}
+
+	getExpressId( geometry, faceIndex ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getExpressId( geometry, faceIndex );
+
+	}
+
+	getAllItemsOfType( type, verbose ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getAllItemsOfType( this.modelID, type, verbose );
+
+	}
+
+	getItemProperties( id, recursive = false ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getItemProperties( this.modelID, id, recursive );
+
+	}
+
+	getPropertySets( id, recursive = false ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getPropertySets( this.modelID, id, recursive );
+
+	}
+
+	getTypeProperties( id, recursive = false ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getTypeProperties( this.modelID, id, recursive );
+
+	}
+
+	getIfcType( id ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getIfcType( this.modelID, id );
+
+	}
+
+	getSpatialStructure() {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getSpatialStructure( this.modelID );
+
+	}
+
+	getSubset( material ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		return this.ifcManager.getSubset( this.modelID, material );
+
+	}
+
+	removeSubset( parent, material ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		this.ifcManager.removeSubset( this.modelID, parent, material );
+
+	}
+
+	createSubset( config ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		const modelConfig = {
+			...config,
+			modelID: this.modelID
+		};
+		return this.ifcManager.createSubset( modelConfig );
+
+	}
+
+	hideItems( ids ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		this.ifcManager.hideItems( this.modelID, ids );
+
+	}
+
+	hideAllItems() {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		this.ifcManager.hideAllItems( this.modelID );
+
+	}
+
+	showItems( ids ) {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		this.ifcManager.showItems( this.modelID, ids );
+
+	}
+
+	showAllItems() {
+
+		if ( this.ifcManager === null )
+			throw new Error( nullIfcManagerErrorMessage );
+		this.ifcManager.showAllItems( this.modelID );
+
+	}
+
+}
+
+class BvhManager {
+
+	initializeMeshBVH( computeBoundsTree, disposeBoundsTree, acceleratedRaycast ) {
+
+		this.computeBoundsTree = computeBoundsTree;
+		this.disposeBoundsTree = disposeBoundsTree;
+		this.acceleratedRaycast = acceleratedRaycast;
+		this.setupThreeMeshBVH();
+
+	}
+
+	applyThreeMeshBVH( geometry ) {
+
+		if ( this.computeBoundsTree )
+			geometry.computeBoundsTree();
+
+	}
+
+	setupThreeMeshBVH() {
+
+		if ( ! this.computeBoundsTree || ! this.disposeBoundsTree || ! this.acceleratedRaycast )
+			return;
+		BufferGeometry.prototype.computeBoundsTree = this.computeBoundsTree;
+		BufferGeometry.prototype.disposeBoundsTree = this.disposeBoundsTree;
+		Mesh.prototype.raycast = this.acceleratedRaycast;
+
+	}
+
+}
+
+class ItemsHider {
+
+	constructor( state ) {
+
+		this.modelCoordinates = {};
+		this.expressIDCoordinatesMap = {};
+		this.state = state;
+
+	}
+
+
+
+	processCoordinates( modelID ) {
+
+		const attributes = this.getAttributes( modelID );
+		const ids = Array.from( attributes.expressID.array );
+		this.expressIDCoordinatesMap[ modelID ] = {};
+		for ( let i = 0; i < ids.length; i ++ ) {
+
+			if ( ! this.expressIDCoordinatesMap[ modelID ][ ids[ i ] ] ) {
+
+				this.expressIDCoordinatesMap[ modelID ][ ids[ i ] ] = [];
+
+			}
+
+			const current = this.expressIDCoordinatesMap[ modelID ];
+			current[ ids[ i ] ].push( 3 * i );
+
+		}
+
+		this.initializeCoordinates( modelID );
+
+	}
+
+	hideItems( modelID, ids ) {
+
+		this.editCoordinates( modelID, ids, true );
+
+	}
+
+	showItems( modelID, ids ) {
+
+		this.editCoordinates( modelID, ids, false );
+
+	}
+
+	editCoordinates( modelID, ids, hide ) {
+
+		const current = this.expressIDCoordinatesMap[ modelID ];
+		const indices = [];
+		ids.forEach( ( id ) => {
+
+			if ( current[ id ] )
+				indices.push( ...current[ id ] );
+
+		} );
+		const coords = this.getCoordinates( modelID );
+		const initial = this.modelCoordinates[ modelID ];
+		if ( hide )
+			indices.forEach( i => coords.set( [ 0, 0, 0 ], i ) );
+		else
+			indices.forEach( i => coords.set( [ initial[ i ], initial[ i + 1 ], initial[ i + 2 ] ], i ) );
+		this.getAttributes( modelID ).position.needsUpdate = true;
+
+	}
+
+	showAllItems( modelID ) {
+
+		if ( this.modelCoordinates[ modelID ] ) {
+
+			this.resetCoordinates( modelID );
+			this.getAttributes( modelID ).position.needsUpdate = true;
+
+		}
+
+	}
+
+	hideAllItems( modelID ) {
+
+		this.getCoordinates( modelID ).fill( 0 );
+		this.getAttributes( modelID ).position.needsUpdate = true;
+
+	}
+
+	initializeCoordinates( modelID ) {
+
+		const coordinates = this.getCoordinates( modelID );
+		if ( ! this.modelCoordinates[ modelID ] ) {
+
+			this.modelCoordinates[ modelID ] = new Float32Array( coordinates );
+
+		}
+
+	}
+
+	resetCoordinates( modelID ) {
+
+		const initial = this.modelCoordinates[ modelID ];
+		this.getCoordinates( modelID ).set( initial );
+
+	}
+
+	getCoordinates( modelID ) {
+
+		return this.getAttributes( modelID ).position.array;
+
+	}
+
+	getAttributes( modelID ) {
+
+		return this.state.models[ modelID ].mesh.geometry.attributes;
+
+	}
+
+}
+
+class IFCManager {
+
+	constructor() {
+
+		this.state = {
+			models: [],
+			api: new IfcAPI(),
+			useJSON: false
+		};
+		this.BVH = new BvhManager();
+		this.parser = new IFCParser( this.state, this.BVH );
+		this.subsets = new SubsetManager( this.state, this.BVH );
+		this.properties = new PropertyManager( this.state );
+		this.types = new TypeManager( this.state );
+		this.hider = new ItemsHider( this.state );
+
+	}
+
+	async parse( buffer ) {
+
+		const mesh = await this.parser.parse( buffer );
+		this.state.useJSON ? this.disposeMemory() : this.types.getAllTypes();
+		this.hider.processCoordinates( mesh.modelID );
+		const model = new IFCModel( mesh.geometry, mesh.material );
+		model.setIFCManager( this );
+		return model;
+
+	}
+
+	setWasmPath( path ) {
+
+		this.state.api.SetWasmPath( path );
+
+	}
+
+	applyWebIfcConfig( settings ) {
+
+		this.state.webIfcSettings = settings;
+
+	}
+
+	useJSONData( useJSON = true ) {
+
+		this.state.useJSON = useJSON;
+		this.disposeMemory();
+
+	}
+
+	addModelJSONData( modelID, data ) {
+
+		const model = this.state.models[ modelID ];
+		if ( model ) {
+
+			model.jsonData = data;
+
+		}
+
+	}
+
+	disposeMemory() {
+
+		this.state.api = null;
+		this.state.api = new IfcAPI();
+
+	}
+
+	setupThreeMeshBVH( computeBoundsTree, disposeBoundsTree, acceleratedRaycast ) {
+
+		this.BVH.initializeMeshBVH( computeBoundsTree, disposeBoundsTree, acceleratedRaycast );
+
+	}
+
+	close( modelID, scene ) {
+
+		this.state.api.CloseModel( modelID );
+		if ( scene ) {
+
+			scene.remove( this.state.models[ modelID ].mesh );
+
+		}
+
+		delete this.state.models[ modelID ];
+
+	}
+
+	getExpressId( geometry, faceIndex ) {
+
+		return this.properties.getExpressId( geometry, faceIndex );
+
+	}
+
+	getAllItemsOfType( modelID, type, verbose ) {
+
+		return this.properties.getAllItemsOfType( modelID, type, verbose );
+
+	}
+
+	getItemProperties( modelID, id, recursive = false ) {
+
+		return this.properties.getItemProperties( modelID, id, recursive );
+
+	}
+
+	getPropertySets( modelID, id, recursive = false ) {
+
+		return this.properties.getPropertySets( modelID, id, recursive );
+
+	}
+
+	getTypeProperties( modelID, id, recursive = false ) {
+
+		return this.properties.getTypeProperties( modelID, id, recursive );
+
+	}
+
+	getMaterialsProperties( modelID, id, recursive = false ) {
+
+		return this.properties.getMaterialsProperties( modelID, id, recursive );
+
+	}
+
+	getIfcType( modelID, id ) {
+
+		const typeID = this.state.models[ modelID ].types[ id ];
+		return IfcElements[ typeID ];
+
+	}
+
+	getSpatialStructure( modelID ) {
+
+		return this.properties.getSpatialStructure( modelID );
+
+	}
+
+	getSubset( modelID, material ) {
+
+		return this.subsets.getSubset( modelID, material );
+
+	}
+
+	removeSubset( modelID, parent, material ) {
+
+		this.subsets.removeSubset( modelID, parent, material );
+
+	}
+
+	createSubset( config ) {
+
+		return this.subsets.createSubset( config );
+
+	}
+
+	hideItems( modelID, ids ) {
+
+		this.hider.hideItems( modelID, ids );
+
+	}
+
+	hideAllItems( modelID ) {
+
+		this.hider.hideAllItems( modelID );
+
+	}
+
+	showItems( modelID, ids ) {
+
+		this.hider.showItems( modelID, ids );
+
+	}
+
+	showAllItems( modelID ) {
+
+		this.hider.showAllItems( modelID );
+
+	}
+
+}
+
+class IFCLoader extends Loader {
+
+	constructor( manager ) {
+
+		super( manager );
+		this.ifcManager = new IFCManager();
+
+	}
+
+	load( url, onLoad, onProgress, onError ) {
+
+		const scope = this;
+		const loader = new FileLoader( scope.manager );
+		loader.setPath( scope.path );
+		loader.setResponseType( 'arraybuffer' );
+		loader.setRequestHeader( scope.requestHeader );
+		loader.setWithCredentials( scope.withCredentials );
+		loader.load( url, async function ( buffer ) {
+
+			try {
+
+				if ( typeof buffer == 'string' ) {
+
+					throw new Error( 'IFC files must be given as a buffer!' );
+
+				}
+
+				onLoad( await scope.parse( buffer ) );
+
+			} catch ( e ) {
+
+				if ( onError ) {
+
+					onError( e );
+
+				} else {
+
+					console.error( e );
+
+				}
+
+				scope.manager.itemError( url );
+
+			}
+
+		}, onProgress, onError );
+
+	}
+
+	parse( buffer ) {
+
+		return this.ifcManager.parse( buffer );
+
+	}
+
 }
 
 export { IFCLoader };

+ 403 - 120
examples/jsm/loaders/KTX2Loader.js

@@ -6,9 +6,6 @@
  * a wide variety of GPU texture compression formats. While KTX 2.0 also allows
  * other hardware-specific formats, this loader does not yet parse them.
  *
- * This loader parses the KTX 2.0 container and then relies on
- * THREE.BasisTextureLoader to complete the transcoding process.
- *
  * References:
  * - KTX: http://github.khronos.org/KTX-Specification/
  * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
@@ -16,33 +13,43 @@
 
 import {
 	CompressedTexture,
-	CompressedTextureLoader,
 	FileLoader,
 	LinearEncoding,
+	LinearFilter,
+	LinearMipmapLinearFilter,
+	Loader,
+	RGBAFormat,
+	RGBA_ASTC_4x4_Format,
+	RGBA_BPTC_Format,
+	RGBA_ETC2_EAC_Format,
+	RGBA_PVRTC_4BPPV1_Format,
+	RGBA_S3TC_DXT5_Format,
+	RGB_ETC1_Format,
+	RGB_ETC2_Format,
+	RGB_PVRTC_4BPPV1_Format,
+	RGB_S3TC_DXT1_Format,
 	sRGBEncoding,
+	UnsignedByteType
 } from '../../../build/three.module.js';
-import {
-	read as readKTX,
-	KTX2ChannelETC1S,
-	KTX2ChannelUASTC,
-	KTX2Flags,
-	KTX2Model,
-	KTX2SupercompressionScheme,
-	KTX2Transfer
-} from '../libs/ktx-parse.module.js';
-import { BasisTextureLoader } from './BasisTextureLoader.js';
-import { ZSTDDecoder } from '../libs/zstddec.module.js';
-
-class KTX2Loader extends CompressedTextureLoader {
+import { WorkerPool } from '../utils/WorkerPool.js'
+
+const KTX2TransferSRGB = 2;
+const KTX2_ALPHA_PREMULTIPLIED = 1;
+const _taskCache = new WeakMap();
+
+class KTX2Loader extends Loader {
 
 	constructor( manager ) {
 
 		super( manager );
 
-		this.basisLoader = new BasisTextureLoader( manager );
-		this.zstd = new ZSTDDecoder();
+		this.transcoderPath = '';
+		this.transcoderBinary = null;
+		this.transcoderPending = null;
 
-		this.zstd.init();
+		this.workerPool = new WorkerPool();
+		this.workerSourceURL = '';
+		this.workerConfig = null;
 
 		if ( typeof MSC_TRANSCODER !== 'undefined' ) {
 
@@ -59,15 +66,15 @@ class KTX2Loader extends CompressedTextureLoader {
 
 	setTranscoderPath( path ) {
 
-		this.basisLoader.setTranscoderPath( path );
+		this.transcoderPath = path;
 
 		return this;
 
 	}
 
-	setWorkerLimit( path ) {
+	setWorkerLimit( num ) {
 
-		this.basisLoader.setWorkerLimit( path );
+		this.workerPool.setWorkerLimit( num );
 
 		return this;
 
@@ -75,7 +82,15 @@ class KTX2Loader extends CompressedTextureLoader {
 
 	detectSupport( renderer ) {
 
-		this.basisLoader.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;
 
@@ -83,200 +98,468 @@ class KTX2Loader extends CompressedTextureLoader {
 
 	dispose() {
 
-		this.basisLoader.dispose();
+		this.workerPool.dispose();
+		if ( this.workerSourceURL ) URL.revokeObjectURL( this.workerSourceURL );
 
 		return this;
 
 	}
 
+	init() {
+
+		if ( ! this.transcoderPending ) {
+
+			// Load transcoder wrapper.
+			const jsLoader = new FileLoader( this.manager );
+			jsLoader.setPath( this.transcoderPath );
+			jsLoader.setWithCredentials( this.withCredentials );
+			const jsContent = jsLoader.loadAsync( 'basis_transcoder.js' );
+
+			// Load transcoder WASM binary.
+			const binaryLoader = new FileLoader( this.manager );
+			binaryLoader.setPath( this.transcoderPath );
+			binaryLoader.setResponseType( 'arraybuffer' );
+			binaryLoader.setWithCredentials( this.withCredentials );
+			const binaryContent = binaryLoader.loadAsync( 'basis_transcoder.wasm' )
+
+			this.transcoderPending = Promise.all( [ jsContent, binaryContent ] )
+				.then( ( [ jsContent, binaryContent ] ) => {
+
+					const fn = KTX2Loader.BasisWorker.toString();
+
+					const body = [
+						'/* constants */',
+						'let _EngineFormat = ' + JSON.stringify( KTX2Loader.EngineFormat ),
+						'let _TranscoderFormat = ' + JSON.stringify( KTX2Loader.TranscoderFormat ),
+						'let _BasisFormat = ' + JSON.stringify( KTX2Loader.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;
+
+					this.workerPool.setWorkerCreator( () => {
+
+						const worker = new Worker( this.workerSourceURL );
+						const transcoderBinary = this.transcoderBinary.slice( 0 );
+			
+						worker.postMessage( { type: 'init', config: this.workerConfig, transcoderBinary }, [ transcoderBinary ] );
+			
+						return worker;
+			
+					} );
+
+				} );
+
+		}
+
+		return this.transcoderPending;
+
+	}
+
 	load( url, onLoad, onProgress, onError ) {
 
-		var scope = this;
+		const loader = new FileLoader( this.manager );
 
-		var texture = new CompressedTexture();
+		loader.setResponseType( 'arraybuffer' );
+		loader.setWithCredentials( this.withCredentials );
 
-		var bufferPending = new Promise( function ( resolve, reject ) {
+		const texture = new CompressedTexture();
 
-			new FileLoader( scope.manager )
-				.setPath( scope.path )
-				.setResponseType( 'arraybuffer' )
-				.load( url, resolve, onProgress, reject );
+		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 );
 
-		bufferPending
-			.then( function ( buffer ) {
+				return cachedTask.promise.then( onLoad ).catch( onError );
 
-				scope.parse( buffer, function ( _texture ) {
+			}
+
+			this._createTexture( [ buffer ] )
+				.then( function ( _texture ) {
 
 					texture.copy( _texture );
 					texture.needsUpdate = true;
 
 					if ( onLoad ) onLoad( texture );
 
-				}, onError );
+				} )
+				.catch( onError );
 
-			} )
-			.catch( onError );
+		}, onProgress, onError );
 
 		return texture;
 
 	}
 
-	parse( buffer, onLoad, onError ) {
+	createTextureFrom( transcodeResult ) {
+		const { mipmaps, width, height, format, type, error, dfdTransferFn, dfdFlags } = transcodeResult;
 
-		var scope = this;
+		if ( type === 'error' ) return Promise.reject( error );
 
-		var ktx = readKTX( new Uint8Array( buffer ) );
+		const texture = new CompressedTexture( mipmaps, width, height, format, UnsignedByteType );
+		texture.minFilter = mipmaps.length === 1 ? LinearFilter : LinearMipmapLinearFilter;
+		texture.magFilter = LinearFilter;
+		texture.generateMipmaps = false;
+		texture.needsUpdate = true;
+		texture.encoding = dfdTransferFn === KTX2TransferSRGB ? sRGBEncoding: LinearEncoding;
+		texture.premultiplyAlpha = !! ( dfdFlags & KTX2_ALPHA_PREMULTIPLIED );
 
-		if ( ktx.pixelDepth > 0 ) {
+		return texture;
 
-			throw new Error( 'THREE.KTX2Loader: Only 2D textures are currently supported.' );
+	}
 
-		}
+	/**
+	 * @param {ArrayBuffer[]} buffers
+	 * @param {object?} config
+	 * @return {Promise<CompressedTexture>}
+	 */
+	_createTexture( buffers, config = {} ) {
 
-		if ( ktx.layerCount > 1 ) {
+		const taskConfig = config;
+		const texturePending = this.init().then( () => {
 
-			throw new Error( 'THREE.KTX2Loader: Array textures are not currently supported.' );
+			return this.workerPool.postMessage( { type: 'transcode', buffers, taskConfig: taskConfig }, buffers );
 
-		}
+		} ).then( ( e ) => this.createTextureFrom( e.data ) );
 
-		if ( ktx.faceCount > 1 ) {
+		// Cache the task result.
+		_taskCache.set( buffers[ 0 ], { promise: texturePending } );
 
-			throw new Error( 'THREE.KTX2Loader: Cube textures are not currently supported.' );
+		return texturePending;
 
-		}
+	}
 
-		var dfd = KTX2Utils.getBasicDFD( ktx );
+	dispose() {
 
-		KTX2Utils.createLevels( ktx, this.zstd ).then( function ( levels ) {
+		URL.revokeObjectURL( this.workerSourceURL );
+		this.workerPool.dispose();
 
-			var basisFormat = dfd.colorModel === KTX2Model.UASTC
-				? BasisTextureLoader.BasisFormat.UASTC_4x4
-				: BasisTextureLoader.BasisFormat.ETC1S;
+		return this;
 
-			var parseConfig = {
+	}
 
-				levels: levels,
-				width: ktx.pixelWidth,
-				height: ktx.pixelHeight,
-				basisFormat: basisFormat,
-				hasAlpha: KTX2Utils.getAlpha( ktx ),
+}
 
-			};
 
-			if ( basisFormat === BasisTextureLoader.BasisFormat.ETC1S ) {
+/* CONSTANTS */
 
-				parseConfig.globalData = ktx.globalData;
+KTX2Loader.BasisFormat = {
+	ETC1S: 0,
+	UASTC_4x4: 1,
+};
 
-			}
+KTX2Loader.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,
+};
 
-			return scope.basisLoader.parseInternalAsync( parseConfig );
+KTX2Loader.EngineFormat = {
+	RGBAFormat: RGBAFormat,
+	RGBA_ASTC_4x4_Format: RGBA_ASTC_4x4_Format,
+	RGBA_BPTC_Format: RGBA_BPTC_Format,
+	RGBA_ETC2_EAC_Format: RGBA_ETC2_EAC_Format,
+	RGBA_PVRTC_4BPPV1_Format: RGBA_PVRTC_4BPPV1_Format,
+	RGBA_S3TC_DXT5_Format: RGBA_S3TC_DXT5_Format,
+	RGB_ETC1_Format: RGB_ETC1_Format,
+	RGB_ETC2_Format: RGB_ETC2_Format,
+	RGB_PVRTC_4BPPV1_Format: RGB_PVRTC_4BPPV1_Format,
+	RGB_S3TC_DXT1_Format: RGB_S3TC_DXT1_Format,
+};
 
-		} ).then( function ( texture ) {
 
-			texture.encoding = dfd.transferFunction === KTX2Transfer.SRGB
-				? sRGBEncoding
-				: LinearEncoding;
-			texture.premultiplyAlpha = KTX2Utils.getPremultiplyAlpha( ktx );
+/* WEB WORKER */
 
-			onLoad( texture );
+KTX2Loader.BasisWorker = function () {
 
-		} ).catch( onError );
+	let config;
+	let transcoderPending;
+	let BasisModule;
 
-		return this;
+	const EngineFormat = _EngineFormat; // eslint-disable-line no-undef
+	const TranscoderFormat = _TranscoderFormat; // eslint-disable-line no-undef
+	const BasisFormat = _BasisFormat; // eslint-disable-line no-undef
 
-	}
+	self.addEventListener( 'message', 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, dfdTransferFn, dfdFlags } = transcode( message.buffers[ 0 ] );
+
+						const buffers = [];
+
+						for ( let i = 0; i < mipmaps.length; ++ i ) {
+
+							buffers.push( mipmaps[ i ].data.buffer );
 
-var KTX2Utils = {
+						}
 
-	createLevels: async function ( ktx, zstd ) {
+						self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format, dfdTransferFn, dfdFlags }, buffers );
 
-		if ( ktx.supercompressionScheme === KTX2SupercompressionScheme.ZSTD ) {
+					} catch ( error ) {
 
-			await zstd.init();
+						console.error( error );
+
+						self.postMessage( { type: 'error', id: message.id, error: error.message } );
+
+					}
+
+				} );
+				break;
 
 		}
 
-		var levels = [];
-		var width = ktx.pixelWidth;
-		var height = ktx.pixelHeight;
+	} );
+
+	function init( wasmBinary ) {
+
+		transcoderPending = new Promise( ( resolve ) => {
 
-		for ( var levelIndex = 0; levelIndex < ktx.levels.length; levelIndex ++ ) {
+			BasisModule = { wasmBinary, onRuntimeInitialized: resolve };
+			BASIS( BasisModule ); // eslint-disable-line no-undef
 
-			var levelWidth = Math.max( 1, Math.floor( width / Math.pow( 2, levelIndex ) ) );
-			var levelHeight = Math.max( 1, Math.floor( height / Math.pow( 2, levelIndex ) ) );
-			var levelData = ktx.levels[ levelIndex ].levelData;
+		} ).then( () => {
 
-			if ( ktx.supercompressionScheme === KTX2SupercompressionScheme.ZSTD ) {
+			BasisModule.initializeBasis();
 
-				levelData = zstd.decode( levelData, ktx.levels[ levelIndex ].uncompressedByteLength );
+			if ( BasisModule.KTX2File === undefined ) {
+
+				console.warn( 'THREE.KTX2Loader: Please update Basis Universal transcoder.' );
 
 			}
 
-			levels.push( {
+		} );
+
+	}
+
+	function transcode( buffer ) {
 
-				index: levelIndex,
-				width: levelWidth,
-				height: levelHeight,
-				data: levelData,
+		const ktx2File = new BasisModule.KTX2File( new Uint8Array( buffer ) );
 
-			} );
+		function cleanup() {
+
+			ktx2File.close();
+			ktx2File.delete();
+
+		}
+
+		if ( !ktx2File.isValid() ) {
+
+			cleanup();
+			throw new Error( 'THREE.KTX2Loader:	Invalid or unsupported .ktx2 file' );
 
 		}
 
-		return levels;
+		const basisFormat = ktx2File.isUASTC() ? BasisFormat.UASTC_4x4 : BasisFormat.ETC1S;
+		const width = ktx2File.getWidth();
+		const height = ktx2File.getHeight();
+		const levels = ktx2File.getLevels();
+		const hasAlpha = ktx2File.getHasAlpha();
+		const dfdTransferFn = ktx2File.getDFDTransferFunc();
+		const dfdFlags = ktx2File.getDFDFlags();
 
-	},
+		const { transcoderFormat, engineFormat } = getTranscoderFormat( basisFormat, width, height, hasAlpha );
 
-	getBasicDFD: function ( ktx ) {
+		if ( ! width || ! height || ! levels ) {
 
-		// Basic Data Format Descriptor Block is always the first DFD.
-		return ktx.dataFormatDescriptor[ 0 ];
+			cleanup();
+			throw new Error( 'THREE.KTX2Loader:	Invalid texture' );
 
-	},
+		}
+
+		if ( ! ktx2File.startTranscoding() ) {
 
-	getAlpha: function ( ktx ) {
+			cleanup();
+			throw new Error( 'THREE.KTX2Loader: .startTranscoding failed' );
 
-		var dfd = this.getBasicDFD( ktx );
+		}
 
-		// UASTC
+		const mipmaps = [];
 
-		if ( dfd.colorModel === KTX2Model.UASTC ) {
+		for ( let mip = 0; mip < levels; mip ++ ) {
 
-			if ( ( dfd.samples[ 0 ].channelID & 0xF ) === KTX2ChannelUASTC.RGBA ) {
+			const levelInfo = ktx2File.getImageLevelInfo( mip, 0, 0 )
+			const mipWidth = levelInfo.origWidth;
+			const mipHeight = levelInfo.origHeight;
+			const dst = new Uint8Array( ktx2File.getImageTranscodedSizeInBytes( mip, 0, 0, transcoderFormat ) );
+
+			const status = ktx2File.transcodeImage(
+				dst,
+				mip,
+				0,
+				0,
+				transcoderFormat,
+				0,
+				-1,
+				-1,
+			);
 
-				return true;
+			if ( ! status ) {
+
+				cleanup();
+				throw new Error( 'THREE.KTX2Loader: .transcodeImage failed.' );
 
 			}
 
-			return false;
+			mipmaps.push( { data: dst, width: mipWidth, height: mipHeight } );
 
 		}
 
-		// ETC1S
+		cleanup();
+
+		return { width, height, hasAlpha, mipmaps, format: engineFormat, dfdTransferFn, dfdFlags };
 
-		if ( dfd.samples.length === 2
-			&& ( dfd.samples[ 1 ].channelID & 0xF ) === KTX2ChannelETC1S.AAA ) {
+	}
 
-			return true;
+	//
+
+	// 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 };
 
 		}
 
-		return false;
+		console.warn( 'THREE.KTX2Loader: No suitable compressed texture format found. Decoding to RGBA32.' );
 
-	},
+		transcoderFormat = TranscoderFormat.RGBA32;
+		engineFormat = EngineFormat.RGBAFormat;
 
-	getPremultiplyAlpha: function ( ktx ) {
+		return { transcoderFormat, engineFormat };
 
-		var dfd = this.getBasicDFD( ktx );
+	}
 
-		return !! ( dfd.flags & KTX2Flags.ALPHA_PREMULTIPLIED );
+	function isPowerOfTwo( value ) {
 
-	},
+		if ( value <= 2 ) return true;
 
-};
+		return ( value & ( value - 1 ) ) === 0 && value !== 0;
+
+	}
+
+}
 
 export { KTX2Loader };

File diff suppressed because it is too large
+ 410 - 201
examples/jsm/loaders/LDrawLoader.js


+ 4 - 4
examples/jsm/loaders/RGBELoader.js

@@ -386,8 +386,8 @@ class RGBELoader extends DataTextureLoader {
 
 					case FloatType:
 
-						numElements = ( image_rgba_data.length / 4 ) * 3;
-						const floatArray = new Float32Array( numElements );
+						numElements = image_rgba_data.length / 4;
+						const floatArray = new Float32Array( numElements * 3 );
 
 						for ( let j = 0; j < numElements; j ++ ) {
 
@@ -402,8 +402,8 @@ class RGBELoader extends DataTextureLoader {
 
 					case HalfFloatType:
 
-						numElements = ( image_rgba_data.length / 4 ) * 3;
-						const halfArray = new Uint16Array( numElements );
+						numElements = image_rgba_data.length / 4;
+						const halfArray = new Uint16Array( numElements * 3 );
 
 						for ( let j = 0; j < numElements; j ++ ) {
 

+ 2 - 2
examples/jsm/loaders/TGALoader.js

@@ -199,10 +199,10 @@ class TGALoader extends DataTextureLoader {
 
 				for ( x = x_start; x !== x_end; x += x_step, i += 2 ) {
 
-					color = image[ i + 0 ] + ( image[ i + 1 ] << 8 ); // Inversed ?
+					color = image[ i + 0 ] + ( image[ i + 1 ] << 8 );
 					imageData[ ( x + width * y ) * 4 + 0 ] = ( color & 0x7C00 ) >> 7;
 					imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2;
-					imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) >> 3;
+					imageData[ ( x + width * y ) * 4 + 2 ] = ( color & 0x001F ) << 3;
 					imageData[ ( x + width * y ) * 4 + 3 ] = ( color & 0x8000 ) ? 0 : 255;
 
 				}

File diff suppressed because it is too large
+ 4557 - 4480
examples/jsm/loaders/ifc/web-ifc-api.js


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