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 />
 			Must be one of these constants:<br /><br />
 			[page:Animation THREE.LoopOnce] - playing the clip once,<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 />
 			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.
 			alternately playing forward and backward.
 		</p>
 		</p>
 
 
@@ -98,7 +98,7 @@
 			The local time of this action (in seconds, starting with 0).<br /><br />
 			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
 			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 />
 			[page:.setEffectiveTimeScale setEffectiveTimeScale] or [page:.setDuration setDuration]).<br />
 		</p>
 		</p>
 
 

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

@@ -15,7 +15,7 @@
 			Object3Ds are a member of layer 0.<br /><br />
 			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
 			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.
 			All classes that inherit from [page:Object3D] have an [page:Object3D.layers] property which is an instance of this class.
 		</p>
 		</p>

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

@@ -41,9 +41,7 @@
 		<h3>[property:Material customDepthMaterial]</h3>
 		<h3>[property:Material customDepthMaterial]</h3>
 		<p>
 		<p>
 		Custom depth material to be used when rendering to the depth map. Can only be used in context of meshes.
 		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>
 		</p>
 
 
 		<h3>[property:Material customDistanceMaterial]</h3>
 		<h3>[property:Material customDistanceMaterial]</h3>

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

@@ -12,7 +12,7 @@
 		<p class="desc">
 		<p class="desc">
 		A class containing utility functions for shapes.<br /><br />
 		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.
 		x, y (and z, w if present) components of a vector.
 		</p>
 		</p>
 
 
@@ -30,7 +30,7 @@
 		<p>
 		<p>
 		pts -- points defining a 2D polygon<br /><br />
 		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 />
 		x, y  components of a polygon.<br /><br />
 
 
 		Used internally by [page:Path Path],
 		Used internally by [page:Path Path],

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

@@ -31,7 +31,6 @@ scene.add( helper );
 
 
 		<h2>Constructor</h2>
 		<h2>Constructor</h2>
 
 
-
 		<h3>[name]( [param:Camera camera] )</h3>
 		<h3>[name]( [param:Camera camera] )</h3>
 		<p>
 		<p>
 		[page:Camera camera] -- The camera to visualize.<br /><br />
 		[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.
 		This create a new [Name] for the specified camera.
 		</p>
 		</p>
 
 
-
-
 		<h2>Properties</h2>
 		<h2>Properties</h2>
 		<p>See the base [page:LineSegments] class for common properties.</p>
 		<p>See the base [page:LineSegments] class for common properties.</p>
 
 
-
-
 		<h3>[property:Camera camera]</h3>
 		<h3>[property:Camera camera]</h3>
 		<p>The camera being visualized.</p>
 		<p>The camera being visualized.</p>
 
 
@@ -57,20 +52,16 @@ scene.add( helper );
 
 
 		<h3>[property:Object matrixAutoUpdate]</h3>
 		<h3>[property:Object matrixAutoUpdate]</h3>
 		<p>
 		<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].
 			camera's [page:Object3D.matrixWorld matrixWorld].
 		</p>
 		</p>
 
 
-
-
-
-
 		<h2>Methods</h2>
 		<h2>Methods</h2>
 		<p>See the base [page:LineSegments] class for common methods.</p>
 		<p>See the base [page:LineSegments] class for common methods.</p>
 
 
-		<h3>[method:CameraHelper dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:null update]()</h3>
 		<h3>[method:null update]()</h3>

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

@@ -13,7 +13,7 @@
 
 
 		<p class="desc">
 		<p class="desc">
 		A helper object to assist with visualizing a [page:Skeleton Skeleton].
 		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>
 		</p>
 
 
 		<h2>Code Example</h2>
 		<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>
 		<p>See the base [page:Light Light] class for common methods.</p>
 
 
-		<h3>[method:DirectionalLight dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		<p>
 		Override of base class's [page:Light.dispose dispose].
 		Override of base class's [page:Light.dispose dispose].
 		Disposes of this light's [page:DirectionalLightShadow shadow].
 		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>
 		<h3>[property:Float intensity]</h3>
 		<p>
 		<p>
 			The light's intensity, or strength.<br />
 			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*.
 			Default - *1.0*.
 		</p>
 		</p>
 
 
@@ -52,7 +51,7 @@
 			See the base [page:Object3D Object3D] class for common methods.
 			See the base [page:Object3D Object3D] class for common methods.
 		</p>
 		</p>
 
 
-		<h3>[method:Light dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		<p>
 		Abstract dispose method for lights; implemented by subclasses that have disposable resources.
 		Abstract dispose method for lights; implemented by subclasses that have disposable resources.
 		</p>
 		</p>

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

@@ -77,17 +77,23 @@ scene.add( light );
 			Default is *0.0*.
 			Default is *0.0*.
 		</p>
 		</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>
 		<h3>[property:Float power]</h3>
 		<p>
 		<p>
 			The light's power.<br />
 			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>
 		</p>
 
 
 		<h3>[property:PointLightShadow shadow]</h3>
 		<h3>[property:PointLightShadow shadow]</h3>
@@ -105,7 +111,7 @@ scene.add( light );
 			See the base [page:Light Light] class for common methods.
 			See the base [page:Light Light] class for common methods.
 		</p>
 		</p>
 
 
-		<h3>[method:PointLight dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		<p>
 		Override of base class's [page:Light.dispose dispose].
 		Override of base class's [page:Light.dispose dispose].
 		Disposes of this light's [page:PointLightShadow shadow].
 		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.
 			See the base [page:Light Light] class for common properties.
 		</p>
 		</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>
 		<h2>Methods</h2>
 		<p>
 		<p>
 			See the base [page:Light Light] class for common methods.
 			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*.
 			Default is *0.0*.
 		</p>
 		</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>
 		<h3>[property:Float penumbra]</h3>
 		<p>
 		<p>
 			Percent of the spotlight cone that is attenuated due to penumbra. Takes values between
 			Percent of the spotlight cone that is attenuated due to penumbra. Takes values between
@@ -117,16 +127,13 @@
 		<h3>[property:Float power]</h3>
 		<h3>[property:Float power]</h3>
 		<p>
 		<p>
 			The light's power.<br />
 			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>
 		</p>
 
 
+
 		<h3>[property:SpotLightShadow shadow]</h3>
 		<h3>[property:SpotLightShadow shadow]</h3>
 		<p>
 		<p>
 			A [page:SpotLightShadow] used to calculate shadows for this light.
 			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>
 		<p>See the base [page:Light Light] class for common methods.</p>
 
 
-		<h3>[method:SpotLight dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		<p>
 		Override of base class's [page:Light.dispose dispose].
 		Override of base class's [page:Light.dispose dispose].
 		Disposes of this light's [page:SpotLightShadow shadow].
 		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>
 		<h3>[property:Float bias]</h3>
 		<p>
 		<p>
 			Shadow map bias, how much to add or subtract from the normalized depth when deciding whether a surface is in shadow.<br />
 			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>
 		</p>
 
 
 		<h3>[property:WebGLRenderTarget map]</h3>
 		<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.
 		Used internally by the renderer to get the number of viewports that need to be rendered for this shadow.
 		</p>
 		</p>
 
 
-		<h3>[method:LightShadow dispose]()</h3>
+		<h3>[method:undefined dispose]()</h3>
 		<p>
 		<p>
 		Disposes of this shadow's textures ([page:LightShadow.map map] and [page:LightShadow.mapPass mapPass]).
 		Disposes of this shadow's textures ([page:LightShadow.map map] and [page:LightShadow.mapPass mapPass]).
 		</p>
 		</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
 		<p class="desc">A global instance of the [page:LoadingManager LoadingManager], used by most loaders
 			when no custom manager has been specified.<br /><br />
 			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.
 			for say, textures and models.
 		</p>
 		</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
 			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 />
 			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>
 		</p>
 
 

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

@@ -32,7 +32,7 @@
 		<h3>[property:Float alphaTest]</h3>
 		<h3>[property:Float alphaTest]</h3>
 		<p>
 		<p>
 		Sets the alpha value to be used when running an alpha test.
 		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*.
 		Default is *0*.
 		</p>
 		</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*
 			This models the reflectivity of non-metallic materials. It has no effect when [page:MeshStandardMaterial.metalness metalness] is *1.0*
 		</p>
 		</p>
 
 
-		<h3>[property:Color sheen]</h3>
+		<h3>[property:Color sheenTint]</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[property:Float transmission]</h3>
 		<h3>[property:Float transmission]</h3>

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

@@ -53,7 +53,7 @@
 						The loop variable has to be *i*.
 						The loop variable has to be *i*.
 					</li>
 					</li>
 					<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>
 					</li>
 				</ul>
 				</ul>
 				<code>
 				<code>
@@ -109,7 +109,6 @@
 			[example:webgl_marchingcubes webgl / marchingcubes]<br />
 			[example:webgl_marchingcubes webgl / marchingcubes]<br />
 			[example:webgl_materials_envmaps webgl / materials / envmaps]<br />
 			[example:webgl_materials_envmaps webgl / materials / envmaps]<br />
 			[example:webgl_materials_lightmap webgl / materials / lightmap]<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_materials_wireframe webgl / materials / wireframe]<br />
 			[example:webgl_modifier_tessellation webgl / modifier / tessellation]<br />
 			[example:webgl_modifier_tessellation webgl / modifier / tessellation]<br />
 			[example:webgl_postprocessing_dof2 webgl / postprocessing / dof2]<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 point] - [page:Vector3].<br/>
 		[page:Vector3 target] — the result will be copied into this Vector3.<br /><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>
 		</p>
 
 
 		<h3>[method:Vector3 getSize]( [param:Vector3 target] )</h3>
 		<h3>[method:Vector3 getSize]( [param:Vector3 target] )</h3>

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

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

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

@@ -152,7 +152,7 @@
 		and determines the direction of the [page:.normal normal].
 		and determines the direction of the [page:.normal normal].
 		</p>
 		</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>
 		<p>
 		[page:Vector3 normal] - a unit length [page:Vector3] defining the normal of the plane.<br />
 		[page:Vector3 normal] - a unit length [page:Vector3] defining the normal of the plane.<br />
 		[page:Vector3 point] - [page:Vector3]<br /><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>
 		<p>A [page:Vector3] defining the center of the sphere. Default is (0, 0, 0).</p>
 
 
 		<h3>[property:Float radius]</h3>
 		<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>
 		<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].
 			If you need more instances than the original count value, you have to create a new [name].
 		</p>
 		</p>
 
 
-		<h3>[property:BufferAttribute instanceColor]</h3>
+		<h3>[property:InstancedBufferAttribute instanceColor]</h3>
 		<p>
 		<p>
 			Represents the colors of all instances. *null* by default.
 			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]().
 			You have to set its [page:BufferAttribute.needsUpdate needsUpdate] flag to true if you modify instanced data via [page:.setColorAt]().
 		</p>
 		</p>
 
 
-		<h3>[property:BufferAttribute instanceMatrix]</h3>
+		<h3>[property:InstancedBufferAttribute instanceMatrix]</h3>
 		<p>
 		<p>
 			Represents the local transformation of all instances.
 			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]().
 			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"*.
 		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 />
 		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 />
 		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
 		[page:Boolean depth] - whether the drawing buffer has a
@@ -64,7 +64,7 @@
 		Default is *true*.<br />
 		Default is *true*.<br />
 
 
 		[page:Boolean logarithmicDepthBuffer] -  whether to use a logarithmic depth buffer. It may
 		[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]
 		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.
 		optimization and can cause a decrease in performance.
 		Default is *false*. See the [example:webgl_camera_logarithmicdepthbuffer camera / logarithmicdepthbuffer] example.
 		Default is *false*. See the [example:webgl_camera_logarithmicdepthbuffer camera / logarithmicdepthbuffer] example.
@@ -316,11 +316,16 @@
 		- *WEBGL_compressed_texture_etc1*
 		- *WEBGL_compressed_texture_etc1*
 		</p>
 		</p>
 
 
-		<h3>[method:null forceContextLoss]( )</h3>
+		<h3>[method:void forceContextLoss]()</h3>
 		<p>
 		<p>
 		Simulate loss of the WebGL context. This requires support for the
 		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.
 			[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>
 		</p>
 
 
 		<h3>[method:Float getClearAlpha]()</h3>
 		<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>
 		<h3>[name]( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding )</h3>
 		<p>
 		<p>
 			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
 			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>
 		<p>
 		<p>
 			The interpretation of the data depends on type and format:
 			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>
 		<h3>[name]( data, width, height, depth )</h3>
 		<p>
 		<p>
 			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
 			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>
 		<p>
 		<p>
 			The interpretation of the data depends on type and format:
 			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>
 	<p>含有对象的子级的数组。请参阅[page:Group]来了解将手动对象进行分组的相关信息。</p>
 
 
 	<h3>[property:Material customDepthMaterial]</h3>
 	<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>
 	</p>
 
 
 	<h3>[property:Material customDistanceMaterial]</h3>
 	<h3>[property:Material customDistanceMaterial]</h3>

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

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

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

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

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

@@ -178,10 +178,10 @@
 
 
 		<h3>[method:this fromBufferAttribute]( [param:BufferAttribute attribute], [param:Integer index] )</h3>
 		<h3>[method:this fromBufferAttribute]( [param:BufferAttribute attribute], [param:Integer index] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Integer getHex]()</h3>
 		<h3>[method:Integer getHex]()</h3>
@@ -234,7 +234,7 @@
 		</p>
 		</p>
 
 
 		<h3>[method:Color multiply]( [param:Color color] ) </h3>
 		<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>
 		<h3>[method:Color multiplyScalar]( [param:Number s] ) </h3>
 		<p>将此颜色的RGB值乘以给定的[page:Number s]的值。</p>
 		<p>将此颜色的RGB值乘以给定的[page:Number s]的值。</p>
@@ -308,11 +308,11 @@
 
 
 		<h3>[method:Color setColorName]( [param:String style] ) </h3>
 		<h3>[method:Color setColorName]( [param:String style] ) </h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Color sub]( [param:Color color] ) </h3>
 		<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>
 		<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
 		<p>
 		<p>
-		[page:Vector3 point] - [page:Vector3] to test.<br /><br />
+		[page:Vector3 point] - [page:Vector3] 被检测的点。<br /><br />
 
 
 		检测该点 [page:Vector3 point] 是否在视锥体内。
 		检测该点 [page:Vector3 point] 是否在视锥体内。
 		</p>
 		</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>
 		<h3>[method:this set]( [param:Plane p0], [param:Plane p1], [param:Plane p2], [param:Plane p3], [param:Plane p4], [param:Plane p5] )</h3>
 		<p>
 		<p>
 		从传入的平面设置当前视锥体。没有隐式的顺序。<br>
 		从传入的平面设置当前视锥体。没有隐式的顺序。<br>
-		Note that this method only copies the values from the given objects.
+		请注意,此方法仅复制给定对象的值。
 		</p>
 		</p>
 
 
 		<h3>[method:this setFromProjectionMatrix]( [param:Matrix4 matrix] )</h3>
 		<h3>[method:this setFromProjectionMatrix]( [param:Matrix4 matrix] )</h3>
 		<p>
 		<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>
 		</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>
 		<h3>[method:Float inverseLerp]( [param:Float x], [param:Float y], [param:Float value] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Float lerp]( [param:Float x], [param:Float y], [param:Float t] )</h3>
 		<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>
 		<h3>[method:Float damp]( [param:Float x], [param:Float y], [param:Float lambda], [param:Float dt] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Float mapLinear]( [param:Float x], [param:Float a1], [param:Float a2], [param:Float b1], [param:Float b2] )</h3>
 		<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>
 		<h3>[method:Float pingpong]( [param:Float x], [param:Float length] )</h3>
 		<p>
 		<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>
 		<h3>[method:Integer ceilPowerOfTwo]( [param:Number n] )</h3>
 		<p>返回大于等于 [page:Number n] 的2的最小次幂。</p>
 		<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>
 		<h3>[method:null setQuaternionFromProperEuler]( [param:Quaternion q], [param:Float a], [param:Float b], [param:Float c], [param:String order] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h2>Source</h2>
 		<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>
 		<h3>[method:this extractBasis]( [param:Vector3 xAxis], [param:Vector3 yAxis], [param:Vector3 zAxis] )</h3>
 		<p>
 		<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>
 		<code>
 a, b, c,
 a, b, c,
 d, e, f,
 d, e, f,
 g, h, i
 g, h, i
 		</code>
 		</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>
 		<code>
 xAxis = (a, d, g)
 xAxis = (a, d, g)
 yAxis = (b, e, h)
 yAxis = (b, e, h)
@@ -142,9 +141,7 @@ zAxis = (c, f, i)
 		[page:Float n32] - 设置第三行第二列的值。<br />
 		[page:Float n32] - 设置第三行第二列的值。<br />
 		[page:Float n33] - 设置第三行第三列的值。<br /><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>
 		</p>
 
 
 		<h3>[method:this premultiply]( [param:Matrix3 m] )</h3>
 		<h3>[method:this premultiply]( [param:Matrix3 m] )</h3>
@@ -181,7 +178,7 @@ zAxis = (c, f, i)
 		<p>
 		<p>
 		[page:Array array] -  用于存储当前矩阵转置结果的数组。<br /><br />
 		[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>
 		</p>
 
 

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

@@ -20,24 +20,27 @@
 			任何3D物体[page:Object3D]都有三个关联的矩阵:
 			任何3D物体[page:Object3D]都有三个关联的矩阵:
 			<ul>
 			<ul>
 				<li>
 				<li>
-					[page:Object3D.matrix]: 存储物体的本地变换。 这是对象相对于其父对象的变换。
+					[page:Object3D.matrix]: 存储物体的本地变换矩阵。 这是对象相对于其父对象的变换矩阵
 				</li>
 				</li>
 				<li>
 				<li>
-					[page:Object3D.matrixWorld]: 对象的全局或世界变换。如果对象没有父对象,那么这与存储在矩阵[page:Object3D.matrix matrix]中的本地变换相同。
+					[page:Object3D.matrixWorld]: 对象的全局或世界变换矩阵。如果对象没有父对象,那么这与存储在矩阵[page:Object3D.matrix matrix]中的本地变换矩阵相同。
 				</li>
 				</li>
 				<li>
 				<li>
-					[page:Object3D.modelViewMatrix]: 表示对象相坐标相对于摄像机空间坐标转换
+					[page:Object3D.modelViewMatrix]: 表示对象相对于摄像机坐标系的变换矩阵
 					一个对象的 modelViewMatrix 是物体世界变换矩阵乘以摄像机相对于世界空间变换矩阵的逆矩阵。
 					一个对象的 modelViewMatrix 是物体世界变换矩阵乘以摄像机相对于世界空间变换矩阵的逆矩阵。
 				</li>
 				</li>
 			</ul>
 			</ul>
 
 
-			摄像机[page:Camera Cameras] 有个额外的四维矩阵:
+			摄像机[page:Camera Cameras] 有个额外的四维矩阵:
 			<ul>
 			<ul>
 				<li>
 				<li>
 					[page:Camera.matrixWorldInverse]: 视矩阵 - 摄像机世界坐标变换的逆矩阵。
 					[page:Camera.matrixWorldInverse]: 视矩阵 - 摄像机世界坐标变换的逆矩阵。
 				</li>
 				</li>
 				<li>
 				<li>
-					[page:Camera.projectionMatrix]: 表示将场景中的信息投影到裁剪空间。
+					[page:Camera.projectionMatrix]: 投影变换矩阵,表示将场景中的信息投影到裁剪空间。
+				</li>
+				<li>
+					[page:Camera.projectionMatrixInverse]: 投影变换矩阵的逆矩阵。
 				</li>
 				</li>
 			</ul>
 			</ul>
 			注意:物体的正规矩阵 [page:Object3D.normalMatrix] 并不是一个4维矩阵,而是一个三维矩阵[page:Matrix3]。
 			注意:物体的正规矩阵 [page:Object3D.normalMatrix] 并不是一个4维矩阵,而是一个三维矩阵[page:Matrix3]。
@@ -70,21 +73,21 @@ m.elements = [ 11, 21, 31, 41,
 		请记住,如果您正在阅读源代码,您必须对这里列出的任何矩阵进行转置[link:https://en.wikipedia.org/wiki/Transpose transpose],以理解计算。
 		请记住,如果您正在阅读源代码,您必须对这里列出的任何矩阵进行转置[link:https://en.wikipedia.org/wiki/Transpose transpose],以理解计算。
 		</p>
 		</p>
 
 
-		<h2>Extracting position, rotation and scale</h2>
+		<h2>提取位置(平移)、旋转和缩放</h2>
 		<p>
 		<p>
-			There are several options available for extracting position, rotation and scale from a Matrix4.
+			有多种选项可用于从 Matrix4 中提取位置、旋转和缩放。
 			<ul>
 			<ul>
 				<li>
 				<li>
-					[page:Vector3.setFromMatrixPosition]: can be used to extract the translation component.
+					[page:Vector3.setFromMatrixPosition]:可用于提取位置相关的分量。
 				</li>
 				</li>
 				<li>
 				<li>
-					[page:Vector3.setFromMatrixScale]: can be used to extract the scale component.
+					[page:Vector3.setFromMatrixScale]:可用于提取缩放相关的分量。
 				</li>
 				</li>
 				<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>
 				<li>
 				<li>
-					[page:.decompose decompose] can be used to extract position, rotation and scale all at once.
+					[page:.decompose decompose]:可用于一次性提取位置、旋转和缩放
 				</li>
 				</li>
 			</ul>
 			</ul>
 		</p>
 		</p>
@@ -123,13 +126,13 @@ m.elements = [ 11, 21, 31, 41,
 
 
 		<h3>[method:this copyPosition]( [param:Matrix4 m] )</h3>
 		<h3>[method:this copyPosition]( [param:Matrix4 m] )</h3>
 		<p>
 		<p>
-		将给定矩阵[param:Matrix4 m] 的平移分量拷贝到当前矩阵中。
+		将给定矩阵 [param:Matrix4 m] 的平移分量拷贝到当前矩阵中。
 		</p>
 		</p>
 
 
 		<h3>[method:null decompose]( [param:Vector3 position], [param:Quaternion quaternion], [param:Vector3 scale] )</h3>
 		<h3>[method:null decompose]( [param:Vector3 position], [param:Quaternion quaternion], [param:Vector3 scale] )</h3>
 		<p>
 		<p>
 			将矩阵分解到给定的平移[page:Vector3 position] ,旋转 [page:Quaternion quaternion],缩放[page:Vector3 scale]分量中。<br/><br/>
 			将矩阵分解到给定的平移[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>
 		</p>
 
 
 		<h3>[method:Float determinant]()</h3>
 		<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>
 		<h3>[method:this lookAt]( [param:Vector3 eye], [param:Vector3 center], [param:Vector3 up], )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:this makeRotationAxis]( [param:Vector3 axis], [param:Float theta] )</h3>
 		<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:Vector3 normal] - 单位长度的向量表示平面的法向量。<br />
 			[page:Float constant] - 原点到平面有符号距离。默认值为 *0*。<br /><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>
 		</p>
 
 
 		<h3>[method:Plane setComponents]( [param:Float x], [param:Float y], [param:Float z], [param:Float w] )</h3>
 		<h3>[method:Plane setComponents]( [param:Float x], [param:Float y], [param:Float z], [param:Float w] )</h3>
@@ -144,12 +144,12 @@
 		 根据给定的三个点确定平面。如果三个点共线将会抛出错误。通过右手螺旋规则确定(向量叉乘)法向量 [page:.normal normal]。
 		 根据给定的三个点确定平面。如果三个点共线将会抛出错误。通过右手螺旋规则确定(向量叉乘)法向量 [page:.normal normal]。
 		</p>
 		</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>
 		<p>
 		[page:Vector3 normal] - 平面单位法向量<br />
 		[page:Vector3 normal] - 平面单位法向量<br />
 		[page:Vector3 point] - 平面上的点<br /><br />
 		[page:Vector3 point] - 平面上的点<br /><br />
 
 
-		通过平面上的一点以及法线确定原点到平面的最短距离(常量)
+		通过参数提供的法线 normal 和 平面上的一个点 point 来设置该平面
 		</p>
 		</p>
 
 
 		<h3>[method:Plane translate]( [param:Vector3 offset] )</h3>
 		<h3>[method:Plane translate]( [param:Vector3 offset] )</h3>

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

@@ -132,26 +132,23 @@
 		</p>
 		</p>
 
 
 		<h3>[method:Quaternion premultiply]( [param:Quaternion q] )</h3>
 		<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>
 		<h3>[method:Quaternion rotateTowards]( [param:Quaternion q], [param:Float step] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Quaternion slerp]( [param:Quaternion qb], [param:Float t] )</h3>
 		<h3>[method:Quaternion slerp]( [param:Quaternion qb], [param:Float t] )</h3>
 		<p>
 		<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>
 			<code>
 			// rotate a mesh towards a target quaternion
 			// rotate a mesh towards a target quaternion
@@ -160,7 +157,7 @@
 		</p>
 		</p>
 
 
 		<h3>[method:this slerpQuaternions]( [param:Quaternion qa], [param:Quaternion qb], [param:Float t] )</h3>
 		<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>
 		<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>
 		<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>
 		<h3>[method:Quaternion setFromUnitVectors]( [param:Vector3 vFrom], [param:Vector3 vTo] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Array toArray]( [param:Array array], [param:Integer offset] )</h3>
 		<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>
 		<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>
 		<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>
 		<p>
 		<p>
-		Like the static *slerp* method above, but operates directly on flat arrays of numbers.
+		类似于上面的 *slerp* 方法,但直接对平面数组进行操作。
 		</p>
 		</p>
 
 
 		<!-- Note: Do not add non-static methods to the bottom of this page. Put them above the <h2>Static Methods</h2> -->
 		<!-- 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>
 		<h3>[method:Float distanceSqToPoint]( [param:Vector3 point] )</h3>
 		<p>
 		<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]之间最近的平方距离。
 		获得[page:Ray]与传入的[page:Vector3]之间最近的平方距离。
 		</p>
 		</p>
@@ -193,7 +193,7 @@
 		该向量必须经过标准化(使用[page:Vector3.normalize]),这样才能使方法正常运行。
 		该向量必须经过标准化(使用[page:Vector3.normalize]),这样才能使方法正常运行。
 		<br /><br />
 		<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>
 		</p>
 
 
 
 

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

@@ -29,7 +29,7 @@
 		<p>A [page:Vector3]定义了球心的位置,默认值位于(0, 0, 0)。</p>
 		<p>A [page:Vector3]定义了球心的位置,默认值位于(0, 0, 0)。</p>
 
 
 		<h3>[property:Float radius]</h3>
 		<h3>[property:Float radius]</h3>
-		<p>球的半径,默认值为0。</p>
+		<p>球的半径,默认值为-1。</p>
 
 
 		<h2>方法</h2>
 		<h2>方法</h2>
 
 
@@ -53,7 +53,7 @@
 
 
 		<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
 		<h3>[method:Boolean containsPoint]( [param:Vector3 point] )</h3>
 		<p>
 		<p>
-		[page:Vector3 point] - the [page:Vector3] to be checked<br /><br />
+		[page:Vector3 point] - [page:Vector3] 要被检查的点<br /><br />
 
 
 		检查球体中是否包含所传入的[page:Vector3 point]点,包括球的表面。
 		检查球体中是否包含所传入的[page:Vector3 point]点,包括球的表面。
 		</p>
 		</p>
@@ -66,24 +66,23 @@
 		<h3>[method:Float distanceToPoint]( [param:Vector3 point] )</h3>
 		<h3>[method:Float distanceToPoint]( [param:Vector3 point] )</h3>
 		<p>
 		<p>
 		返回球的边界到所传入的[page:Vector3 point]点的最近距离。
 		返回球的边界到所传入的[page:Vector3 point]点的最近距离。
-		若这个点,则距离将为负值。
+		若这个点位于球内,则距离将为负值。
 		</p>
 		</p>
 
 
 		<h3>[method:this expandByPoint]( [param:Vector3 point] )</h3>
 		<h3>[method:this expandByPoint]( [param:Vector3 point] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Boolean isEmpty]()</h3>
 		<h3>[method:Boolean isEmpty]()</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:Sphere makeEmpty]()</h3>
 		<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>
 		<h3>[method:Boolean equals]( [param:Sphere sphere] )</h3>
 		<p>
 		<p>
@@ -124,7 +123,7 @@
 			[page:Float radius] - 球的半径。<br /><br />
 			[page:Float radius] - 球的半径。<br /><br />
 
 
 		设置球的[page:.center center]和[page:.radius radius]属性。<br>
 		设置球的[page:.center center]和[page:.radius radius]属性。<br>
-		Please note that this method only copies the values from the given center.
+		请注意此,方法使用复制的方式来设置中心值。
 		</p>
 		</p>
 
 
 		<h3>[method:Sphere setFromPoints]( [param:Array points], [param:Vector3 optionalCenter] )</h3>
 		<h3>[method:Sphere setFromPoints]( [param:Array points], [param:Vector3 optionalCenter] )</h3>
@@ -143,9 +142,9 @@
 
 
 		<h3>[method:this union]( [param:Sphere sphere] )</h3>
 		<h3>[method:this union]( [param:Sphere sphere] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h2>源代码</h2>
 		<h2>源代码</h2>

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

@@ -19,10 +19,10 @@
 		<p>
 		<p>
 		[page:Float radius] - 半径值,或者说从该点到原点的
 		[page:Float radius] - 半径值,或者说从该点到原点的
 		[link:https://en.wikipedia.org/wiki/Euclidean_distance Euclidean distance](欧几里得距离,即直线距离)。默认值为*1.0*。<br />
 		[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>
 		</p>
 
 
 
 
@@ -51,7 +51,7 @@
 
 
 		<h3>[method:Spherical makeSafe]()</h3>
 		<h3>[method:Spherical makeSafe]()</h3>
 		<p>
 		<p>
-		将极角 [page:.phi phi] 的值限制在0.000001 和 pi - 0.000001 之间。
+		将极角 [page:.phi phi] 的值限制在0.000001 和 π - 0.000001 之间。
 		</p>
 		</p>
 
 
 		<h3>[method:Spherical set]( [param:Float radius], [param:Float phi], [param:Float theta] )</h3>
 		<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>
 		<h3>[method:Triangle set]( [param:Vector3 a], [param:Vector3 b], [param:Vector3 c] ) [param:Triangle this]</h3>
 		<p>
 		<p>
 			将三角形的[page:.a a]、[page:.b b]和[page:.c c]属性设置为所传入的[page:Vector3 vector3]。<br>
 			将三角形的[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>
 		</p>
 
 
 		<h3>[method:Triangle setFromPointsAndIndices]( [param:Array points], [param:Integer i0], [param:Integer i1], [param:Integer i2] ) [param:Triangle this]</h3>
 		<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>
 		<h3>[method:this applyMatrix4]( [param:Matrix4 m] )</h3>
 		<p>
 		<p>
-			将该向量乘以四阶矩阵m(第四个维度隐式地为1),and divides by perspective.
+			将该向量乘以四阶矩阵m(第四个维度隐式地为1),并按角度进行划分。
 		</p>
 		</p>
 
 
 		<h3>[method:this applyNormalMatrix]( [param:Matrix3 m] )</h3>
 		<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>
 		<h3>[method:this applyQuaternion]( [param:Quaternion quaternion] )</h3>
 		<p>
 		<p>
@@ -302,15 +302,14 @@
 		<p>
 		<p>
 		[page:Camera camera] — 在投影中使用的摄像机。<br /><br />
 		[page:Camera camera] — 在投影中使用的摄像机。<br /><br />
 
 
-		Projects this vector from world space into the camera's normalized device coordinate (NDC) space.
+		将此向量(坐标)从世界空间投影到相机的标准化设备坐标 (NDC) 空间。
 		</p>
 		</p>
 
 
 		<h3>[method:this projectOnPlane]( [param:Vector3 planeNormal] )</h3>
 		<h3>[method:this projectOnPlane]( [param:Vector3 planeNormal] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:this projectOnVector]( [param:Vector3 v] )</h3>
 		<h3>[method:this projectOnVector]( [param:Vector3 v] )</h3>
@@ -318,14 +317,13 @@
 
 
 		<h3>[method:this reflect]( [param:Vector3 normal] )</h3>
 		<h3>[method:this reflect]( [param:Vector3 normal] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:this round]()</h3>
 		<h3>[method:this round]()</h3>
-		<p>	向量中的分量四舍五入取整为最接近的整数值。</p>
+		<p>向量中的分量四舍五入取整为最接近的整数值。</p>
 
 
 		<h3>[method:this roundToZero]()</h3>
 		<h3>[method:this roundToZero]()</h3>
 		<p>
 		<p>
@@ -361,7 +359,7 @@
 
 
 		<h3>[method:this setFromMatrix3Column]( [param:Matrix3 matrix], [param:Integer index] )</h3>
 		<h3>[method:this setFromMatrix3Column]( [param:Matrix3 matrix], [param:Integer index] )</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[method:this setFromMatrixPosition]( [param:Matrix4 m] )</h3>
 		<h3>[method:this setFromMatrixPosition]( [param:Matrix4 m] )</h3>
@@ -430,12 +428,12 @@
 		<p>
 		<p>
 		[page:Camera camera] — 在投影中使用的摄像机。<br /><br />
 		[page:Camera camera] — 在投影中使用的摄像机。<br /><br />
 
 
-		Projects this vector from the camera's normalized device coordinate (NDC) space into world space.
+		将此向量(坐标)从相机的标准化设备坐标 (NDC) 空间投影到世界空间。
 		</p>
 		</p>
 
 
 		<h3>[method:this random]()</h3>
 		<h3>[method:this random]()</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h2>源代码</h2>
 		<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>
 		<h3>[method:null setComponent]( [param:Integer index], [param:Float value] )</h3>
 		<p>
 		<p>
-		[page:Integer index] - 0、1或2。<br />
+		[page:Integer index] - 0、1、2 或 3。<br />
 		[page:Float value] - [page:Float]<br /><br />
 		[page:Float value] - [page:Float]<br /><br />
 
 
 		若index为 0 则设置 [page:.x x] 值为 [page:Float value]。<br />
 		若index为 0 则设置 [page:.x x] 值为 [page:Float value]。<br />
@@ -326,7 +326,7 @@
 
 
 		<h3>[method:this random]()</h3>
 		<h3>[method:this random]()</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h2>源代码</h2>
 		<h2>源代码</h2>

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

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

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

@@ -300,11 +300,16 @@
 		- *WEBGL_compressed_texture_etc1*
 		- *WEBGL_compressed_texture_etc1*
 		</p>
 		</p>
 
 
-		<h3>[method:null forceContextLoss]( )</h3>
+		<h3>[method:void forceContextLoss]()</h3>
 		<p>
 		<p>
 		模拟WebGL环境的丢失。需要支持
 		模拟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>
 		</p>
 
 
 		<h3>[method:Float getClearAlpha]()</h3>
 		<h3>[method:Float getClearAlpha]()</h3>

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

@@ -19,7 +19,7 @@
 		<h3>[name]( data, width, height, depth )</h3>
 		<h3>[name]( data, width, height, depth )</h3>
 		<p>
 		<p>
 			The data argument must be an [link:https://developer.mozilla.org/en-US/docs/Web/API/ArrayBufferView ArrayBufferView].
 			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>
 		<p>
 		<p>
 			The interpretation of the data depends on type and format:
 			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
 		function setUrlFragment( pageName ) { // eslint-disable-line no-undef
 
 
 			// Handle navigation from the subpages (iframes):
 			// 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)
 			// then identify the subpage's URL and set it as URL fragment (re-adding the member)
 
 
 			const splitPageName = decomposePageName( pageName, '.', '.' );
 			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_ballshooter WebXR / VR / ballshooter]<br />
 			[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 			[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 			[example:webxr_vr_dragging WebXR / VR / dragging]<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_paint WebXR / VR / paint]<br />
 			[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 			[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 			[example:webxr_vr_panorama WebXR / VR / panorama]<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_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<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_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<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_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<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_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<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>
 	</code>
 
 
 	<p>
 	<p>
-        마지막으로, 자주 쓰이는 *window.requestAnimationFrame()* 기능을 활용할 수 없기 때문에, 애니메이션 루프를 수정해주어야 합니다. 
+        마지막으로, 자주 쓰이는 *window.requestAnimationFrame()* 기능을 활용할 수 없기 때문에, 애니메이션 루프를 수정해주어야 합니다.
         VR 프로젝트에서는 [page:WebGLRenderer.setAnimationLoop setAnimationLoop]를 사용합니다.
         VR 프로젝트에서는 [page:WebGLRenderer.setAnimationLoop setAnimationLoop]를 사용합니다.
 		가장 간소화된 코드는 다음과 같습니다:
 		가장 간소화된 코드는 다음과 같습니다:
 	</p>
 	</p>
@@ -65,7 +65,6 @@ renderer.setAnimationLoop( function () {
 		[example:webxr_vr_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<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_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<br />
@@ -77,4 +76,4 @@ renderer.setAnimationLoop( function () {
 
 
 </body>
 </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_ballshooter WebXR / VR / ballshooter]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_cubes WebXR / VR / cubes]<br />
 		[example:webxr_vr_dragging WebXR / VR / dragging]<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_paint WebXR / VR / paint]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama_depth WebXR / VR / panorama_depth]<br />
 		[example:webxr_vr_panorama WebXR / VR / panorama]<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();
 				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';
 const cacheName = 'threejs-editor';
 
 

+ 0 - 4
examples/files.json

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

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

@@ -44,7 +44,7 @@
 				cameraAnimation: true
 				cameraAnimation: true
 			};
 			};
 
 
-			this.onBeforePhysics = function ( ) {}; // experimental
+			this.onBeforePhysics = function () {}; // experimental
 
 
 
 
 			this.sharedPhysics = false;
 			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 ) {
 			function handleMouseWheel( event ) {
@@ -698,7 +698,7 @@
 
 
 			}
 			}
 
 
-			function handleTouchEnd( ) { // no-op
+			function handleTouchEnd() { // no-op
 			} //
 			} //
 			// event handlers - FSM: listen for events and reset state
 			// 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();
 				const vector = new THREE.Vector2();
 				return function getMouseOnCircle( pageX, pageY ) {
 				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;
 					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 ) {
 		function handleMouseWheel( event ) {
@@ -691,7 +691,7 @@
 
 
 		}
 		}
 
 
-		function handleTouchEnd( ) { // no-op
+		function handleTouchEnd() { // no-op
 		} //
 		} //
 		// event handlers - FSM: listen for events and reset state
 		// 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 ];
 		pointLight = pointLights[ i ];
 
 
-		getPointDirectLightIrradiance( pointLight, geometry, directLight );
+		getPointLightInfo( pointLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
 		pointLightShadow = pointLightShadows[ i ];
 		pointLightShadow = pointLightShadows[ i ];
@@ -56,7 +56,7 @@ IncidentLight directLight;
 
 
 		spotLight = spotLights[ i ];
 		spotLight = spotLights[ i ];
 
 
-		getSpotDirectLightIrradiance( spotLight, geometry, directLight );
+		getSpotLightInfo( spotLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
 		spotLightShadow = spotLightShadows[ i ];
 		spotLightShadow = spotLightShadows[ i ];
@@ -90,7 +90,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 
 		directionalLight = directionalLights[ i ];
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 
 		// NOTE: Depth gets larger away from the camera.
 		// NOTE: Depth gets larger away from the camera.
 		// cascade.x is closer, cascade.y is further
 		// cascade.x is closer, cascade.y is further
@@ -136,7 +136,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 
 		directionalLight = directionalLights[ i ];
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 
 
@@ -167,7 +167,7 @@ IncidentLight directLight;
 
 
 		directionalLight = directionalLights[ i ];
 		directionalLight = directionalLights[ i ];
 
 
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		directionalLightShadow = directionalLightShadows[ i ];
 		directionalLightShadow = directionalLightShadows[ i ];

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

@@ -10,444 +10,467 @@
  *	NURBS Utils
  *	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 ++ ];
 			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
 		} // Parse nodes in FBXTree.Objects.Deformer
 		// Deformer node can contain skinning or Vertex Cache animation data, however only skinning is supported here
 		// 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 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 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 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 = {
 			const uniforms = {
 				specular: {
 				specular: {
 					value: new THREE.Color().setHex( 0xffffff )
 					value: new THREE.Color().setHex( 0xffffff )
@@ -1555,6 +1555,22 @@
 		return result;
 		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 ************/
 	/********** INTERNALS ************/
@@ -2707,7 +2723,7 @@
 
 
 		}
 		}
 
 
-		getMaterialType( ) {
+		getMaterialType() {
 
 
 			return THREE.MeshStandardMaterial;
 			return THREE.MeshStandardMaterial;
 
 
@@ -3271,7 +3287,8 @@
 								// A CUBICSPLINE keyframe in glTF has three output values for each input value,
 								// A CUBICSPLINE keyframe in glTF has three output values for each input value,
 								// representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
 								// representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
 								// must be divided by three to get the interpolant's sampleSize argument.
 								// 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.
 							}; // 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;
 					const scanline_width = w;
 
 
 					if ( // run length encoding is not allowed so read flat
 					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 the flat buffer
 						return new Uint8Array( buffer );
 						return new Uint8Array( buffer );
@@ -381,8 +380,8 @@
 							break;
 							break;
 
 
 						case THREE.FloatType:
 						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 ++ ) {
 							for ( let j = 0; j < numElements; j ++ ) {
 
 
@@ -396,8 +395,8 @@
 							break;
 							break;
 
 
 						case THREE.HalfFloatType:
 						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 ++ ) {
 							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 ) {
 					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 + 0 ] = ( color & 0x7C00 ) >> 7;
 						imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2;
 						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;
 						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.' );
 			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.' );
 			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
  * 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 {
 	class CSS3DObject extends THREE.Object3D {
 
 
 		constructor( element ) {
 		constructor( element ) {
@@ -186,10 +192,11 @@
 						_matrix.transpose();
 						_matrix.transpose();
 
 
 						if ( object.rotation2D !== 0 ) _matrix.multiply( _matrix2.makeRotationZ( object.rotation2D ) );
 						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[ 3 ] = 0;
 						_matrix.elements[ 7 ] = 0;
 						_matrix.elements[ 7 ] = 0;

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

@@ -17,13 +17,6 @@
 	const lights_mmd_toon_pars_fragment = `
 	const lights_mmd_toon_pars_fragment = `
 varying vec3 vViewPosition;
 varying vec3 vViewPosition;
 
 
-#ifndef FLAT_SHADED
-
-	varying vec3 vNormal;
-
-#endif
-
-
 struct BlinnPhongMaterial {
 struct BlinnPhongMaterial {
 
 
 	vec3 diffuseColor;
 	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;
 	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 ) {
 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 () {
 ( 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
 		// temporary vector
 		_quat = /*@__PURE__*/new THREE.Quaternion(); // temporary quaternion
 		_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
  * @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 () {
 ( 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 () {
 ( 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 () {
 ( 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 ];
 		pointLight = pointLights[ i ];
 
 
-		getPointDirectLightIrradiance( pointLight, geometry, directLight );
+		getPointLightInfo( pointLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS )
 		pointLightShadow = pointLightShadows[ i ];
 		pointLightShadow = pointLightShadows[ i ];
@@ -54,7 +54,7 @@ IncidentLight directLight;
 
 
 		spotLight = spotLights[ i ];
 		spotLight = spotLights[ i ];
 
 
-		getSpotDirectLightIrradiance( spotLight, geometry, directLight );
+		getSpotLightInfo( spotLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS )
 		spotLightShadow = spotLightShadows[ i ];
 		spotLightShadow = spotLightShadows[ i ];
@@ -88,7 +88,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 
 		directionalLight = directionalLights[ i ];
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 
 		// NOTE: Depth gets larger away from the camera.
 		// NOTE: Depth gets larger away from the camera.
 		// cascade.x is closer, cascade.y is further
 		// cascade.x is closer, cascade.y is further
@@ -134,7 +134,7 @@ IncidentLight directLight;
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 	for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {
 
 
 		directionalLight = directionalLights[ i ];
 		directionalLight = directionalLights[ i ];
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 
 
@@ -165,7 +165,7 @@ IncidentLight directLight;
 
 
 		directionalLight = directionalLights[ i ];
 		directionalLight = directionalLights[ i ];
 
 
-		getDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );
+		getDirectionalLightInfo( directionalLight, geometry, directLight );
 
 
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		#if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS )
 		directionalLightShadow = directionalLightShadows[ i ];
 		directionalLightShadow = directionalLightShadows[ i ];

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

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

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

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

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

@@ -14,466 +14,474 @@ import {
  *	NURBS Utils
  *	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 {
 import {
 	Frustum,
 	Frustum,
-	Vector3
+	Vector3,
+	Matrix4,
+	Quaternion,
 } from '../../../build/three.module.js';
 } from '../../../build/three.module.js';
 
 
 /**
 /**
@@ -27,6 +29,10 @@ const _vectemp1 = new Vector3();
 const _vectemp2 = new Vector3();
 const _vectemp2 = new Vector3();
 const _vectemp3 = new Vector3();
 const _vectemp3 = new Vector3();
 
 
+const _matrix = new Matrix4();
+const _quaternion = new Quaternion();
+const _scale = new Vector3();
+
 class SelectionBox {
 class SelectionBox {
 
 
 	constructor( camera, scene, deep = Number.MAX_VALUE ) {
 	constructor( camera, scene, deep = Number.MAX_VALUE ) {
@@ -36,6 +42,7 @@ class SelectionBox {
 		this.startPoint = new Vector3();
 		this.startPoint = new Vector3();
 		this.endPoint = new Vector3();
 		this.endPoint = new Vector3();
 		this.collection = [];
 		this.collection = [];
+		this.instances = {};
 		this.deep = deep;
 		this.deep = deep;
 
 
 	}
 	}
@@ -167,7 +174,24 @@ class SelectionBox {
 
 
 		if ( object.isMesh || object.isLine || object.isPoints ) {
 		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();
 				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 = {
  * parameters = {
  *  color: <hex>,
  *  color: <hex>,
@@ -13,21 +5,28 @@ import {
  *  dashed: <boolean>,
  *  dashed: <boolean>,
  *  dashScale: <float>,
  *  dashScale: <float>,
  *  dashSize: <float>,
  *  dashSize: <float>,
- *  dashOffset: <float>,
  *  gapSize: <float>,
  *  gapSize: <float>,
  *  resolution: <Vector2>, // to be set by renderer
  *  resolution: <Vector2>, // to be set by renderer
  * }
  * }
  */
  */
 
 
+import {
+	ShaderLib,
+	ShaderMaterial,
+	UniformsLib,
+	UniformsUtils,
+	Vector2
+} from "../../../build/three.module.js";
+
+
 UniformsLib.line = {
 UniformsLib.line = {
 
 
+	worldUnits: { value: 1 },
 	linewidth: { value: 1 },
 	linewidth: { value: 1 },
 	resolution: { value: new Vector2( 1, 1 ) },
 	resolution: { value: new Vector2( 1, 1 ) },
 	dashScale: { value: 1 },
 	dashScale: { value: 1 },
 	dashSize: { 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
 		UniformsLib.line
 	] ),
 	] ),
 
 
-	vertexShader: /* glsl */`
+	vertexShader:
+		/* glsl */`
 		#include <common>
 		#include <common>
 		#include <color_pars_vertex>
 		#include <color_pars_vertex>
 		#include <fog_pars_vertex>
 		#include <fog_pars_vertex>
@@ -56,6 +56,9 @@ ShaderLib[ 'line' ] = {
 		attribute vec3 instanceColorEnd;
 		attribute vec3 instanceColorEnd;
 
 
 		varying vec2 vUv;
 		varying vec2 vUv;
+		varying vec4 worldPos;
+		varying vec3 worldStart;
+		varying vec3 worldEnd;
 
 
 		#ifdef USE_DASH
 		#ifdef USE_DASH
 
 
@@ -103,6 +106,9 @@ ShaderLib[ 'line' ] = {
 			vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
 			vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
 			vec4 end = modelViewMatrix * vec4( instanceEnd, 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
 			// 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
 			// 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
 			// 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;
 			vec4 clipEnd = projectionMatrix * end;
 
 
 			// ndc space
 			// 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
 			// direction
-			vec2 dir = ndcEnd - ndcStart;
+			vec2 dir = ndcEnd.xy - ndcStart.xy;
 
 
 			// account for clip-space aspect ratio
 			// account for clip-space aspect ratio
 			dir.x *= aspect;
 			dir.x *= aspect;
 			dir = normalize( dir );
 			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;
 			gl_Position = clip;
 
 
@@ -182,21 +246,26 @@ ShaderLib[ 'line' ] = {
 			#include <clipping_planes_vertex>
 			#include <clipping_planes_vertex>
 			#include <fog_vertex>
 			#include <fog_vertex>
 
 
-		}`,
+		}
+		`,
 
 
-	fragmentShader: /* glsl */`
+	fragmentShader:
+		/* glsl */`
 		uniform vec3 diffuse;
 		uniform vec3 diffuse;
 		uniform float opacity;
 		uniform float opacity;
+		uniform float linewidth;
 
 
 		#ifdef USE_DASH
 		#ifdef USE_DASH
 
 
 			uniform float dashSize;
 			uniform float dashSize;
-			uniform float dashOffset;
 			uniform float gapSize;
 			uniform float gapSize;
 
 
 		#endif
 		#endif
 
 
 		varying float vLineDistance;
 		varying float vLineDistance;
+		varying vec4 worldPos;
+		varying vec3 worldStart;
+		varying vec3 worldEnd;
 
 
 		#include <common>
 		#include <common>
 		#include <color_pars_fragment>
 		#include <color_pars_fragment>
@@ -206,6 +275,35 @@ ShaderLib[ 'line' ] = {
 
 
 		varying vec2 vUv;
 		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() {
 		void main() {
 
 
 			#include <clipping_planes_fragment>
 			#include <clipping_planes_fragment>
@@ -214,54 +312,90 @@ ShaderLib[ 'line' ] = {
 
 
 				if ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps
 				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
 			#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
 			#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
 			#endif
 
 
-			vec4 diffuseColor = vec4( diffuse, opacity * alpha );
+			vec4 diffuseColor = vec4( diffuse, alpha );
 
 
 			#include <logdepthbuf_fragment>
 			#include <logdepthbuf_fragment>
 			#include <color_fragment>
 			#include <color_fragment>
 
 
-			gl_FragColor = diffuseColor;
+			gl_FragColor = vec4( diffuseColor.rgb, alpha );
 
 
 			#include <tonemapping_fragment>
 			#include <tonemapping_fragment>
 			#include <encodings_fragment>
 			#include <encodings_fragment>
 			#include <fog_fragment>
 			#include <fog_fragment>
 			#include <premultiplied_alpha_fragment>
 			#include <premultiplied_alpha_fragment>
 
 
-		}`
-
+		}
+		`
 };
 };
 
 
 class LineMaterial extends ShaderMaterial {
 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: {
 			linewidth: {
 
 
 				enumerable: true,
 				enumerable: true,

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

@@ -624,6 +624,7 @@ class FBXTreeParser {
 						parameters.map.encoding = sRGBEncoding;
 						parameters.map.encoding = sRGBEncoding;
 
 
 					}
 					}
+
 					break;
 					break;
 
 
 				case 'DisplacementColor':
 				case 'DisplacementColor':
@@ -637,6 +638,7 @@ class FBXTreeParser {
 						parameters.emissiveMap.encoding = sRGBEncoding;
 						parameters.emissiveMap.encoding = sRGBEncoding;
 
 
 					}
 					}
+
 					break;
 					break;
 
 
 				case 'NormalMap':
 				case 'NormalMap':
@@ -652,6 +654,7 @@ class FBXTreeParser {
 						parameters.envMap.encoding = sRGBEncoding;
 						parameters.envMap.encoding = sRGBEncoding;
 
 
 					}
 					}
+
 					break;
 					break;
 
 
 				case 'SpecularColor':
 				case 'SpecularColor':
@@ -661,6 +664,7 @@ class FBXTreeParser {
 						parameters.specularMap.encoding = sRGBEncoding;
 						parameters.specularMap.encoding = sRGBEncoding;
 
 
 					}
 					}
+
 					break;
 					break;
 
 
 				case 'TransparentColor':
 				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,
 	Points,
 	PointsMaterial,
 	PointsMaterial,
 	PropertyBinding,
 	PropertyBinding,
+	Quaternion,
 	QuaternionKeyframeTrack,
 	QuaternionKeyframeTrack,
 	RGBFormat,
 	RGBFormat,
 	RepeatWrapping,
 	RepeatWrapping,
@@ -1405,9 +1406,9 @@ class GLTFMeshStandardSGMaterial extends MeshStandardMaterial {
 			'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );',
 			'material.diffuseColor = diffuseColor.rgb * ( 1. - max( specularFactor.r, max( specularFactor.g, specularFactor.b ) ) );',
 			'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );',
 			'vec3 dxy = max( abs( dFdx( geometryNormal ) ), abs( dFdy( geometryNormal ) ) );',
 			'float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );',
 			'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;',
 			'material.specularColor = specularFactor;',
 		].join( '\n' );
 		].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 ************/
 /********** INTERNALS ************/
 /*********************************/
 /*********************************/
@@ -3100,6 +3118,7 @@ class GLTFParser {
 
 
 		} else {
 		} else {
 
 
+			materialParams.format = RGBFormat;
 			materialParams.transparent = false;
 			materialParams.transparent = false;
 
 
 			if ( alphaMode === ALPHA_MODES.MASK ) {
 			if ( alphaMode === ALPHA_MODES.MASK ) {
@@ -3615,7 +3634,9 @@ class GLTFParser {
 							// representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
 							// representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
 							// must be divided by three to get the interpolant's sampleSize argument.
 							// 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,
 	IFCRELAGGREGATES,
 	IFCRELCONTAINEDINSPATIALSTRUCTURE,
 	IFCRELCONTAINEDINSPATIALSTRUCTURE,
 	IFCRELDEFINESBYPROPERTIES,
 	IFCRELDEFINESBYPROPERTIES,
+	IFCRELASSOCIATESMATERIAL,
 	IFCRELDEFINESBYTYPE,
 	IFCRELDEFINESBYTYPE,
 	IFCPROJECT,
 	IFCPROJECT,
-	IfcAPI,
-} from "./ifc/web-ifc-api.js";
+	IfcAPI
+} from './ifc/web-ifc-api.js';
 import {
 import {
 	BufferAttribute,
 	BufferAttribute,
-	BufferGeometry,
 	Mesh,
 	Mesh,
 	Matrix4,
 	Matrix4,
+	BufferGeometry,
 	Color,
 	Color,
 	MeshLambertMaterial,
 	MeshLambertMaterial,
 	DoubleSide,
 	DoubleSide,
-	Group,
 	Loader,
 	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 = {
 const PropsNames = {
 	aggregates: {
 	aggregates: {
 		name: IFCRELAGGREGATES,
 		name: IFCRELAGGREGATES,
-		relating: "RelatingObject",
-		related: "RelatedObjects",
-		key: "children",
+		relating: 'RelatingObject',
+		related: 'RelatedObjects',
+		key: 'children'
 	},
 	},
 	spatial: {
 	spatial: {
 		name: IFCRELCONTAINEDINSPATIALSTRUCTURE,
 		name: IFCRELCONTAINEDINSPATIALSTRUCTURE,
-		relating: "RelatingStructure",
-		related: "RelatedElements",
-		key: "children",
+		relating: 'RelatingStructure',
+		related: 'RelatedElements',
+		key: 'children'
 	},
 	},
 	psets: {
 	psets: {
 		name: IFCRELDEFINESBYPROPERTIES,
 		name: IFCRELDEFINESBYPROPERTIES,
-		relating: "RelatingPropertyDefinition",
-		related: "RelatedObjects",
-		key: "hasPsets",
+		relating: 'RelatingPropertyDefinition',
+		related: 'RelatedObjects',
+		key: 'hasPsets'
+	},
+	materials: {
+		name: IFCRELASSOCIATESMATERIAL,
+		relating: 'RelatingMaterial',
+		related: 'RelatedObjects',
+		key: 'hasMaterial'
 	},
 	},
 	type: {
 	type: {
 		name: IFCRELDEFINESBYTYPE,
 		name: IFCRELDEFINESBYTYPE,
-		relating: "RelatingType",
-		related: "RelatedObjects",
-		key: "hasType",
-	},
+		relating: 'RelatingType',
+		related: 'RelatedObjects',
+		key: 'hasType'
+	}
 };
 };
 
 
 class IFCParser {
 class IFCParser {
-	constructor(state) {
-		this.currentID = -1;
+
+	constructor( state, BVH ) {
+
 		this.state = state;
 		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: {},
 			mesh: {},
 			items: {},
 			items: {},
 			types: {},
 			types: {},
+			jsonData: {}
 		};
 		};
-		return modelID;
+
 	}
 	}
 
 
 	loadAllGeometry() {
 	loadAllGeometry() {
+
 		this.saveAllPlacedGeometriesByMaterial();
 		this.saveAllPlacedGeometriesByMaterial();
 		return this.generateAllGeometriesByMaterial();
 		return this.generateAllGeometriesByMaterial();
+
 	}
 	}
 
 
 	generateAllGeometriesByMaterial() {
 	generateAllGeometriesByMaterial() {
+
 		const { geometry, materials } = this.getGeometryAndMaterials();
 		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;
 		return mesh;
+
 	}
 	}
 
 
 	getGeometryAndMaterials() {
 	getGeometryAndMaterials() {
-		const items = this.state.models[this.currentID].items;
+
+		const items = this.state.models[ this.currentModelID ].items;
 		const mergedByMaterial = [];
 		const mergedByMaterial = [];
 		const materials = [];
 		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 {
 		return {
 			geometry,
 			geometry,
-			materials,
+			materials
 		};
 		};
+
 	}
 	}
 
 
 	saveAllPlacedGeometriesByMaterial() {
 	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;
 			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();
 		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 vData = geometry.GetVertexData();
 		const vDataSize = geometry.GetVertexDataSize();
 		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 iData = geometry.GetIndexData();
 		const iDataSize = geometry.GetIndexDataSize();
 		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();
 		const mat = new Matrix4();
-		mat.fromArray(matrix);
+		mat.fromArray( matrix );
 		return mat;
 		return mat;
+
 	}
 	}
 
 
-	ifcGeomToBufferGeom(vertices, normals, indexData) {
+	ifcGeomToBufferGeom( vertices, normals, indexData ) {
+
 		const geometry = new BufferGeometry();
 		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;
 		return geometry;
+
 	}
 	}
 
 
-	extractVertexData(vertexData) {
+	extractVertexData( vertexData ) {
+
 		const vertices = [];
 		const vertices = [];
 		const normals = [];
 		const normals = [];
 		let isNormalData = false;
 		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 {
 		return {
 			vertices,
 			vertices,
-			normals,
+			normals
 		};
 		};
+
 	}
 	}
 
 
-	saveGeometryByMaterial(geom, placedGeom, id) {
+	saveGeometryByMaterial( geom, placedGeom, id ) {
+
 		const color = placedGeom.color;
 		const color = placedGeom.color;
 		const colorID = `${color.x}${color.y}${color.z}${color.w}`;
 		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 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,
 			color: col,
-			side: DoubleSide,
-		});
+			side: DoubleSide
+		} );
 		newMaterial.transparent = color.w !== 1;
 		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,
 			material: newMaterial,
-			geometries: {},
+			geometries: {}
 		};
 		};
+
 	}
 	}
+
 }
 }
 
 
 class SubsetManager {
 class SubsetManager {
-	constructor(state) {
-		this.state = state;
+
+	constructor( state, BVH ) {
+
 		this.selected = {};
 		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;
 		mesh.modelID = config.modelID;
-		config.scene.add(mesh);
+		config.scene.add( mesh );
 		return 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;
 		return item != undefined && item != null;
+
 	}
 	}
 
 
-	getGeomAndMat(filtered) {
+	getGeomAndMat( filtered ) {
+
 		const geomsByMaterial = [];
 		const geomsByMaterial = [];
 		const materials = [];
 		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 {
 		return {
 			geomsByMaterial,
 			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
 		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 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;
 		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 = {};
 		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;
 		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 {
 				return {
 					...obj,
 					...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;
 			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;
 		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 = {
 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 {
 class PropertyManager {
-	constructor(state) {
+
+	constructor( state ) {
+
 		this.state = state;
 		this.state = state;
+
 	}
 	}
 
 
-	getExpressId(geometry, faceIndex) {
-		if (!geometry.index) return;
+	getExpressId( geometry, faceIndex ) {
+
+		if ( ! geometry.index )
+			return;
 		const geoIndex = geometry.index.array;
 		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 {
 		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 };
 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
  * 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.
  * 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:
  * References:
  * - KTX: http://github.khronos.org/KTX-Specification/
  * - KTX: http://github.khronos.org/KTX-Specification/
  * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
  * - DFD: https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html#basicdescriptor
@@ -16,33 +13,43 @@
 
 
 import {
 import {
 	CompressedTexture,
 	CompressedTexture,
-	CompressedTextureLoader,
 	FileLoader,
 	FileLoader,
 	LinearEncoding,
 	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,
 	sRGBEncoding,
+	UnsignedByteType
 } from '../../../build/three.module.js';
 } 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 ) {
 	constructor( manager ) {
 
 
 		super( 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' ) {
 		if ( typeof MSC_TRANSCODER !== 'undefined' ) {
 
 
@@ -59,15 +66,15 @@ class KTX2Loader extends CompressedTextureLoader {
 
 
 	setTranscoderPath( path ) {
 	setTranscoderPath( path ) {
 
 
-		this.basisLoader.setTranscoderPath( path );
+		this.transcoderPath = path;
 
 
 		return this;
 		return this;
 
 
 	}
 	}
 
 
-	setWorkerLimit( path ) {
+	setWorkerLimit( num ) {
 
 
-		this.basisLoader.setWorkerLimit( path );
+		this.workerPool.setWorkerLimit( num );
 
 
 		return this;
 		return this;
 
 
@@ -75,7 +82,15 @@ class KTX2Loader extends CompressedTextureLoader {
 
 
 	detectSupport( renderer ) {
 	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;
 		return this;
 
 
@@ -83,200 +98,468 @@ class KTX2Loader extends CompressedTextureLoader {
 
 
 	dispose() {
 	dispose() {
 
 
-		this.basisLoader.dispose();
+		this.workerPool.dispose();
+		if ( this.workerSourceURL ) URL.revokeObjectURL( this.workerSourceURL );
 
 
 		return this;
 		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 ) {
 	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.copy( _texture );
 					texture.needsUpdate = true;
 					texture.needsUpdate = true;
 
 
 					if ( onLoad ) onLoad( texture );
 					if ( onLoad ) onLoad( texture );
 
 
-				}, onError );
+				} )
+				.catch( onError );
 
 
-			} )
-			.catch( onError );
+		}, onProgress, onError );
 
 
 		return texture;
 		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 };
 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:
 					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 ++ ) {
 						for ( let j = 0; j < numElements; j ++ ) {
 
 
@@ -402,8 +402,8 @@ class RGBELoader extends DataTextureLoader {
 
 
 					case HalfFloatType:
 					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 ++ ) {
 						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 ) {
 				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 + 0 ] = ( color & 0x7C00 ) >> 7;
 					imageData[ ( x + width * y ) * 4 + 1 ] = ( color & 0x03E0 ) >> 2;
 					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;
 					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