Browse Source

Merge pull request #3 from mrdoob/dev

Merge with repo
Rik Cabanier 4 years ago
parent
commit
a8f0bebeff
100 changed files with 2910 additions and 846 deletions
  1. 1 2
      .github/CONTRIBUTING.md
  2. 3 0
      .github/FUNDING.yml
  3. 496 95
      build/three.js
  4. 0 0
      build/three.min.js
  5. 224 172
      build/three.module.js
  6. 5 0
      docs/api/ar/animation/AnimationClip.html
  7. 5 0
      docs/api/en/animation/AnimationClip.html
  8. 3 0
      docs/api/en/core/BufferGeometry.html
  9. 3 0
      docs/api/en/core/Object3D.html
  10. 5 2
      docs/api/en/loaders/ObjectLoader.html
  11. 7 0
      docs/api/en/materials/MeshPhysicalMaterial.html
  12. 2 2
      docs/api/en/math/Matrix4.html
  13. 1 1
      docs/api/en/textures/DataTexture.html
  14. 5 0
      docs/api/zh/animation/AnimationClip.html
  15. 3 0
      docs/api/zh/core/BufferGeometry.html
  16. 3 0
      docs/api/zh/core/Object3D.html
  17. 5 2
      docs/api/zh/loaders/ObjectLoader.html
  18. 7 0
      docs/api/zh/materials/MeshPhysicalMaterial.html
  19. 1 0
      docs/examples/en/loaders/GLTFLoader.html
  20. 2 2
      docs/examples/en/math/MeshSurfaceSampler.html
  21. 1 0
      docs/examples/zh/loaders/GLTFLoader.html
  22. 2 2
      docs/examples/zh/math/MeshSurfaceSampler.html
  23. 2 2
      docs/manual/ar/introduction/How-to-update-things.html
  24. 2 4
      docs/manual/ar/introduction/Installation.html
  25. 1 1
      docs/manual/en/introduction/How-to-dispose-of-objects.html
  26. 2 2
      docs/manual/en/introduction/How-to-update-things.html
  27. 2 4
      docs/manual/en/introduction/Installation.html
  28. 2 2
      docs/manual/zh/introduction/How-to-update-things.html
  29. 4 6
      docs/manual/zh/introduction/Installation.html
  30. 4 54
      editor/js/Editor.js
  31. 48 12
      editor/js/Loader.js
  32. 2 4
      editor/js/Menubar.File.js
  33. 5 5
      editor/js/Sidebar.Animation.js
  34. 112 20
      editor/js/Sidebar.Material.js
  35. 6 0
      editor/js/Strings.js
  36. 6 0
      editor/js/Viewport.js
  37. 2 1
      editor/sw.js
  38. 1 1
      examples/files.json
  39. 10 10
      examples/index.html
  40. 69 20
      examples/js/controls/DragControls.js
  41. 3 5
      examples/js/controls/OrbitControls.js
  42. 84 32
      examples/js/exporters/DRACOExporter.js
  43. 61 5
      examples/js/exporters/GLTFExporter.js
  44. 70 0
      examples/js/exporters/OBJExporter.js
  45. 1 1
      examples/js/exporters/PLYExporter.js
  46. 14 1
      examples/js/loaders/3MFLoader.js
  47. 7 1
      examples/js/loaders/ColladaLoader.js
  48. 115 78
      examples/js/loaders/OBJLoader.js
  49. 58 18
      examples/js/loaders/TDSLoader.js
  50. 12 0
      examples/js/postprocessing/EffectComposer.js
  51. 69 20
      examples/jsm/controls/DragControls.js
  52. 3 5
      examples/jsm/controls/OrbitControls.js
  53. 3 3
      examples/jsm/exporters/DRACOExporter.d.ts
  54. 84 32
      examples/jsm/exporters/DRACOExporter.js
  55. 63 6
      examples/jsm/exporters/GLTFExporter.js
  56. 72 0
      examples/jsm/exporters/OBJExporter.js
  57. 1 1
      examples/jsm/exporters/PLYExporter.js
  58. 39 5
      examples/jsm/loaders/3DMLoader.js
  59. 14 1
      examples/jsm/loaders/3MFLoader.js
  60. 0 2
      examples/jsm/loaders/ColladaLoader.d.ts
  61. 7 1
      examples/jsm/loaders/ColladaLoader.js
  62. 4 0
      examples/jsm/loaders/GLTFLoader.d.ts
  63. 3 2
      examples/jsm/loaders/LottieLoader.js
  64. 115 78
      examples/jsm/loaders/OBJLoader.js
  65. 58 18
      examples/jsm/loaders/TDSLoader.js
  66. 3 2
      examples/jsm/math/MeshSurfaceSampler.d.ts
  67. 30 5
      examples/jsm/math/MeshSurfaceSampler.js
  68. 54 48
      examples/jsm/modifiers/CurveModifier.js
  69. 1 0
      examples/jsm/postprocessing/EffectComposer.d.ts
  70. 12 0
      examples/jsm/postprocessing/EffectComposer.js
  71. 10 8
      examples/jsm/postprocessing/SAOPass.d.ts
  72. 3 1
      examples/jsm/postprocessing/SSAARenderPass.d.ts
  73. 11 9
      examples/jsm/postprocessing/SSAOPass.d.ts
  74. 1 1
      examples/misc_exporter_draco.html
  75. 18 2
      examples/misc_exporter_gltf.html
  76. 23 2
      examples/misc_exporter_obj.html
  77. 18 0
      examples/models/gltf/MaterialsVariantsShoe/README.md
  78. BIN
      examples/models/gltf/MaterialsVariantsShoe/glTF/MaterialsVariantsShoe.bin
  79. 393 0
      examples/models/gltf/MaterialsVariantsShoe/glTF/MaterialsVariantsShoe.gltf
  80. BIN
      examples/models/gltf/MaterialsVariantsShoe/glTF/diffuseBeach.jpg
  81. BIN
      examples/models/gltf/MaterialsVariantsShoe/glTF/diffuseMidnight.jpg
  82. BIN
      examples/models/gltf/MaterialsVariantsShoe/glTF/diffuseStreet.jpg
  83. BIN
      examples/models/gltf/MaterialsVariantsShoe/glTF/normal.jpg
  84. BIN
      examples/models/gltf/MaterialsVariantsShoe/glTF/occlusionRougnessMetalness.jpg
  85. BIN
      examples/models/gltf/MaterialsVariantsShoe/screenshot/screenshot.jpg
  86. BIN
      examples/screenshots/webgl_framebuffer_texture.jpg
  87. BIN
      examples/screenshots/webgl_loader_gltf_variants.jpg
  88. BIN
      examples/screenshots/webgl_postprocessing_ssaa.jpg
  89. BIN
      examples/screenshots/webgl_postprocessing_ssaa_unbiased.jpg
  90. 2 2
      examples/tags.json
  91. 0 1
      examples/webgl_decals.html
  92. 133 14
      examples/webgl_framebuffer_texture.html
  93. 1 1
      examples/webgl_loader_collada_skinning.html
  94. 170 0
      examples/webgl_loader_gltf_variants.html
  95. 0 1
      examples/webgl_loader_nodes.html
  96. 1 1
      examples/webgl_loader_texture_rgbm.html
  97. 0 1
      examples/webgl_materials_compile.html
  98. 0 2
      examples/webgl_materials_nodes.html
  99. 1 1
      examples/webgl_modifier_curve.html
  100. 1 1
      examples/webgl_modifier_curve_instanced.html

+ 1 - 2
.github/CONTRIBUTING.md

@@ -40,7 +40,7 @@ Many linting errors can be fixed automatically by running
 
 
 If you’d like to make a minified version of the build files i.e. ‘build/three.min.js’ run:
 If you’d like to make a minified version of the build files i.e. ‘build/three.min.js’ run:
         
         
-    npm run-script build-closure
+    npm run build
 
 
 ## Making changes
 ## Making changes
 
 
@@ -75,7 +75,6 @@ When you’ve decided to make changes, start with the following:
 
 
       npm run make-screenshot <example_1_name> ...<example_N_name>
       npm run make-screenshot <example_1_name> ...<example_N_name>
 
 
-* Watch out for Closure compiler warnings when building the libs, there should not be any.
 * Once done with a patch / feature do not add more commits to a feature branch
 * Once done with a patch / feature do not add more commits to a feature branch
 * Create separate branches per patch or feature.
 * Create separate branches per patch or feature.
 * If you make a PR but it is not actually ready to be pulled into the dev branch, add `[Draft]` into the PR title and/or convert it to a draft PR
 * If you make a PR but it is not actually ready to be pulled into the dev branch, add `[Draft]` into the PR title and/or convert it to a draft PR

+ 3 - 0
.github/FUNDING.yml

@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: [mrdoob, HumanInteractive]

File diff suppressed because it is too large
+ 496 - 95
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
+ 224 - 172
build/three.module.js


+ 5 - 0
docs/api/ar/animation/AnimationClip.html

@@ -72,6 +72,11 @@
 			يضبط [page:.duration duration] للمقطع على أطول مدة [page:KeyframeTrack] ممكنة.
 			يضبط [page:.duration duration] للمقطع على أطول مدة [page:KeyframeTrack] ممكنة.
 		</p>
 		</p>
 
 
+		<h3>[method:Object toJSON]()</h3>
+		<p>
+			Returns a JSON object representing the serialized animation clip.
+		</p>
+
 		<h3>[method:this trim]()</h3>
 		<h3>[method:this trim]()</h3>
 		<p>
 		<p>
 			اقتطاع كل المسارات حسب مدة المقطع.
 			اقتطاع كل المسارات حسب مدة المقطع.

+ 5 - 0
docs/api/en/animation/AnimationClip.html

@@ -82,6 +82,11 @@
 			[page:KeyframeTrack].
 			[page:KeyframeTrack].
 		</p>
 		</p>
 
 
+		<h3>[method:Object toJSON]()</h3>
+		<p>
+			Returns a JSON object representing the serialized animation clip.
+		</p>
+
 		<h3>[method:this trim]()</h3>
 		<h3>[method:this trim]()</h3>
 		<p>
 		<p>
 			Trims all tracks to the clip's duration.
 			Trims all tracks to the clip's duration.

+ 3 - 0
docs/api/en/core/BufferGeometry.html

@@ -233,6 +233,9 @@
 		<h3>[method:BufferAttribute getIndex] ()</h3>
 		<h3>[method:BufferAttribute getIndex] ()</h3>
 		<p>Return the [page:.index] buffer.</p>
 		<p>Return the [page:.index] buffer.</p>
 
 
+		<h3>[method:Boolean hasAttribute]( [param:String name] )</h3>
+		<p>Returns *true* if the attribute with the specified name exists.</p>
+
 		<h3>[method:BufferGeometry lookAt] ( [param:Vector3 vector] )</h3>
 		<h3>[method:BufferGeometry lookAt] ( [param:Vector3 vector] )</h3>
 		<p>
 		<p>
 		vector - A world vector to look at.<br /><br />
 		vector - A world vector to look at.<br /><br />

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

@@ -29,6 +29,9 @@
 
 
 		<h2>Properties</h2>
 		<h2>Properties</h2>
 
 
+		<h3>[property:AnimationClip animations]</h3>
+		<p>Array with object's animation clips.</p>
+
 		<h3>[property:Boolean castShadow]</h3>
 		<h3>[property:Boolean castShadow]</h3>
 		<p>Whether the object gets rendered into shadow map. Default is *false*.</p>
 		<p>Whether the object gets rendered into shadow map. Default is *false*.</p>
 
 

+ 5 - 2
docs/api/en/loaders/ObjectLoader.html

@@ -130,9 +130,12 @@
 		This is used [page:.parse] to parse any textures in the JSON structure.
 		This is used [page:.parse] to parse any textures in the JSON structure.
 		</p>
 		</p>
 
 
-		<h3>[method:Object3D parseObject]( [param:Object json] )</h3>
+		<h3>[method:Object3D parseObject]( [param:Object json], [param:BufferGeometry geometries], [param:Material materials], [param:AnimationClip animations] )</h3>
 		<p>
 		<p>
-		[page:Object json] — required. The JSON source to parse.<br /><br />
+		[page:Object json] — required. The JSON source to parse.<br />
+		[page:BufferGeometry geometries] — required. The geometries of the JSON.<br />
+		[page:Material materials] — required. The materials of the JSON.<br />
+		[page:AnimationClip animations] — required. The animations of the JSON.<br /><br />
 
 
 		This is used [page:.parse] to parse any objects in the JSON structure.
 		This is used [page:.parse] to parse any objects in the JSON structure.
 		Objects can be of the following types:
 		Objects can be of the following types:

+ 7 - 0
docs/api/en/materials/MeshPhysicalMaterial.html

@@ -61,6 +61,7 @@
 			[example:webgl_materials_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
+			[example:webgl_materials_physical_sheen materials / physical / sheen]<br />
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 		</p>
 		</p>
 
 
@@ -132,6 +133,12 @@
 			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>
+		<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.
+		</p>
+
 		<h3>[property:Float transmission]</h3>
 		<h3>[property:Float transmission]</h3>
 		<p>
 		<p>
 		Degree of transmission (or optical transparency), from *0.0* to *1.0*. Default is *0.0*.<br />
 		Degree of transmission (or optical transparency), from *0.0* to *1.0*. Default is *0.0*.<br />

+ 2 - 2
docs/api/en/math/Matrix4.html

@@ -138,8 +138,8 @@ m.elements = [ 11, 21, 31, 41,
 
 
 		<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>
-		Decomposes this matrix into it's [page:Vector3 position], [page:Quaternion quaternion] and
-		[page:Vector3 scale] components.
+		Decomposes this matrix into it's [page:Vector3 position], [page:Quaternion quaternion] and [page:Vector3 scale] components.
+		The method can't be used to decompose an object's world matrix if its parent has a non-uniform scale.
 		</p>
 		</p>
 
 
 		<h3>[method:Float determinant]()</h3>
 		<h3>[method:Float determinant]()</h3>

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

@@ -16,7 +16,7 @@
 
 
 		<h2>Constructor</h2>
 		<h2>Constructor</h2>
 
 
-		<h3>[name]( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy )</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 intially set to false.

+ 5 - 0
docs/api/zh/animation/AnimationClip.html

@@ -74,6 +74,11 @@
 			将剪辑的持续时间([page:.duration duration])设为最长的关键帧轨道([page:KeyframeTrack])的持续时间。
 			将剪辑的持续时间([page:.duration duration])设为最长的关键帧轨道([page:KeyframeTrack])的持续时间。
 		</p>
 		</p>
 
 
+		<h3>[method:Object toJSON]()</h3>
+		<p>
+			Returns a JSON object representing the serialized animation clip.
+		</p>
+
 		<h3>[method:this trim]()</h3>
 		<h3>[method:this trim]()</h3>
 		<p>
 		<p>
 			修剪所有的轨道到该剪辑的持续时间。
 			修剪所有的轨道到该剪辑的持续时间。

+ 3 - 0
docs/api/zh/core/BufferGeometry.html

@@ -222,6 +222,9 @@
 		<h3>[method:BufferAttribute getIndex] ()</h3>
 		<h3>[method:BufferAttribute getIndex] ()</h3>
 		<p>返回缓存相关的 [page:.index]。</p>
 		<p>返回缓存相关的 [page:.index]。</p>
 
 
+		<h3>[method:Boolean hasAttribute]( [param:String name] )</h3>
+		<p>Returns *true* if the attribute with the specified name exists.</p>
+
 		<h3>[method:BufferGeometry lookAt] ( [param:Vector3 vector] )</h3>
 		<h3>[method:BufferGeometry lookAt] ( [param:Vector3 vector] )</h3>
 		<p>
 		<p>
 			vector - 几何体所朝向的世界坐标。<br /><br />
 			vector - 几何体所朝向的世界坐标。<br /><br />

+ 3 - 0
docs/api/zh/core/Object3D.html

@@ -29,6 +29,9 @@
 
 
 	<h2>属性</h2>
 	<h2>属性</h2>
 
 
+	<h3>[property:AnimationClip animations]</h3>
+	<p>Array with object's animation clips.</p>
+
 	<h3>[property:Boolean castShadow]</h3>
 	<h3>[property:Boolean castShadow]</h3>
 	<p>对象是否被渲染到阴影贴图中。默认值为*false*。</p>
 	<p>对象是否被渲染到阴影贴图中。默认值为*false*。</p>
 
 

+ 5 - 2
docs/api/zh/loaders/ObjectLoader.html

@@ -128,9 +128,12 @@
 			此函数通过[page:.parse]来解析JSON结构中任意纹理。
 			此函数通过[page:.parse]来解析JSON结构中任意纹理。
 		</p>
 		</p>
 
 
-		<h3>[method:Object3D parseObject]( [param:Object json] )</h3>
+		<h3>[method:Object3D parseObject]( [param:Object json], [param:BufferGeometry geometries], [param:Material materials], [param:AnimationClip animations] )</h3>
 		<p>
 		<p>
-		[page:Object json] —  必选参数,需要被解析的JSON源。<br /><br />
+		[page:Object json] —  必选参数,需要被解析的JSON源。<br />
+		[page:BufferGeometry geometries] — required. The geometries of the JSON.<br />
+		[page:Material materials] — required. The materials of the JSON.<br />
+		[page:AnimationClip animations] — required. The animations of the JSON.<br /><br />
 
 
 			此函数通过[page:.parse]来解析JSON结构中任意对象。
 			此函数通过[page:.parse]来解析JSON结构中任意对象。
 		对象可以为如下类型:
 		对象可以为如下类型:

+ 7 - 0
docs/api/zh/materials/MeshPhysicalMaterial.html

@@ -62,6 +62,7 @@
 			[example:webgl_materials_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
+			[example:webgl_materials_physical_sheen materials / physical / sheen]<br />
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 		</p>
 		</p>
 
 
@@ -129,6 +130,12 @@
 			这模拟了非金属材质的反射率。当[page:MeshStandardMaterial]为*1.0*时,此属性无效。
 			这模拟了非金属材质的反射率。当[page:MeshStandardMaterial]为*1.0*时,此属性无效。
 		</p>
 		</p>
 
 
+		<h3>[property:Color sheen]</h3>
+		<p>
+			If a color is assigned to this property, the material will use a special sheen BRDF intended for rendering cloth materials such as velvet.
+			The sheen color provides the ability to create two-tone specular materials. *null* by default.
+		</p>
+
 		<h3>[property:Float transmission]</h3>
 		<h3>[property:Float transmission]</h3>
 		<p>
 		<p>
 		Degree of transmission (or optical transparency), from *0.0* to *1.0*. Default is *0.0*.<br />
 		Degree of transmission (or optical transparency), from *0.0* to *1.0*. Default is *0.0*.<br />

+ 1 - 0
docs/examples/en/loaders/GLTFLoader.html

@@ -37,6 +37,7 @@
 			<li>KHR_texture_basisu <i>(experimental)</i></li>
 			<li>KHR_texture_basisu <i>(experimental)</i></li>
 			<li>KHR_texture_transform<sup>2</sup></li>
 			<li>KHR_texture_transform<sup>2</sup></li>
 			<li>EXT_texture_webp</li>
 			<li>EXT_texture_webp</li>
+			<li>EXT_meshopt_compression</li>
 			<li>MSFT_texture_dds</li>
 			<li>MSFT_texture_dds</li>
 		</ul>
 		</ul>
 
 

+ 2 - 2
docs/examples/en/math/MeshSurfaceSampler.html

@@ -72,9 +72,9 @@
 		Processes the input geometry and prepares to return samples. Any configuration of the geometry or sampler must occur before this method is called. Time complexity is <i>O(n)</i> for a surface with <i>n</i> faces.
 		Processes the input geometry and prepares to return samples. Any configuration of the geometry or sampler must occur before this method is called. Time complexity is <i>O(n)</i> for a surface with <i>n</i> faces.
 		</p>
 		</p>
 
 
-		<h3>[method:this sample]( [param:Vector3 targetPosition], [param:Vector3 targetNormal] )</h3>
+		<h3>[method:this sample]( [param:Vector3 targetPosition], [param:Vector3 targetNormal], [param:Color targetColor] )</h3>
 		<p>
 		<p>
-		Selects a random point on the surface of the input geometry, returning the position and normal vector at that point. Time complexity is <i>O(log n)</i> for a surface with <i>n</i> faces.</i></p>
+		Selects a random point on the surface of the input geometry, returning the position and optionally the normal vector and color at that point. Time complexity is <i>O(log n)</i> for a surface with <i>n</i> faces.</i></p>
 
 
 		<h2>Source</h2>
 		<h2>Source</h2>
 
 

+ 1 - 0
docs/examples/zh/loaders/GLTFLoader.html

@@ -35,6 +35,7 @@
 			<li>KHR_texture_basisu <i>(experimental)</i></li>
 			<li>KHR_texture_basisu <i>(experimental)</i></li>
 			<li>KHR_texture_transform<sup>2</sup></li>
 			<li>KHR_texture_transform<sup>2</sup></li>
 			<li>EXT_texture_webp</li>
 			<li>EXT_texture_webp</li>
+			<li>EXT_meshopt_compression</li>
 			<li>MSFT_texture_dds</li>
 			<li>MSFT_texture_dds</li>
 		</ul>
 		</ul>
 
 

+ 2 - 2
docs/examples/zh/math/MeshSurfaceSampler.html

@@ -72,9 +72,9 @@
 		Processes the input geometry and prepares to return samples. Any configuration of the geometry or sampler must occur before this method is called. Time complexity is <i>O(n)</i> for a surface with <i>n</i> faces.
 		Processes the input geometry and prepares to return samples. Any configuration of the geometry or sampler must occur before this method is called. Time complexity is <i>O(n)</i> for a surface with <i>n</i> faces.
 		</p>
 		</p>
 
 
-		<h3>[method:this sample]( [param:Vector3 targetPosition], [param:Vector3 targetNormal] )</h3>
+		<h3>[method:this sample]( [param:Vector3 targetPosition], [param:Vector3 targetNormal], [param:Color targetColor] )</h3>
 		<p>
 		<p>
-		Selects a random point on the surface of the input geometry, returning the position and normal vector at that point. Time complexity is <i>O(log n)</i> for a surface with <i>n</i> faces.</i></p>
+		Selects a random point on the surface of the input geometry, returning the position and optionally the normal vector and color at that point. Time complexity is <i>O(log n)</i> for a surface with <i>n</i> faces.</i></p>
 
 
 		<h2>Source</h2>
 		<h2>Source</h2>
 
 

+ 2 - 2
docs/manual/ar/introduction/How-to-update-things.html

@@ -144,8 +144,8 @@ geometry.tangentsNeedUpdate = true;
 			</p>
 			</p>
 
 
 			<code>
 			<code>
-	//removed after r66
-	geometry.dynamic = true;
+// removed after r66
+geometry.dynamic = true;
 			</code>
 			</code>
 
 
 		</div>
 		</div>

+ 2 - 4
docs/manual/ar/introduction/Installation.html

@@ -42,14 +42,12 @@
 		</p>
 		</p>
 
 
 		<code>
 		<code>
-		///////////////////////////////////////////////////////
 		// Option 1: Import the entire three.js core library.
 		// Option 1: Import the entire three.js core library.
 		import * as THREE from 'three';
 		import * as THREE from 'three';
 
 
 		const scene = new THREE.Scene();
 		const scene = new THREE.Scene();
 
 
 
 
-		///////////////////////////////////////////////////////
 		// Option 2: Import just the parts you need.
 		// Option 2: Import just the parts you need.
 		import { Scene } from 'three';
 		import { Scene } from 'three';
 
 
@@ -91,7 +89,7 @@
 
 
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // redirect to the newest stable release.
 		  // redirect to the newest stable release.
-		  import * as THREE from 'https://unpkg.com/three@&lt;VERSION>/build/three.module.js';
+		  import * as THREE from 'https://unpkg.com/three/build/three.module.js';
 
 
 		  const scene = new THREE.Scene();
 		  const scene = new THREE.Scene();
 
 
@@ -129,7 +127,7 @@
 
 
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // redirect to the newest stable release.
 		  // redirect to the newest stable release.
-		  import { OrbitControls } from 'https://unpkg.com/three@&lt;VERSION>/examples/jsm/controls/OrbitControls.js';
+		  import { OrbitControls } from 'https://unpkg.com/three/examples/jsm/controls/OrbitControls.js';
 
 
 		  const controls = new OrbitControls();
 		  const controls = new OrbitControls();
 
 

+ 1 - 1
docs/manual/en/introduction/How-to-dispose-of-objects.html

@@ -80,7 +80,7 @@
 
 
 	<p>
 	<p>
 		Yes. It's possible to evaluate [page:WebGLRenderer.info], a special property of the renderer with a series of statistical information about the graphics board memory
 		Yes. It's possible to evaluate [page:WebGLRenderer.info], a special property of the renderer with a series of statistical information about the graphics board memory
-		and the rendering process. Among other things, it tells you have many textures, geometries and shader programs are internally stored. If you notice performance problems
+		and the rendering process. Among other things, it tells you how many textures, geometries and shader programs are internally stored. If you notice performance problems
 		in your application, it's a good idea to debug this property in order to easily identify a memory leak.
 		in your application, it's a good idea to debug this property in order to easily identify a memory leak.
 	</p>
 	</p>
 
 

+ 2 - 2
docs/manual/en/introduction/How-to-update-things.html

@@ -159,8 +159,8 @@ geometry.tangentsNeedUpdate = true;
 			</p>
 			</p>
 
 
 			<code>
 			<code>
-	//removed after r66
-	geometry.dynamic = true;
+// removed after r66
+geometry.dynamic = true;
 			</code>
 			</code>
 
 
 		</div>
 		</div>

+ 2 - 4
docs/manual/en/introduction/Installation.html

@@ -36,14 +36,12 @@
 		</p>
 		</p>
 
 
 		<code>
 		<code>
-		///////////////////////////////////////////////////////
 		// Option 1: Import the entire three.js core library.
 		// Option 1: Import the entire three.js core library.
 		import * as THREE from 'three';
 		import * as THREE from 'three';
 
 
 		const scene = new THREE.Scene();
 		const scene = new THREE.Scene();
 
 
 
 
-		///////////////////////////////////////////////////////
 		// Option 2: Import just the parts you need.
 		// Option 2: Import just the parts you need.
 		import { Scene } from 'three';
 		import { Scene } from 'three';
 
 
@@ -73,7 +71,7 @@
 
 
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // redirect to the newest stable release.
 		  // redirect to the newest stable release.
-		  import * as THREE from 'https://unpkg.com/three@&lt;VERSION>/build/three.module.js';
+		  import * as THREE from 'https://unpkg.com/three/build/three.module.js';
 
 
 		  const scene = new THREE.Scene();
 		  const scene = new THREE.Scene();
 
 
@@ -111,7 +109,7 @@
 
 
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // Find the latest version by visiting https://unpkg.com/three. The URL will
 		  // redirect to the newest stable release.
 		  // redirect to the newest stable release.
-		  import { OrbitControls } from 'https://unpkg.com/three@&lt;VERSION>/examples/jsm/controls/OrbitControls.js';
+		  import { OrbitControls } from 'https://unpkg.com/three/examples/jsm/controls/OrbitControls.js';
 
 
 		  const controls = new OrbitControls();
 		  const controls = new OrbitControls();
 
 

+ 2 - 2
docs/manual/zh/introduction/How-to-update-things.html

@@ -142,8 +142,8 @@ geometry.tangentsNeedUpdate = true;
 			</p>
 			</p>
 
 
 			<code>
 			<code>
-	//removed after r66
-	geometry.dynamic = true;
+// removed after r66
+geometry.dynamic = true;
 			</code>
 			</code>
 		</div>
 		</div>
 
 

+ 4 - 6
docs/manual/zh/introduction/Installation.html

@@ -36,14 +36,12 @@
 		</p>
 		</p>
 
 
 		<code>
 		<code>
-		///////////////////////////////////////////////////////
 		// 方式 1: 导入整个 three.js核心库
 		// 方式 1: 导入整个 three.js核心库
 		import * as THREE from 'three';
 		import * as THREE from 'three';
 
 
 		const scene = new THREE.Scene();
 		const scene = new THREE.Scene();
 
 
 
 
-		///////////////////////////////////////////////////////
 		// 方式 2: 仅导入你所需要的部分
 		// 方式 2: 仅导入你所需要的部分
 		import { Scene } from 'three';
 		import { Scene } from 'three';
 
 
@@ -71,9 +69,9 @@
 		<code>
 		<code>
 		&lt;script type="module">
 		&lt;script type="module">
 
 
-		  // 通过访问 https://unpkg.com/three 来查找最新版本。 
+		  // 通过访问 https://unpkg.com/three 来查找最新版本。
 		  // 该URL将会重定向到最新的稳定版本。
 		  // 该URL将会重定向到最新的稳定版本。
-		  import * as THREE from 'https://unpkg.com/three@&lt;VERSION>/build/three.module.js';
+		  import * as THREE from 'https://unpkg.com/three/build/three.module.js';
 
 
 		  const scene = new THREE.Scene();
 		  const scene = new THREE.Scene();
 
 
@@ -109,9 +107,9 @@
 		<code>
 		<code>
 		&lt;script type="module">
 		&lt;script type="module">
 
 
-		  // 通过访问 https://unpkg.com/three 来查找最新版本。 
+		  // 通过访问 https://unpkg.com/three 来查找最新版本。
 		  // 该URL将会重定向到最新的稳定版本。
 		  // 该URL将会重定向到最新的稳定版本。
-		  import { OrbitControls } from 'https://unpkg.com/three@&lt;VERSION>/examples/jsm/controls/OrbitControls.js';
+		  import { OrbitControls } from 'https://unpkg.com/three/examples/jsm/controls/OrbitControls.js';
 
 
 		  const controls = new OrbitControls();
 		  const controls = new OrbitControls();
 
 

+ 4 - 54
editor/js/Editor.js

@@ -79,7 +79,9 @@ function Editor() {
 		refreshSidebarObject3D: new Signal(),
 		refreshSidebarObject3D: new Signal(),
 		historyChanged: new Signal(),
 		historyChanged: new Signal(),
 
 
-		viewportCameraChanged: new Signal()
+		viewportCameraChanged: new Signal(),
+
+		animationStopped: new Signal()
 
 
 	};
 	};
 
 
@@ -105,7 +107,6 @@ function Editor() {
 
 
 	this.materialsRefCounter = new Map(); // tracks how often is a material used by a 3D object
 	this.materialsRefCounter = new Map(); // tracks how often is a material used by a 3D object
 
 
-	this.animations = {};
 	this.mixer = new THREE.AnimationMixer( this.scene );
 	this.mixer = new THREE.AnimationMixer( this.scene );
 
 
 	this.selected = null;
 	this.selected = null;
@@ -357,16 +358,6 @@ Editor.prototype = {
 
 
 	},
 	},
 
 
-	addAnimations: function ( object, animations ) {
-
-		if ( animations.length > 0 ) {
-
-			this.animations[ object.uuid ] = animations;
-
-		}
-
-	},
-
 	//
 	//
 
 
 	addCamera: function ( camera ) {
 	addCamera: function ( camera ) {
@@ -664,50 +655,10 @@ Editor.prototype = {
 
 
 		} );
 		} );
 
 
-		// animations
-
-		var animationsJSON = json.animations;
-
-		for ( var i = 0; i < animationsJSON.length; i ++ ) {
-
-			var objectJSON = animationsJSON[ i ];
-			var animations = [];
-
-			for ( var j = 0; j < objectJSON.animations.length; j ++ ) {
-
-				animations.push( THREE.AnimationClip.parse( objectJSON.animations[ j ] ) );
-
-			}
-
-			this.animations[ objectJSON.uuid ] = animations;
-
-		}
-
 	},
 	},
 
 
 	toJSON: function () {
 	toJSON: function () {
 
 
-		// animations
-
-		var animations = this.animations;
-		var animationsJSON = [];
-
-		for ( var entry in animations ) {
-
-			var objectAnimations = animations[ entry ];
-			var objectJSON = { uuid: entry, animations: [] };
-
-			for ( var i = 0; i < objectAnimations.length; i ++ ) {
-
-				var objectAnimation = objectAnimations[ i ];
-				objectJSON.animations.push( THREE.AnimationClip.toJSON( objectAnimation ) );
-
-			}
-
-			animationsJSON.push( objectJSON );
-
-		}
-
 		// scripts clean up
 		// scripts clean up
 
 
 		var scene = this.scene;
 		var scene = this.scene;
@@ -741,8 +692,7 @@ Editor.prototype = {
 			camera: this.camera.toJSON(),
 			camera: this.camera.toJSON(),
 			scene: this.scene.toJSON(),
 			scene: this.scene.toJSON(),
 			scripts: this.scripts,
 			scripts: this.scripts,
-			history: this.history.toJSON(),
-			animations: animationsJSON
+			history: this.history.toJSON()
 
 
 		};
 		};
 
 

+ 48 - 12
editor/js/Loader.js

@@ -1,5 +1,6 @@
 import * as THREE from '../../build/three.module.js';
 import * as THREE from '../../build/three.module.js';
 
 
+import { Rhino3dmLoader } from '../../examples/jsm/loaders/3DMLoader.js';
 import { ThreeMFLoader } from '../../examples/jsm/loaders/3MFLoader.js';
 import { ThreeMFLoader } from '../../examples/jsm/loaders/3MFLoader.js';
 import { AMFLoader } from '../../examples/jsm/loaders/AMFLoader.js';
 import { AMFLoader } from '../../examples/jsm/loaders/AMFLoader.js';
 import { ColladaLoader } from '../../examples/jsm/loaders/ColladaLoader.js';
 import { ColladaLoader } from '../../examples/jsm/loaders/ColladaLoader.js';
@@ -16,7 +17,7 @@ import { SVGLoader } from '../../examples/jsm/loaders/SVGLoader.js';
 import { TDSLoader } from '../../examples/jsm/loaders/TDSLoader.js';
 import { TDSLoader } from '../../examples/jsm/loaders/TDSLoader.js';
 import { VTKLoader } from '../../examples/jsm/loaders/VTKLoader.js';
 import { VTKLoader } from '../../examples/jsm/loaders/VTKLoader.js';
 import { VRMLLoader } from '../../examples/jsm/loaders/VRMLLoader.js';
 import { VRMLLoader } from '../../examples/jsm/loaders/VRMLLoader.js';
-import { Rhino3dmLoader } from '../../examples/jsm/loaders/3DMLoader.js';
+import { XYZLoader } from '../../examples/jsm/loaders/XYZLoader.js';
 
 
 import { TGALoader } from '../../examples/jsm/loaders/TGALoader.js';
 import { TGALoader } from '../../examples/jsm/loaders/TGALoader.js';
 
 
@@ -169,7 +170,6 @@ function Loader( editor ) {
 
 
 					collada.scene.name = filename;
 					collada.scene.name = filename;
 
 
-					editor.addAnimations( collada.scene, collada.animations );
 					editor.execute( new AddObjectCommand( editor, collada.scene ) );
 					editor.execute( new AddObjectCommand( editor, collada.scene ) );
 
 
 				}, false );
 				}, false );
@@ -187,12 +187,27 @@ function Loader( editor ) {
 					loader.setDecoderPath( '../examples/js/libs/draco/' );
 					loader.setDecoderPath( '../examples/js/libs/draco/' );
 					loader.decodeDracoFile( contents, function ( geometry ) {
 					loader.decodeDracoFile( contents, function ( geometry ) {
 
 
-						var material = new THREE.MeshStandardMaterial();
+						var object;
+
+						if ( geometry.index !== null ) {
+
+							var material = new THREE.MeshStandardMaterial();
+
+							object = new THREE.Mesh( geometry, material );
+							object.name = filename;
+
+						} else {
+
+							var material = new THREE.PointsMaterial( { size: 0.01 } );
 
 
-						var mesh = new THREE.Mesh( geometry, material );
-						mesh.name = filename;
+							if ( geometry.hasAttribute( 'color' ) === true ) material.vertexColors = true;
 
 
-						editor.execute( new AddObjectCommand( editor, mesh ) );
+							object = new THREE.Points( geometry, material );
+							object.name = filename;
+
+						}
+
+						editor.execute( new AddObjectCommand( editor, object ) );
 
 
 					} );
 					} );
 
 
@@ -210,7 +225,6 @@ function Loader( editor ) {
 					var loader = new FBXLoader( manager );
 					var loader = new FBXLoader( manager );
 					var object = loader.parse( contents );
 					var object = loader.parse( contents );
 
 
-					editor.addAnimations( object, object.animations );
 					editor.execute( new AddObjectCommand( editor, object ) );
 					editor.execute( new AddObjectCommand( editor, object ) );
 
 
 				}, false );
 				}, false );
@@ -234,7 +248,7 @@ function Loader( editor ) {
 						var scene = result.scene;
 						var scene = result.scene;
 						scene.name = filename;
 						scene.name = filename;
 
 
-						editor.addAnimations( scene, result.animations );
+						scene.animations.push( ...result.animations );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 
 
 					} );
 					} );
@@ -271,7 +285,7 @@ function Loader( editor ) {
 						var scene = result.scene;
 						var scene = result.scene;
 						scene.name = filename;
 						scene.name = filename;
 
 
-						editor.addAnimations( scene, result.animations );
+						scene.animations.push( ...result.animations );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 
 
 					} );
 					} );
@@ -370,7 +384,7 @@ function Loader( editor ) {
 					mesh.mixer = new THREE.AnimationMixer( mesh );
 					mesh.mixer = new THREE.AnimationMixer( mesh );
 					mesh.name = filename;
 					mesh.name = filename;
 
 
-					editor.addAnimations( mesh, geometry.animations );
+					mesh.animations.push( ...geometry.animations );
 					editor.execute( new AddObjectCommand( editor, mesh ) );
 					editor.execute( new AddObjectCommand( editor, mesh ) );
 
 
 				}, false );
 				}, false );
@@ -530,6 +544,28 @@ function Loader( editor ) {
 
 
 				break;
 				break;
 
 
+			case 'xyz':
+
+				reader.addEventListener( 'load', function ( event ) {
+
+					var contents = event.target.result;
+
+					var geometry = new XYZLoader().parse( contents );
+
+					var material = new THREE.PointsMaterial();
+
+					if ( geometry.hasAttribute( 'color' ) === true ) material.vertexColors = true;
+
+					var points = new THREE.Points( geometry, material );
+					points.name = filename;
+
+					editor.execute( new AddObjectCommand( editor, points ) );
+
+				}, false );
+				reader.readAsText( file );
+
+				break;
+
 			case 'zip':
 			case 'zip':
 
 
 				reader.addEventListener( 'load', function ( event ) {
 				reader.addEventListener( 'load', function ( event ) {
@@ -682,7 +718,7 @@ function Loader( editor ) {
 
 
 						var scene = result.scene;
 						var scene = result.scene;
 
 
-						editor.addAnimations( scene, result.animations );
+						scene.animations.push( ...result.animations );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 
 
 					} );
 					} );
@@ -700,7 +736,7 @@ function Loader( editor ) {
 
 
 						var scene = result.scene;
 						var scene = result.scene;
 
 
-						editor.addAnimations( scene, result.animations );
+						scene.animations.push( ...result.animations );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 						editor.execute( new AddObjectCommand( editor, scene ) );
 
 
 					} );
 					} );

+ 2 - 4
editor/js/Menubar.File.js

@@ -232,7 +232,7 @@ function MenubarFile( editor ) {
 		var exporter = new DRACOExporter();
 		var exporter = new DRACOExporter();
 
 
 		// TODO: Change to DRACOExporter's parse( geometry, onParse )?
 		// TODO: Change to DRACOExporter's parse( geometry, onParse )?
-		var result = exporter.parse( object.geometry );
+		var result = exporter.parse( object );
 		saveArrayBuffer( result, 'model.drc' );
 		saveArrayBuffer( result, 'model.drc' );
 
 
 	} );
 	} );
@@ -483,9 +483,7 @@ function MenubarFile( editor ) {
 
 
 		scene.traverse( function ( object ) {
 		scene.traverse( function ( object ) {
 
 
-			var objectAnimations = editor.animations[ object.uuid ];
-
-			if ( objectAnimations !== undefined ) animations.push( ... objectAnimations );
+			animations.push( ... object.animations );
 
 
 		} );
 		} );
 
 

+ 5 - 5
editor/js/Sidebar.Animation.js

@@ -10,9 +10,9 @@ function SidebarAnimation( editor ) {
 
 
 	signals.objectSelected.add( function ( object ) {
 	signals.objectSelected.add( function ( object ) {
 
 
-		var animations = editor.animations[ object !== null ? object.uuid : '' ];
+		if ( object !== null && object.animations.length > 0 ) {
 
 
-		if ( animations !== undefined ) {
+			var animations = object.animations;
 
 
 			container.setDisplay( '' );
 			container.setDisplay( '' );
 
 
@@ -42,9 +42,7 @@ function SidebarAnimation( editor ) {
 
 
 	signals.objectRemoved.add( function ( object ) {
 	signals.objectRemoved.add( function ( object ) {
 
 
-		var animations = editor.animations[ object !== null ? object.uuid : '' ];
-
-		if ( animations !== undefined ) {
+		if ( object !== null && object.animations.length > 0 ) {
 
 
 			mixer.uncacheRoot( object );
 			mixer.uncacheRoot( object );
 
 
@@ -62,6 +60,8 @@ function SidebarAnimation( editor ) {
 
 
 		actions[ animationsSelect.getValue() ].stop();
 		actions[ animationsSelect.getValue() ].stop();
 
 
+		signals.animationStopped.dispatch();
+
 	}
 	}
 
 
 	function changeTimeScale() {
 	function changeTimeScale() {

+ 112 - 20
editor/js/Sidebar.Material.js

@@ -24,7 +24,8 @@ var materialClasses = {
 	'RawShaderMaterial': THREE.RawShaderMaterial,
 	'RawShaderMaterial': THREE.RawShaderMaterial,
 	'ShaderMaterial': THREE.ShaderMaterial,
 	'ShaderMaterial': THREE.ShaderMaterial,
 	'ShadowMaterial': THREE.ShadowMaterial,
 	'ShadowMaterial': THREE.ShadowMaterial,
-	'SpriteMaterial': THREE.SpriteMaterial
+	'SpriteMaterial': THREE.SpriteMaterial,
+	'PointsMaterial': THREE.PointsMaterial
 };
 };
 
 
 function SidebarMaterial( editor ) {
 function SidebarMaterial( editor ) {
@@ -59,25 +60,7 @@ function SidebarMaterial( editor ) {
 	// type
 	// type
 
 
 	var materialClassRow = new UIRow();
 	var materialClassRow = new UIRow();
-	var materialClass = new UISelect().setOptions( {
-
-		'LineBasicMaterial': 'LineBasicMaterial',
-		'LineDashedMaterial': 'LineDashedMaterial',
-		'MeshBasicMaterial': 'MeshBasicMaterial',
-		'MeshDepthMaterial': 'MeshDepthMaterial',
-		'MeshNormalMaterial': 'MeshNormalMaterial',
-		'MeshLambertMaterial': 'MeshLambertMaterial',
-		'MeshMatcapMaterial': 'MeshMatcapMaterial',
-		'MeshPhongMaterial': 'MeshPhongMaterial',
-		'MeshToonMaterial': 'MeshToonMaterial',
-		'MeshStandardMaterial': 'MeshStandardMaterial',
-		'MeshPhysicalMaterial': 'MeshPhysicalMaterial',
-		'RawShaderMaterial': 'RawShaderMaterial',
-		'ShaderMaterial': 'ShaderMaterial',
-		'ShadowMaterial': 'ShadowMaterial',
-		'SpriteMaterial': 'SpriteMaterial'
-
-	} ).setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
+	var materialClass = new UISelect().setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
 
 
 	materialClassRow.add( new UIText( strings.getKey( 'sidebar/material/type' ) ).setWidth( '90px' ) );
 	materialClassRow.add( new UIText( strings.getKey( 'sidebar/material/type' ) ).setWidth( '90px' ) );
 	materialClassRow.add( materialClass );
 	materialClassRow.add( materialClass );
@@ -501,6 +484,26 @@ function SidebarMaterial( editor ) {
 
 
 	container.add( materialSideRow );
 	container.add( materialSideRow );
 
 
+	// size
+
+	var materialSizeRow = new UIRow();
+	var materialSize = new UINumber( 1 ).setWidth( '60px' ).setRange( 0, Infinity ).onChange( update );
+
+	materialSizeRow.add( new UIText( strings.getKey( 'sidebar/material/size' ) ).setWidth( '90px' ) );
+	materialSizeRow.add( materialSize );
+
+	container.add( materialSizeRow );
+
+	// sizeAttenuation
+
+	var materialSizeAttenuationRow = new UIRow();
+	var materialSizeAttenuation = new UICheckbox( true ).onChange( update );
+
+	materialSizeAttenuationRow.add( new UIText( strings.getKey( 'sidebar/material/sizeAttenuation' ) ).setWidth( '90px' ) );
+	materialSizeAttenuationRow.add( materialSizeAttenuation );
+
+	container.add( materialSizeAttenuationRow );
+
 	// shading
 	// shading
 
 
 	var materialShadingRow = new UIRow();
 	var materialShadingRow = new UIRow();
@@ -1120,6 +1123,28 @@ function SidebarMaterial( editor ) {
 
 
 			}
 			}
 
 
+			if ( material.size !== undefined ) {
+
+				var size = materialSize.getValue();
+				if ( material.size !== size ) {
+
+					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'size', size, currentMaterialSlot ) );
+
+				}
+
+			}
+
+			if ( material.sizeAttenuation !== undefined ) {
+
+				var sizeAttenuation = materialSizeAttenuation.getValue();
+				if ( material.sizeAttenuation !== sizeAttenuation ) {
+
+					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'sizeAttenuation', sizeAttenuation, currentMaterialSlot ) );
+
+				}
+
+			}
+
 			if ( material.flatShading !== undefined ) {
 			if ( material.flatShading !== undefined ) {
 
 
 				var flatShading = materialShading.getValue();
 				var flatShading = materialShading.getValue();
@@ -1248,6 +1273,8 @@ function SidebarMaterial( editor ) {
 			'emissiveMap': materialEmissiveMapRow,
 			'emissiveMap': materialEmissiveMapRow,
 			'gradientMap': materialGradientMapRow,
 			'gradientMap': materialGradientMapRow,
 			'side': materialSideRow,
 			'side': materialSideRow,
+			'size': materialSize,
+			'sizeAttenuation': materialSizeAttenuation,
 			'flatShading': materialShadingRow,
 			'flatShading': materialShadingRow,
 			'blending': materialBlendingRow,
 			'blending': materialBlendingRow,
 			'opacity': materialOpacityRow,
 			'opacity': materialOpacityRow,
@@ -1319,8 +1346,27 @@ function SidebarMaterial( editor ) {
 
 
 		}
 		}
 
 
+		if ( currentObject.isMesh ) {
+
+			materialClass.setOptions( meshMaterialOptions );
+
+		} else if ( currentObject.isSprite ) {
+
+			materialClass.setOptions( spriteMaterialOptions );
+
+		} else if ( currentObject.isPoints ) {
+
+			materialClass.setOptions( pointsMaterialOptions );
+
+		} else if ( currentObject.isLine ) {
+
+			lineMaterialOptions.setOptions( lineMaterialOptions );
+
+		}
+
 		materialClass.setValue( material.type );
 		materialClass.setValue( material.type );
 
 
+
 		if ( material.color !== undefined ) {
 		if ( material.color !== undefined ) {
 
 
 			materialColor.setHexValue( material.color.getHexString() );
 			materialColor.setHexValue( material.color.getHexString() );
@@ -1602,6 +1648,18 @@ function SidebarMaterial( editor ) {
 
 
 		}
 		}
 
 
+		if ( material.size !== undefined ) {
+
+			materialSize.setValue( material.size );
+
+		}
+
+		if ( material.sizeAttenuation !== undefined ) {
+
+			materialSizeAttenuation.setValue( material.sizeAttenuation );
+
+		}
+
 		if ( material.flatShading !== undefined ) {
 		if ( material.flatShading !== undefined ) {
 
 
 			materialShading.setValue( material.flatShading );
 			materialShading.setValue( material.flatShading );
@@ -1701,6 +1759,40 @@ function SidebarMaterial( editor ) {
 		'attribute vec3 position;\n\n',
 		'attribute vec3 position;\n\n',
 	].join( '\n' );
 	].join( '\n' );
 
 
+	var meshMaterialOptions = {
+		'MeshBasicMaterial': 'MeshBasicMaterial',
+		'MeshDepthMaterial': 'MeshDepthMaterial',
+		'MeshNormalMaterial': 'MeshNormalMaterial',
+		'MeshLambertMaterial': 'MeshLambertMaterial',
+		'MeshMatcapMaterial': 'MeshMatcapMaterial',
+		'MeshPhongMaterial': 'MeshPhongMaterial',
+		'MeshToonMaterial': 'MeshToonMaterial',
+		'MeshStandardMaterial': 'MeshStandardMaterial',
+		'MeshPhysicalMaterial': 'MeshPhysicalMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial',
+		'ShadowMaterial': 'ShadowMaterial'
+	};
+
+	var lineMaterialOptions = {
+		'LineBasicMaterial': 'LineBasicMaterial',
+		'LineDashedMaterial': 'LineDashedMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial'
+	};
+
+	var spriteMaterialOptions = {
+		'SpriteMaterial': 'SpriteMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial'
+	};
+
+	var pointsMaterialOptions = {
+		'PointsMaterial': 'PointsMaterial',
+		'RawShaderMaterial': 'RawShaderMaterial',
+		'ShaderMaterial': 'ShaderMaterial'
+	};
+
 	return container;
 	return container;
 
 
 }
 }

+ 6 - 0
editor/js/Strings.js

@@ -268,6 +268,8 @@ function Strings( config ) {
 			'sidebar/material/side/front': 'Front',
 			'sidebar/material/side/front': 'Front',
 			'sidebar/material/side/back': 'Back',
 			'sidebar/material/side/back': 'Back',
 			'sidebar/material/side/double': 'Double',
 			'sidebar/material/side/double': 'Double',
+			'sidebar/material/size': 'Size',
+			'sidebar/material/sizeAttenuation': 'Size Attenuation',
 			'sidebar/material/flatshaded': 'Flat Shaded',
 			'sidebar/material/flatshaded': 'Flat Shaded',
 			'sidebar/material/blending': 'Blending',
 			'sidebar/material/blending': 'Blending',
 			'sidebar/material/blending/no': 'No',
 			'sidebar/material/blending/no': 'No',
@@ -592,6 +594,8 @@ function Strings( config ) {
 			'sidebar/material/side/front': 'Face avant',
 			'sidebar/material/side/front': 'Face avant',
 			'sidebar/material/side/back': 'Face Arrière',
 			'sidebar/material/side/back': 'Face Arrière',
 			'sidebar/material/side/double': 'Double face',
 			'sidebar/material/side/double': 'Double face',
+			'sidebar/material/size': 'Size',
+			'sidebar/material/sizeAttenuation': 'Size Attenuation',
 			'sidebar/material/flatshaded': 'Rendu plat',
 			'sidebar/material/flatshaded': 'Rendu plat',
 			'sidebar/material/blending': 'Mélange',
 			'sidebar/material/blending': 'Mélange',
 			'sidebar/material/blending/no': 'Non',
 			'sidebar/material/blending/no': 'Non',
@@ -900,6 +904,8 @@ function Strings( config ) {
 			'sidebar/material/side/front': '正面',
 			'sidebar/material/side/front': '正面',
 			'sidebar/material/side/back': '背面',
 			'sidebar/material/side/back': '背面',
 			'sidebar/material/side/double': '双面',
 			'sidebar/material/side/double': '双面',
+			'sidebar/material/size': '大小',
+			'sidebar/material/sizeAttenuation': '大小衰减',
 			'sidebar/material/flatshaded': '平面着色',
 			'sidebar/material/flatshaded': '平面着色',
 			'sidebar/material/blending': '混合',
 			'sidebar/material/blending': '混合',
 			'sidebar/material/blending/no': '无',
 			'sidebar/material/blending/no': '无',

+ 6 - 0
editor/js/Viewport.js

@@ -511,6 +511,12 @@ function Viewport( editor ) {
 
 
 	} );
 	} );
 
 
+	signals.animationStopped.add( function () {
+
+		render();
+
+	} );
+
 	// background
 	// background
 
 
 	signals.sceneBackgroundChanged.add( function ( backgroundType, backgroundColor, backgroundTexture, backgroundEquirectangularTexture, environmentType ) {
 	signals.sceneBackgroundChanged.add( function ( backgroundType, backgroundColor, backgroundTexture, backgroundEquirectangularTexture, environmentType ) {

+ 2 - 1
editor/sw.js

@@ -1,4 +1,4 @@
-// r122
+// r123
 
 
 const assets = [
 const assets = [
 	'./',
 	'./',
@@ -47,6 +47,7 @@ const assets = [
 	'../examples/jsm/loaders/TDSLoader.js',
 	'../examples/jsm/loaders/TDSLoader.js',
 	'../examples/jsm/loaders/VRMLLoader.js',
 	'../examples/jsm/loaders/VRMLLoader.js',
 	'../examples/jsm/loaders/VTKLoader.js',
 	'../examples/jsm/loaders/VTKLoader.js',
+	'../examples/jsm/loaders/XYZLoader.js',
 
 
 	'../examples/jsm/curves/NURBSCurve.js',
 	'../examples/jsm/curves/NURBSCurve.js',
 	'../examples/jsm/curves/NURBSUtils.js',
 	'../examples/jsm/curves/NURBSUtils.js',

+ 1 - 1
examples/files.json

@@ -92,6 +92,7 @@
 		"webgl_loader_gcode",
 		"webgl_loader_gcode",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf_extensions",
 		"webgl_loader_gltf_extensions",
+		"webgl_loader_gltf_variants",
 		"webgl_loader_imagebitmap",
 		"webgl_loader_imagebitmap",
 		"webgl_loader_kmz",
 		"webgl_loader_kmz",
 		"webgl_loader_ldraw",
 		"webgl_loader_ldraw",
@@ -267,7 +268,6 @@
 		"webgl_postprocessing_rgb_halftone",
 		"webgl_postprocessing_rgb_halftone",
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_masking",
 		"webgl_postprocessing_ssaa",
 		"webgl_postprocessing_ssaa",
-		"webgl_postprocessing_ssaa_unbiased",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_outline",
 		"webgl_postprocessing_pixel",
 		"webgl_postprocessing_pixel",
 		"webgl_postprocessing_procedural",
 		"webgl_postprocessing_procedural",

+ 10 - 10
examples/index.html

@@ -188,16 +188,16 @@
 
 
 		function createLink( file ) {
 		function createLink( file ) {
 
 
-			const template = [
-				'<div class="card">',
-				'	<a href="' + file + '.html" target="viewer">',
-				'		<div class="cover">',
-				'			<img src="screenshots/' + file + '.jpg" loading="lazy" width="400" />',
-				'		</div>',
-				'		<div class="title">' + getName( file ) + '</div>',
-				'	</a>',
-				'</div>'
-			].join( "\n" );
+			const template = `
+				<div class="card">
+					<a href="${file}.html" target="viewer">
+						<div class="cover">
+							<img src="screenshots/${ file }.jpg" loading="lazy" width="400" />
+						</div>
+						<div class="title">${getName( file )}</div>
+					</a>
+				</div>
+			`;
 
 
 			const link = createElementFromHTML( template );
 			const link = createElementFromHTML( template );
 
 

+ 69 - 20
examples/js/controls/DragControls.js

@@ -18,25 +18,25 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	function activate() {
 	function activate() {
 
 
-		_domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
-		_domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
-		_domElement.addEventListener( 'mouseup', onDocumentMouseCancel, false );
-		_domElement.addEventListener( 'mouseleave', onDocumentMouseCancel, false );
-		_domElement.addEventListener( 'touchmove', onDocumentTouchMove, false );
-		_domElement.addEventListener( 'touchstart', onDocumentTouchStart, false );
-		_domElement.addEventListener( 'touchend', onDocumentTouchEnd, false );
+		_domElement.addEventListener( 'pointermove', onPointerMove, false );
+		_domElement.addEventListener( 'pointerdown', onPointerDown, false );
+		_domElement.addEventListener( 'pointerup', onPointerCancel, false );
+		_domElement.addEventListener( 'pointerleave', onPointerCancel, false );
+		_domElement.addEventListener( 'touchmove', onTouchMove, false );
+		_domElement.addEventListener( 'touchstart', onTouchStart, false );
+		_domElement.addEventListener( 'touchend', onTouchEnd, false );
 
 
 	}
 	}
 
 
 	function deactivate() {
 	function deactivate() {
 
 
-		_domElement.removeEventListener( 'mousemove', onDocumentMouseMove, false );
-		_domElement.removeEventListener( 'mousedown', onDocumentMouseDown, false );
-		_domElement.removeEventListener( 'mouseup', onDocumentMouseCancel, false );
-		_domElement.removeEventListener( 'mouseleave', onDocumentMouseCancel, false );
-		_domElement.removeEventListener( 'touchmove', onDocumentTouchMove, false );
-		_domElement.removeEventListener( 'touchstart', onDocumentTouchStart, false );
-		_domElement.removeEventListener( 'touchend', onDocumentTouchEnd, false );
+		_domElement.removeEventListener( 'pointermove', onPointerMove, false );
+		_domElement.removeEventListener( 'pointerdown', onPointerDown, false );
+		_domElement.removeEventListener( 'pointerup', onPointerCancel, false );
+		_domElement.removeEventListener( 'pointerleave', onPointerCancel, false );
+		_domElement.removeEventListener( 'touchmove', onTouchMove, false );
+		_domElement.removeEventListener( 'touchstart', onTouchStart, false );
+		_domElement.removeEventListener( 'touchend', onTouchEnd, false );
 
 
 		_domElement.style.cursor = '';
 		_domElement.style.cursor = '';
 
 
@@ -54,10 +54,25 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentMouseMove( event ) {
+	function onPointerMove( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 
+		switch ( event.pointerType ) {
+
+			case 'mouse':
+			case 'pen':
+				onMouseMove( event );
+				break;
+
+			// TODO touch
+
+		}
+
+	}
+
+	function onMouseMove( event ) {
+
 		var rect = _domElement.getBoundingClientRect();
 		var rect = _domElement.getBoundingClientRect();
 
 
 		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
 		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
@@ -114,7 +129,24 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentMouseDown( event ) {
+	function onPointerDown( event ) {
+
+		event.preventDefault();
+
+		switch ( event.pointerType ) {
+
+			case 'mouse':
+			case 'pen':
+				onMouseDown( event );
+				break;
+
+			// TODO touch
+
+		}
+
+	}
+
+	function onMouseDown( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 
@@ -143,7 +175,24 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentMouseCancel( event ) {
+	function onPointerCancel( event ) {
+
+		event.preventDefault();
+
+		switch ( event.pointerType ) {
+
+			case 'mouse':
+			case 'pen':
+				onMouseCancel( event );
+				break;
+
+			// TODO touch
+
+		}
+
+	}
+
+	function onMouseCancel( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 
@@ -159,7 +208,7 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentTouchMove( event ) {
+	function onTouchMove( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 		event = event.changedTouches[ 0 ];
 		event = event.changedTouches[ 0 ];
@@ -187,7 +236,7 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentTouchStart( event ) {
+	function onTouchStart( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 		event = event.changedTouches[ 0 ];
 		event = event.changedTouches[ 0 ];
@@ -224,7 +273,7 @@ THREE.DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentTouchEnd( event ) {
+	function onTouchEnd( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 

+ 3 - 5
examples/js/controls/OrbitControls.js

@@ -800,8 +800,6 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 
 	function onPointerUp( event ) {
 	function onPointerUp( event ) {
 
 
-		if ( scope.enabled === false ) return;
-
 		switch ( event.pointerType ) {
 		switch ( event.pointerType ) {
 
 
 			case 'mouse':
 			case 'mouse':
@@ -961,13 +959,13 @@ THREE.OrbitControls = function ( object, domElement ) {
 
 
 	function onMouseUp( event ) {
 	function onMouseUp( event ) {
 
 
+		scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, false );
+		scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp, false );
+
 		if ( scope.enabled === false ) return;
 		if ( scope.enabled === false ) return;
 
 
 		handleMouseUp( event );
 		handleMouseUp( event );
 
 
-		scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, false );
-		scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp, false );
-
 		scope.dispatchEvent( endEvent );
 		scope.dispatchEvent( endEvent );
 
 
 		state = STATE.NONE;
 		state = STATE.NONE;

+ 84 - 32
examples/js/exporters/DRACOExporter.js

@@ -20,8 +20,13 @@ THREE.DRACOExporter.prototype = {
 
 
 	constructor: THREE.DRACOExporter,
 	constructor: THREE.DRACOExporter,
 
 
-	parse: function ( geometry, options ) {
+	parse: function ( object, options ) {
 
 
+		if ( object.isBufferGeometry === true || object.isGeometry === true ) {
+
+			throw new Error( 'DRACOExporter: The first parameter of parse() is now an instance of Mesh or Points.' );
+
+		}
 
 
 		if ( DracoEncoderModule === undefined ) {
 		if ( DracoEncoderModule === undefined ) {
 
 
@@ -45,15 +50,17 @@ THREE.DRACOExporter.prototype = {
 
 
 		}
 		}
 
 
+		var geometry = object.geometry;
+
 		var dracoEncoder = DracoEncoderModule();
 		var dracoEncoder = DracoEncoderModule();
 		var encoder = new dracoEncoder.Encoder();
 		var encoder = new dracoEncoder.Encoder();
-		var builder = new dracoEncoder.MeshBuilder();
-		var mesh = new dracoEncoder.Mesh();
+		var builder;
+		var dracoObject;
 
 
 		if ( geometry.isGeometry === true ) {
 		if ( geometry.isGeometry === true ) {
 
 
 			var bufferGeometry = new THREE.BufferGeometry();
 			var bufferGeometry = new THREE.BufferGeometry();
-			bufferGeometry.fromGeometry( geometry );
+			bufferGeometry.setFromObject( object );
 			geometry = bufferGeometry;
 			geometry = bufferGeometry;
 
 
 		}
 		}
@@ -64,63 +71,94 @@ THREE.DRACOExporter.prototype = {
 
 
 		}
 		}
 
 
-		var vertices = geometry.getAttribute( 'position' );
-		builder.AddFloatAttributeToMesh( mesh, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
+		if ( object.isMesh === true ) {
 
 
-		var faces = geometry.getIndex();
+			builder = new dracoEncoder.MeshBuilder();
+			dracoObject = new dracoEncoder.Mesh();
 
 
-		if ( faces !== null ) {
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
 
-			builder.AddFacesToMesh( mesh, faces.count / 3, faces.array );
+			var faces = geometry.getIndex();
 
 
-		} else {
+			if ( faces !== null ) {
 
 
-			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+				builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array );
 
 
-			for ( var i = 0; i < faces.length; i ++ ) {
+			} else {
+
+				var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+
+				for ( var i = 0; i < faces.length; i ++ ) {
+
+					faces[ i ] = i;
+
+				}
 
 
-				faces[ i ] = i;
+				builder.AddFacesToMesh( dracoObject, vertices.count, faces );
 
 
 			}
 			}
 
 
-			builder.AddFacesToMesh( mesh, vertices.count, faces );
+			if ( options.exportNormals === true ) {
 
 
-		}
+				var normals = geometry.getAttribute( 'normal' );
+
+				if ( normals !== undefined ) {
 
 
-		if ( options.exportNormals === true ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
 
 
-			var normals = geometry.getAttribute( 'normal' );
+				}
+
+			}
 
 
-			if ( normals !== undefined ) {
+			if ( options.exportUvs === true ) {
 
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
+				var uvs = geometry.getAttribute( 'uv' );
+
+				if ( uvs !== undefined ) {
+
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+
+				}
 
 
 			}
 			}
 
 
-		}
+			if ( options.exportColor === true ) {
 
 
-		if ( options.exportUvs === true ) {
+				var colors = geometry.getAttribute( 'color' );
 
 
-			var uvs = geometry.getAttribute( 'uv' );
+				if ( colors !== undefined ) {
 
 
-			if ( uvs !== undefined ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
 
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+				}
 
 
 			}
 			}
 
 
-		}
+		} else if ( object.isPoints === true ) {
+
+			builder = new dracoEncoder.PointCloudBuilder();
+			dracoObject = new dracoEncoder.PointCloud();
+
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
 
-		if ( options.exportColor === true ) {
+			if ( options.exportColor === true ) {
 
 
-			var colors = geometry.getAttribute( 'color' );
+				var colors = geometry.getAttribute( 'color' );
 
 
-			if ( colors !== undefined ) {
+				if ( colors !== undefined ) {
 
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+					builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+
+				}
 
 
 			}
 			}
 
 
+		} else {
+
+			throw new Error( 'DRACOExporter: Unsupported object type.' );
+
 		}
 		}
 
 
 		//Compress using draco encoder
 		//Compress using draco encoder
@@ -129,7 +167,10 @@ THREE.DRACOExporter.prototype = {
 
 
 		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
 		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
 
 
-		encoder.SetSpeedOptions( options.encodeSpeed || 5, options.decodeSpeed || 5 );
+		var encodeSpeed = ( options.encodeSpeed !== undefined ) ? options.encodeSpeed : 5;
+		var decodeSpeed = ( options.decodeSpeed !== undefined ) ? options.decodeSpeed : 5;
+
+		encoder.SetSpeedOptions( encodeSpeed, decodeSpeed );
 
 
 		// Sets the desired encoding method for a given geometry.
 		// Sets the desired encoding method for a given geometry.
 
 
@@ -155,8 +196,19 @@ THREE.DRACOExporter.prototype = {
 
 
 		}
 		}
 
 
-		var length = encoder.EncodeMeshToDracoBuffer( mesh, encodedData );
-		dracoEncoder.destroy( mesh );
+		var length;
+
+		if ( object.isMesh === true ) {
+
+			length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData );
+
+		} else {
+
+			length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData );
+
+		}
+
+		dracoEncoder.destroy( dracoObject );
 
 
 		if ( length === 0 ) {
 		if ( length === 0 ) {
 
 

+ 61 - 5
examples/js/exporters/GLTFExporter.js

@@ -1,6 +1,7 @@
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 // Constants
 // Constants
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+
 var WEBGL_CONSTANTS = {
 var WEBGL_CONSTANTS = {
 	POINTS: 0x0000,
 	POINTS: 0x0000,
 	LINES: 0x0001,
 	LINES: 0x0001,
@@ -211,7 +212,23 @@ THREE.GLTFExporter.prototype = {
 
 
 				for ( var a = 0; a < attribute.itemSize; a ++ ) {
 				for ( var a = 0; a < attribute.itemSize; a ++ ) {
 
 
-					var value = attribute.array[ i * attribute.itemSize + a ];
+					var value;
+
+					if ( attribute.itemSize > 4 ) {
+
+						 // no support for interleaved data for itemSize > 4
+
+						value = attribute.array[ i * attribute.itemSize + a ];
+
+					} else {
+
+						if ( a === 0 ) value = attribute.getX( i );
+						else if ( a === 1 ) value = attribute.getY( i );
+						else if ( a === 2 ) value = attribute.getZ( i );
+						else if ( a === 3 ) value = attribute.getW( i );
+
+					}
+
 					output.min[ a ] = Math.min( output.min[ a ], value );
 					output.min[ a ] = Math.min( output.min[ a ], value );
 					output.max[ a ] = Math.max( output.max[ a ], value );
 					output.max[ a ] = Math.max( output.max[ a ], value );
 
 
@@ -243,7 +260,7 @@ THREE.GLTFExporter.prototype = {
 			for ( var i = 0, il = normal.count; i < il; i ++ ) {
 			for ( var i = 0, il = normal.count; i < il; i ++ ) {
 
 
 				// 0.0005 is from glTF-validator
 				// 0.0005 is from glTF-validator
-				if ( Math.abs( v.fromArray( normal.array, i * 3 ).length() - 1.0 ) > 0.0005 ) return false;
+				if ( Math.abs( v.fromBufferAttribute( normal, i ).length() - 1.0 ) > 0.0005 ) return false;
 
 
 			}
 			}
 
 
@@ -272,7 +289,7 @@ THREE.GLTFExporter.prototype = {
 
 
 			for ( var i = 0, il = attribute.count; i < il; i ++ ) {
 			for ( var i = 0, il = attribute.count; i < il; i ++ ) {
 
 
-				v.fromArray( attribute.array, i * 3 );
+				v.fromBufferAttribute( attribute, i );
 
 
 				if ( v.x === 0 && v.y === 0 && v.z === 0 ) {
 				if ( v.x === 0 && v.y === 0 && v.z === 0 ) {
 
 
@@ -285,7 +302,7 @@ THREE.GLTFExporter.prototype = {
 
 
 				}
 				}
 
 
-				v.toArray( attribute.array, i * 3 );
+				attribute.setXYZ( i, v.x, v.y, v.z );
 
 
 			}
 			}
 
 
@@ -776,7 +793,46 @@ THREE.GLTFExporter.prototype = {
 
 
 				}
 				}
 
 
-				ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
+				if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
+					( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
+					( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
+
+					ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
+
+				} else {
+
+					if ( format !== THREE.RGBAFormat && format !== THREE.RGBFormat ) {
+
+						console.error( 'GLTFExporter: Only RGB and RGBA formats are supported.' );
+
+					}
+
+					if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) {
+
+						console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image );
+
+					}
+
+					let data = image.data;
+
+					if ( format === THREE.RGBFormat ) {
+
+						data = new Uint8ClampedArray( image.height * image.width * 4 );
+
+						for ( var i = 0, j = 0; i < data.length; i += 4, j += 3 ) {
+
+							data[ i + 0 ] = image.data[ j + 0 ];
+							data[ i + 1 ] = image.data[ j + 1 ];
+							data[ i + 2 ] = image.data[ j + 2 ];
+							data[ i + 3 ] = 255;
+
+						}
+
+					}
+
+					ctx.putImageData( new ImageData( data, image.width, image.height ), 0, 0 );
+
+				}
 
 
 				if ( options.binary === true ) {
 				if ( options.binary === true ) {
 
 

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

@@ -13,6 +13,7 @@ THREE.OBJExporter.prototype = {
 		var indexNormals = 0;
 		var indexNormals = 0;
 
 
 		var vertex = new THREE.Vector3();
 		var vertex = new THREE.Vector3();
+		var color = new THREE.Color();
 		var normal = new THREE.Vector3();
 		var normal = new THREE.Vector3();
 		var uv = new THREE.Vector2();
 		var uv = new THREE.Vector2();
 
 
@@ -235,6 +236,69 @@ THREE.OBJExporter.prototype = {
 
 
 		};
 		};
 
 
+		var parsePoints = function ( points ) {
+
+			var nbVertex = 0;
+
+			var geometry = points.geometry;
+
+			if ( geometry instanceof THREE.Geometry ) {
+
+				geometry = new THREE.BufferGeometry().setFromObject( points );
+
+			}
+
+			if ( geometry instanceof THREE.BufferGeometry ) {
+
+				var vertices = geometry.getAttribute( 'position' );
+				var colors = geometry.getAttribute( 'color' );
+
+				output += 'o ' + points.name + '\n';
+
+				if ( vertices !== undefined ) {
+
+					for ( i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
+
+						vertex.fromBufferAttribute( vertices, i );
+						vertex.applyMatrix4( points.matrixWorld );
+
+						output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z;
+
+						if ( colors !== undefined ) {
+
+							color.fromBufferAttribute( colors, i );
+
+							output += ' ' + color.r + ' ' + color.g + ' ' + color.b;
+
+						}
+
+						output += '\n';
+
+					}
+
+				}
+
+				output += 'p ';
+
+				for ( j = 1, l = vertices.count; j <= l; j ++ ) {
+
+					output += ( indexVertex + j ) + ' ';
+
+				}
+
+				output += '\n';
+
+			} else {
+
+				console.warn( 'THREE.OBJExporter.parsePoints(): geometry type unsupported', geometry );
+
+			}
+
+			// update index
+			indexVertex += nbVertex;
+
+		};
+
 		object.traverse( function ( child ) {
 		object.traverse( function ( child ) {
 
 
 			if ( child instanceof THREE.Mesh ) {
 			if ( child instanceof THREE.Mesh ) {
@@ -249,6 +313,12 @@ THREE.OBJExporter.prototype = {
 
 
 			}
 			}
 
 
+			if ( child instanceof THREE.Points ) {
+
+				parsePoints( child );
+
+			}
+
 		} );
 		} );
 
 
 		return output;
 		return output;

+ 1 - 1
examples/js/exporters/PLYExporter.js

@@ -45,7 +45,7 @@ THREE.PLYExporter.prototype = {
 
 
 					if ( geometry.isBufferGeometry === true ) {
 					if ( geometry.isBufferGeometry === true ) {
 
 
-						if ( geometry.getAttribute( 'position' ) !== undefined ) {
+						if ( geometry.hasAttribute( 'position' ) === true ) {
 
 
 							cb( mesh, geometry );
 							cb( mesh, geometry );
 
 

+ 14 - 1
examples/js/loaders/3MFLoader.js

@@ -1387,11 +1387,24 @@ THREE.ThreeMFLoader.prototype = Object.assign( Object.create( THREE.Loader.proto
 
 
 		}
 		}
 
 
+		function fetch3DModelPart( rels ) {
+
+			for ( var i = 0; i < rels.length; i ++ ) {
+
+				var rel = rels[ i ];
+				var extension = rel.target.split( '.' ).pop();
+
+				if ( extension.toLowerCase() === 'model' ) return rel;
+
+			}
+
+		}
+
 		function build( objects, data3mf ) {
 		function build( objects, data3mf ) {
 
 
 			var group = new THREE.Group();
 			var group = new THREE.Group();
 
 
-			var relationship = data3mf[ 'rels' ][ 0 ];
+			var relationship = fetch3DModelPart( data3mf[ 'rels' ] );
 			var buildData = data3mf.model[ relationship[ 'target' ].substring( 1 ) ][ 'build' ];
 			var buildData = data3mf.model[ relationship[ 'target' ].substring( 1 ) ][ 'build' ];
 
 
 			for ( var i = 0; i < buildData.length; i ++ ) {
 			for ( var i = 0; i < buildData.length; i ++ ) {

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

@@ -3955,6 +3955,7 @@ THREE.ColladaLoader.prototype = Object.assign( Object.create( THREE.Loader.proto
 		setupKinematics();
 		setupKinematics();
 
 
 		var scene = parseScene( getElementsByTagName( collada, 'scene' )[ 0 ] );
 		var scene = parseScene( getElementsByTagName( collada, 'scene' )[ 0 ] );
+		scene.animations = animations;
 
 
 		if ( asset.upAxis === 'Z_UP' ) {
 		if ( asset.upAxis === 'Z_UP' ) {
 
 
@@ -3965,7 +3966,12 @@ THREE.ColladaLoader.prototype = Object.assign( Object.create( THREE.Loader.proto
 		scene.scale.multiplyScalar( asset.unit );
 		scene.scale.multiplyScalar( asset.unit );
 
 
 		return {
 		return {
-			animations: animations,
+			get animations() {
+
+				console.warn( 'THREE.ColladaLoader: Please access animations over scene.animations now.' );
+				return animations;
+
+			},
 			kinematics: kinematics,
 			kinematics: kinematics,
 			library: library,
 			library: library,
 			scene: scene
 			scene: scene

+ 115 - 78
examples/js/loaders/OBJLoader.js

@@ -375,7 +375,10 @@ THREE.OBJLoader = ( function () {
 
 
 				for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
 				for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
 
 
-					this.addVertexPoint( this.parseVertexIndex( vertices[ vi ], vLen ) );
+					var index = this.parseVertexIndex( vertices[ vi ], vLen );
+
+					this.addVertexPoint( index );
+					this.addColor( index );
 
 
 				}
 				}
 
 
@@ -708,147 +711,181 @@ THREE.OBJLoader = ( function () {
 			var container = new THREE.Group();
 			var container = new THREE.Group();
 			container.materialLibraries = [].concat( state.materialLibraries );
 			container.materialLibraries = [].concat( state.materialLibraries );
 
 
-			for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
+			var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
 
 
-				var object = state.objects[ i ];
-				var geometry = object.geometry;
-				var materials = object.materials;
-				var isLine = ( geometry.type === 'Line' );
-				var isPoints = ( geometry.type === 'Points' );
-				var hasVertexColors = false;
+			if ( hasPrimitives === true ) {
 
 
-				// Skip o/g line declarations that did not follow with any faces
-				if ( geometry.vertices.length === 0 ) continue;
+				for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
 
 
-				var buffergeometry = new THREE.BufferGeometry();
+					var object = state.objects[ i ];
+					var geometry = object.geometry;
+					var materials = object.materials;
+					var isLine = ( geometry.type === 'Line' );
+					var isPoints = ( geometry.type === 'Points' );
+					var hasVertexColors = false;
 
 
-				buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
+					// Skip o/g line declarations that did not follow with any faces
+					if ( geometry.vertices.length === 0 ) continue;
 
 
-				buffergeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
+					var buffergeometry = new THREE.BufferGeometry();
 
 
-				if ( geometry.colors.length > 0 ) {
+					buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( geometry.vertices, 3 ) );
 
 
-					hasVertexColors = true;
-					buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
+					if ( geometry.normals.length > 0 ) {
 
 
-				}
+						buffergeometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( geometry.normals, 3 ) );
 
 
-				if ( geometry.hasUVIndices === true ) {
+					}
 
 
-					buffergeometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
+					if ( geometry.colors.length > 0 ) {
 
 
-				}
+						hasVertexColors = true;
+						buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( geometry.colors, 3 ) );
+
+					}
+
+					if ( geometry.hasUVIndices === true ) {
 
 
-				// Create materials
+						buffergeometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( geometry.uvs, 2 ) );
 
 
-				var createdMaterials = [];
+					}
+
+					// Create materials
 
 
-				for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+					var createdMaterials = [];
 
 
-					var sourceMaterial = materials[ mi ];
-					var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
-					var material = state.materials[ materialHash ];
+					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
 
-					if ( this.materials !== null ) {
+						var sourceMaterial = materials[ mi ];
+						var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
+						var material = state.materials[ materialHash ];
 
 
-						material = this.materials.create( sourceMaterial.name );
+						if ( this.materials !== null ) {
 
 
-						// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
-						if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
+							material = this.materials.create( sourceMaterial.name );
 
 
-							var materialLine = new THREE.LineBasicMaterial();
-							THREE.Material.prototype.copy.call( materialLine, material );
-							materialLine.color.copy( material.color );
-							material = materialLine;
+							// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
+							if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
 
 
-						} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
+								var materialLine = new THREE.LineBasicMaterial();
+								THREE.Material.prototype.copy.call( materialLine, material );
+								materialLine.color.copy( material.color );
+								material = materialLine;
 
 
-							var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
-							THREE.Material.prototype.copy.call( materialPoints, material );
-							materialPoints.color.copy( material.color );
-							materialPoints.map = material.map;
-							material = materialPoints;
+							} else if ( isPoints && material && ! ( material instanceof THREE.PointsMaterial ) ) {
+
+								var materialPoints = new THREE.PointsMaterial( { size: 10, sizeAttenuation: false } );
+								THREE.Material.prototype.copy.call( materialPoints, material );
+								materialPoints.color.copy( material.color );
+								materialPoints.map = material.map;
+								material = materialPoints;
+
+							}
 
 
 						}
 						}
 
 
-					}
+						if ( material === undefined ) {
 
 
-					if ( material === undefined ) {
+							if ( isLine ) {
 
 
-						if ( isLine ) {
+								material = new THREE.LineBasicMaterial();
 
 
-							material = new THREE.LineBasicMaterial();
+							} else if ( isPoints ) {
 
 
-						} else if ( isPoints ) {
+								material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
 
 
-							material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
+							} else {
 
 
-						} else {
+								material = new THREE.MeshPhongMaterial();
 
 
-							material = new THREE.MeshPhongMaterial();
+							}
 
 
-						}
+							material.name = sourceMaterial.name;
+							material.flatShading = sourceMaterial.smooth ? false : true;
+							material.vertexColors = hasVertexColors;
 
 
-						material.name = sourceMaterial.name;
-						material.flatShading = sourceMaterial.smooth ? false : true;
-						material.vertexColors = hasVertexColors;
+							state.materials[ materialHash ] = material;
 
 
-						state.materials[ materialHash ] = material;
+						}
+
+						createdMaterials.push( material );
 
 
 					}
 					}
 
 
-					createdMaterials.push( material );
+					// Create mesh
 
 
-				}
+					var mesh;
 
 
-				// Create mesh
+					if ( createdMaterials.length > 1 ) {
 
 
-				var mesh;
+						for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
 
-				if ( createdMaterials.length > 1 ) {
+							var sourceMaterial = materials[ mi ];
+							buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
 
 
-					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+						}
 
 
-						var sourceMaterial = materials[ mi ];
-						buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
+						if ( isLine ) {
 
 
-					}
+							mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
 
 
-					if ( isLine ) {
+						} else if ( isPoints ) {
 
 
-						mesh = new THREE.LineSegments( buffergeometry, createdMaterials );
+							mesh = new THREE.Points( buffergeometry, createdMaterials );
 
 
-					} else if ( isPoints ) {
+						} else {
 
 
-						mesh = new THREE.Points( buffergeometry, createdMaterials );
+							mesh = new THREE.Mesh( buffergeometry, createdMaterials );
+
+						}
 
 
 					} else {
 					} else {
 
 
-						mesh = new THREE.Mesh( buffergeometry, createdMaterials );
+						if ( isLine ) {
 
 
-					}
+							mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
 
 
-				} else {
+						} else if ( isPoints ) {
 
 
-					if ( isLine ) {
+							mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
 
 
-						mesh = new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] );
+						} else {
 
 
-					} else if ( isPoints ) {
+							mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
 
 
-						mesh = new THREE.Points( buffergeometry, createdMaterials[ 0 ] );
+						}
 
 
-					} else {
+					}
 
 
-						mesh = new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] );
+					mesh.name = object.name;
 
 
-					}
+					container.add( mesh );
 
 
 				}
 				}
 
 
-				mesh.name = object.name;
+			} else {
+
+				// if there is only the default parser state object with no geometry data, interpret data as point cloud
+
+				if ( state.vertices.length > 0 ) {
+
+					var material = new THREE.PointsMaterial( { size: 1, sizeAttenuation: false } );
+
+					var buffergeometry = new THREE.BufferGeometry();
+
+					buffergeometry.setAttribute( 'position', new THREE.Float32BufferAttribute( state.vertices, 3 ) );
 
 
-				container.add( mesh );
+					if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) {
+
+						buffergeometry.setAttribute( 'color', new THREE.Float32BufferAttribute( state.colors, 3 ) );
+						material.vertexColors = true;
+
+					}
+
+					var points = new THREE.Points( buffergeometry, material );
+					container.add( points );
+
+				}
 
 
 			}
 			}
 
 

+ 58 - 18
examples/js/loaders/TDSLoader.js

@@ -286,16 +286,16 @@ THREE.TDSLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype
 
 
 			} else if ( next === MAT_SHININESS ) {
 			} else if ( next === MAT_SHININESS ) {
 
 
-				var shininess = this.readWord( data );
-				material.shininess = shininess;
+				var shininess = this.readPercentage( data );
+				material.shininess = shininess * 100;
 				this.debugMessage( '   Shininess : ' + shininess );
 				this.debugMessage( '   Shininess : ' + shininess );
 
 
 			} else if ( next === MAT_TRANSPARENCY ) {
 			} else if ( next === MAT_TRANSPARENCY ) {
 
 
-				var opacity = this.readWord( data );
-				material.opacity = opacity * 0.01;
-				this.debugMessage( '  Opacity : ' + opacity );
-				material.transparent = opacity < 100 ? true : false;
+				var transparency = this.readPercentage( data );
+				material.opacity = 1 - transparency;
+				this.debugMessage( '  Transparency : ' + transparency );
+				material.transparent = material.opacity < 1 ? true : false;
 
 
 			} else if ( next === MAT_TEXMAP ) {
 			} else if ( next === MAT_TEXMAP ) {
 
 
@@ -494,42 +494,49 @@ THREE.TDSLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype
 
 
 		//The rest of the FACE_ARRAY chunk is subchunks
 		//The rest of the FACE_ARRAY chunk is subchunks
 
 
+		var materialIndex = 0;
+		var start = 0;
+
 		while ( this.position < chunk.end ) {
 		while ( this.position < chunk.end ) {
 
 
-			var chunk = this.readChunk( data );
+			var subchunk = this.readChunk( data );
 
 
-			if ( chunk.id === MSH_MAT_GROUP ) {
+			if ( subchunk.id === MSH_MAT_GROUP ) {
 
 
 				this.debugMessage( '      Material Group' );
 				this.debugMessage( '      Material Group' );
 
 
 				this.resetPosition( data );
 				this.resetPosition( data );
 
 
 				var group = this.readMaterialGroup( data );
 				var group = this.readMaterialGroup( data );
+				var count = group.index.length * 3; // assuming successive indices
 
 
-				var material = this.materials[ group.name ];
+				mesh.geometry.addGroup( start, count, materialIndex );
 
 
-				if ( material !== undefined )	{
+				start += count;
+				materialIndex ++;
 
 
-					mesh.material = material;
+				var material = this.materials[ group.name ];
 
 
-					if ( material.name === '' )		{
+				if ( Array.isArray( mesh.material ) === false ) mesh.material = [];
 
 
-						material.name = mesh.name;
+				if ( material !== undefined )	{
 
 
-					}
+					mesh.material.push( material );
 
 
 				}
 				}
 
 
 			} else {
 			} else {
 
 
-				this.debugMessage( '      Unknown face array chunk: ' + chunk.toString( 16 ) );
+				this.debugMessage( '      Unknown face array chunk: ' + subchunk.toString( 16 ) );
 
 
 			}
 			}
 
 
-			this.endChunk( chunk );
+			this.endChunk( subchunk );
 
 
 		}
 		}
 
 
+		if ( mesh.material.length === 1 ) mesh.material = mesh.material[ 0 ]; // for backwards compatibility
+
 		this.endChunk( chunk );
 		this.endChunk( chunk );
 
 
 	},
 	},
@@ -869,6 +876,39 @@ THREE.TDSLoader.prototype = Object.assign( Object.create( THREE.Loader.prototype
 
 
 	},
 	},
 
 
+	/**
+	 * Read percentage value.
+	 *
+	 * @method readPercentage
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readPercentage: function ( data ) {
+
+		var chunk = this.readChunk( data );
+		var value;
+
+		switch ( chunk.id ) {
+
+			case INT_PERCENTAGE:
+				value = ( this.readShort( data ) / 100 );
+				break;
+
+			case FLOAT_PERCENTAGE:
+				value = this.readFloat( data );
+				break;
+
+			default:
+				this.debugMessage( '      Unknown percentage chunk: ' + chunk.toString( 16 ) );
+
+		}
+
+		this.endChunk( chunk );
+
+		return value;
+
+	},
+
 	/**
 	/**
 	 * Print debug message to the console.
 	 * Print debug message to the console.
 	 *
 	 *
@@ -902,8 +942,8 @@ var COLOR_F = 0x0010;
 var COLOR_24 = 0x0011;
 var COLOR_24 = 0x0011;
 var LIN_COLOR_24 = 0x0012;
 var LIN_COLOR_24 = 0x0012;
 var LIN_COLOR_F = 0x0013;
 var LIN_COLOR_F = 0x0013;
-// var INT_PERCENTAGE = 0x0030;
-// var FLOAT_PERCENTAGE = 0x0031;
+var INT_PERCENTAGE = 0x0030;
+var FLOAT_PERCENTAGE = 0x0031;
 var MDATA = 0x3D3D;
 var MDATA = 0x3D3D;
 var MESH_VERSION = 0x3D3E;
 var MESH_VERSION = 0x3D3E;
 var MASTER_SCALE = 0x0100;
 var MASTER_SCALE = 0x0100;

+ 12 - 0
examples/js/postprocessing/EffectComposer.js

@@ -81,6 +81,18 @@ Object.assign( THREE.EffectComposer.prototype, {
 
 
 	},
 	},
 
 
+	removePass: function ( pass ) {
+
+		const index = this.passes.indexOf( pass );
+
+		if ( index !== - 1 ) {
+
+			this.passes.splice( index, 1 );
+
+		}
+
+	},
+
 	isLastEnabledPass: function ( passIndex ) {
 	isLastEnabledPass: function ( passIndex ) {
 
 
 		for ( var i = passIndex + 1; i < this.passes.length; i ++ ) {
 		for ( var i = passIndex + 1; i < this.passes.length; i ++ ) {

+ 69 - 20
examples/jsm/controls/DragControls.js

@@ -27,25 +27,25 @@ var DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	function activate() {
 	function activate() {
 
 
-		_domElement.addEventListener( 'mousemove', onDocumentMouseMove, false );
-		_domElement.addEventListener( 'mousedown', onDocumentMouseDown, false );
-		_domElement.addEventListener( 'mouseup', onDocumentMouseCancel, false );
-		_domElement.addEventListener( 'mouseleave', onDocumentMouseCancel, false );
-		_domElement.addEventListener( 'touchmove', onDocumentTouchMove, false );
-		_domElement.addEventListener( 'touchstart', onDocumentTouchStart, false );
-		_domElement.addEventListener( 'touchend', onDocumentTouchEnd, false );
+		_domElement.addEventListener( 'pointermove', onPointerMove, false );
+		_domElement.addEventListener( 'pointerdown', onPointerDown, false );
+		_domElement.addEventListener( 'pointerup', onPointerCancel, false );
+		_domElement.addEventListener( 'pointerleave', onPointerCancel, false );
+		_domElement.addEventListener( 'touchmove', onTouchMove, false );
+		_domElement.addEventListener( 'touchstart', onTouchStart, false );
+		_domElement.addEventListener( 'touchend', onTouchEnd, false );
 
 
 	}
 	}
 
 
 	function deactivate() {
 	function deactivate() {
 
 
-		_domElement.removeEventListener( 'mousemove', onDocumentMouseMove, false );
-		_domElement.removeEventListener( 'mousedown', onDocumentMouseDown, false );
-		_domElement.removeEventListener( 'mouseup', onDocumentMouseCancel, false );
-		_domElement.removeEventListener( 'mouseleave', onDocumentMouseCancel, false );
-		_domElement.removeEventListener( 'touchmove', onDocumentTouchMove, false );
-		_domElement.removeEventListener( 'touchstart', onDocumentTouchStart, false );
-		_domElement.removeEventListener( 'touchend', onDocumentTouchEnd, false );
+		_domElement.removeEventListener( 'pointermove', onPointerMove, false );
+		_domElement.removeEventListener( 'pointerdown', onPointerDown, false );
+		_domElement.removeEventListener( 'pointerup', onPointerCancel, false );
+		_domElement.removeEventListener( 'pointerleave', onPointerCancel, false );
+		_domElement.removeEventListener( 'touchmove', onTouchMove, false );
+		_domElement.removeEventListener( 'touchstart', onTouchStart, false );
+		_domElement.removeEventListener( 'touchend', onTouchEnd, false );
 
 
 		_domElement.style.cursor = '';
 		_domElement.style.cursor = '';
 
 
@@ -63,10 +63,25 @@ var DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentMouseMove( event ) {
+	function onPointerMove( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 
+		switch ( event.pointerType ) {
+
+			case 'mouse':
+			case 'pen':
+				onMouseMove( event );
+				break;
+
+			// TODO touch
+
+		}
+
+	}
+
+	function onMouseMove( event ) {
+
 		var rect = _domElement.getBoundingClientRect();
 		var rect = _domElement.getBoundingClientRect();
 
 
 		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
 		_mouse.x = ( ( event.clientX - rect.left ) / rect.width ) * 2 - 1;
@@ -123,7 +138,24 @@ var DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentMouseDown( event ) {
+	function onPointerDown( event ) {
+
+		event.preventDefault();
+
+		switch ( event.pointerType ) {
+
+			case 'mouse':
+			case 'pen':
+				onMouseDown( event );
+				break;
+
+			// TODO touch
+
+		}
+
+	}
+
+	function onMouseDown( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 
@@ -152,7 +184,24 @@ var DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentMouseCancel( event ) {
+	function onPointerCancel( event ) {
+
+		event.preventDefault();
+
+		switch ( event.pointerType ) {
+
+			case 'mouse':
+			case 'pen':
+				onMouseCancel( event );
+				break;
+
+			// TODO touch
+
+		}
+
+	}
+
+	function onMouseCancel( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 
@@ -168,7 +217,7 @@ var DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentTouchMove( event ) {
+	function onTouchMove( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 		event = event.changedTouches[ 0 ];
 		event = event.changedTouches[ 0 ];
@@ -196,7 +245,7 @@ var DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentTouchStart( event ) {
+	function onTouchStart( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 		event = event.changedTouches[ 0 ];
 		event = event.changedTouches[ 0 ];
@@ -233,7 +282,7 @@ var DragControls = function ( _objects, _camera, _domElement ) {
 
 
 	}
 	}
 
 
-	function onDocumentTouchEnd( event ) {
+	function onTouchEnd( event ) {
 
 
 		event.preventDefault();
 		event.preventDefault();
 
 

+ 3 - 5
examples/jsm/controls/OrbitControls.js

@@ -810,8 +810,6 @@ var OrbitControls = function ( object, domElement ) {
 
 
 	function onPointerUp( event ) {
 	function onPointerUp( event ) {
 
 
-		if ( scope.enabled === false ) return;
-
 		switch ( event.pointerType ) {
 		switch ( event.pointerType ) {
 
 
 			case 'mouse':
 			case 'mouse':
@@ -971,13 +969,13 @@ var OrbitControls = function ( object, domElement ) {
 
 
 	function onMouseUp( event ) {
 	function onMouseUp( event ) {
 
 
+		scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, false );
+		scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp, false );
+
 		if ( scope.enabled === false ) return;
 		if ( scope.enabled === false ) return;
 
 
 		handleMouseUp( event );
 		handleMouseUp( event );
 
 
-		scope.domElement.ownerDocument.removeEventListener( 'pointermove', onPointerMove, false );
-		scope.domElement.ownerDocument.removeEventListener( 'pointerup', onPointerUp, false );
-
 		scope.dispatchEvent( endEvent );
 		scope.dispatchEvent( endEvent );
 
 
 		state = STATE.NONE;
 		state = STATE.NONE;

+ 3 - 3
examples/jsm/exporters/DRACOExporter.d.ts

@@ -1,6 +1,6 @@
 import {
 import {
-	BufferGeometry,
-	Geometry
+	Mesh,
+	Points
 } from '../../../src/Three';
 } from '../../../src/Three';
 
 
 export interface DRACOExporterOptions {
 export interface DRACOExporterOptions {
@@ -17,6 +17,6 @@ export class DRACOExporter {
 
 
 	constructor();
 	constructor();
 
 
-	parse( geometry: BufferGeometry | Geometry, options: DRACOExporterOptions ): Int8Array;
+	parse( object: Mesh | Points, options: DRACOExporterOptions ): Int8Array;
 
 
 }
 }

+ 84 - 32
examples/jsm/exporters/DRACOExporter.js

@@ -24,8 +24,13 @@ DRACOExporter.prototype = {
 
 
 	constructor: DRACOExporter,
 	constructor: DRACOExporter,
 
 
-	parse: function ( geometry, options ) {
+	parse: function ( object, options ) {
 
 
+		if ( object.isBufferGeometry === true || object.isGeometry === true ) {
+
+			throw new Error( 'DRACOExporter: The first parameter of parse() is now an instance of Mesh or Points.' );
+
+		}
 
 
 		if ( DracoEncoderModule === undefined ) {
 		if ( DracoEncoderModule === undefined ) {
 
 
@@ -49,15 +54,17 @@ DRACOExporter.prototype = {
 
 
 		}
 		}
 
 
+		var geometry = object.geometry;
+
 		var dracoEncoder = DracoEncoderModule();
 		var dracoEncoder = DracoEncoderModule();
 		var encoder = new dracoEncoder.Encoder();
 		var encoder = new dracoEncoder.Encoder();
-		var builder = new dracoEncoder.MeshBuilder();
-		var mesh = new dracoEncoder.Mesh();
+		var builder;
+		var dracoObject;
 
 
 		if ( geometry.isGeometry === true ) {
 		if ( geometry.isGeometry === true ) {
 
 
 			var bufferGeometry = new BufferGeometry();
 			var bufferGeometry = new BufferGeometry();
-			bufferGeometry.fromGeometry( geometry );
+			bufferGeometry.setFromObject( object );
 			geometry = bufferGeometry;
 			geometry = bufferGeometry;
 
 
 		}
 		}
@@ -68,63 +75,94 @@ DRACOExporter.prototype = {
 
 
 		}
 		}
 
 
-		var vertices = geometry.getAttribute( 'position' );
-		builder.AddFloatAttributeToMesh( mesh, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
+		if ( object.isMesh === true ) {
 
 
-		var faces = geometry.getIndex();
+			builder = new dracoEncoder.MeshBuilder();
+			dracoObject = new dracoEncoder.Mesh();
 
 
-		if ( faces !== null ) {
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
 
-			builder.AddFacesToMesh( mesh, faces.count / 3, faces.array );
+			var faces = geometry.getIndex();
 
 
-		} else {
+			if ( faces !== null ) {
 
 
-			var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+				builder.AddFacesToMesh( dracoObject, faces.count / 3, faces.array );
 
 
-			for ( var i = 0; i < faces.length; i ++ ) {
+			} else {
+
+				var faces = new ( vertices.count > 65535 ? Uint32Array : Uint16Array )( vertices.count );
+
+				for ( var i = 0; i < faces.length; i ++ ) {
+
+					faces[ i ] = i;
+
+				}
 
 
-				faces[ i ] = i;
+				builder.AddFacesToMesh( dracoObject, vertices.count, faces );
 
 
 			}
 			}
 
 
-			builder.AddFacesToMesh( mesh, vertices.count, faces );
+			if ( options.exportNormals === true ) {
 
 
-		}
+				var normals = geometry.getAttribute( 'normal' );
+
+				if ( normals !== undefined ) {
 
 
-		if ( options.exportNormals === true ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
 
 
-			var normals = geometry.getAttribute( 'normal' );
+				}
+
+			}
 
 
-			if ( normals !== undefined ) {
+			if ( options.exportUvs === true ) {
 
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.NORMAL, normals.count, normals.itemSize, normals.array );
+				var uvs = geometry.getAttribute( 'uv' );
+
+				if ( uvs !== undefined ) {
+
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+
+				}
 
 
 			}
 			}
 
 
-		}
+			if ( options.exportColor === true ) {
 
 
-		if ( options.exportUvs === true ) {
+				var colors = geometry.getAttribute( 'color' );
 
 
-			var uvs = geometry.getAttribute( 'uv' );
+				if ( colors !== undefined ) {
 
 
-			if ( uvs !== undefined ) {
+					builder.AddFloatAttributeToMesh( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
 
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.TEX_COORD, uvs.count, uvs.itemSize, uvs.array );
+				}
 
 
 			}
 			}
 
 
-		}
+		} else if ( object.isPoints === true ) {
+
+			builder = new dracoEncoder.PointCloudBuilder();
+			dracoObject = new dracoEncoder.PointCloud();
+
+			var vertices = geometry.getAttribute( 'position' );
+			builder.AddFloatAttribute( dracoObject, dracoEncoder.POSITION, vertices.count, vertices.itemSize, vertices.array );
 
 
-		if ( options.exportColor === true ) {
+			if ( options.exportColor === true ) {
 
 
-			var colors = geometry.getAttribute( 'color' );
+				var colors = geometry.getAttribute( 'color' );
 
 
-			if ( colors !== undefined ) {
+				if ( colors !== undefined ) {
 
 
-				builder.AddFloatAttributeToMesh( mesh, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+					builder.AddFloatAttribute( dracoObject, dracoEncoder.COLOR, colors.count, colors.itemSize, colors.array );
+
+				}
 
 
 			}
 			}
 
 
+		} else {
+
+			throw new Error( 'DRACOExporter: Unsupported object type.' );
+
 		}
 		}
 
 
 		//Compress using draco encoder
 		//Compress using draco encoder
@@ -133,7 +171,10 @@ DRACOExporter.prototype = {
 
 
 		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
 		//Sets the desired encoding and decoding speed for the given options from 0 (slowest speed, but the best compression) to 10 (fastest, but the worst compression).
 
 
-		encoder.SetSpeedOptions( options.encodeSpeed || 5, options.decodeSpeed || 5 );
+		var encodeSpeed = ( options.encodeSpeed !== undefined ) ? options.encodeSpeed : 5;
+		var decodeSpeed = ( options.decodeSpeed !== undefined ) ? options.decodeSpeed : 5;
+
+		encoder.SetSpeedOptions( encodeSpeed, decodeSpeed );
 
 
 		// Sets the desired encoding method for a given geometry.
 		// Sets the desired encoding method for a given geometry.
 
 
@@ -159,8 +200,19 @@ DRACOExporter.prototype = {
 
 
 		}
 		}
 
 
-		var length = encoder.EncodeMeshToDracoBuffer( mesh, encodedData );
-		dracoEncoder.destroy( mesh );
+		var length;
+
+		if ( object.isMesh === true ) {
+
+			length = encoder.EncodeMeshToDracoBuffer( dracoObject, encodedData );
+
+		} else {
+
+			length = encoder.EncodePointCloudToDracoBuffer( dracoObject, true, encodedData );
+
+		}
+
+		dracoEncoder.destroy( dracoObject );
 
 
 		if ( length === 0 ) {
 		if ( length === 0 ) {
 
 

+ 63 - 6
examples/jsm/exporters/GLTFExporter.js

@@ -15,6 +15,7 @@ import {
 	NearestMipmapNearestFilter,
 	NearestMipmapNearestFilter,
 	PropertyBinding,
 	PropertyBinding,
 	RGBAFormat,
 	RGBAFormat,
+	RGBFormat,
 	RepeatWrapping,
 	RepeatWrapping,
 	Scene,
 	Scene,
 	Vector3
 	Vector3
@@ -23,6 +24,7 @@ import {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
 // Constants
 // Constants
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
+
 var WEBGL_CONSTANTS = {
 var WEBGL_CONSTANTS = {
 	POINTS: 0x0000,
 	POINTS: 0x0000,
 	LINES: 0x0001,
 	LINES: 0x0001,
@@ -233,7 +235,23 @@ GLTFExporter.prototype = {
 
 
 				for ( var a = 0; a < attribute.itemSize; a ++ ) {
 				for ( var a = 0; a < attribute.itemSize; a ++ ) {
 
 
-					var value = attribute.array[ i * attribute.itemSize + a ];
+					var value;
+
+					if ( attribute.itemSize > 4 ) {
+
+						 // no support for interleaved data for itemSize > 4
+
+						value = attribute.array[ i * attribute.itemSize + a ];
+
+					} else {
+
+						if ( a === 0 ) value = attribute.getX( i );
+						else if ( a === 1 ) value = attribute.getY( i );
+						else if ( a === 2 ) value = attribute.getZ( i );
+						else if ( a === 3 ) value = attribute.getW( i );
+
+					}
+
 					output.min[ a ] = Math.min( output.min[ a ], value );
 					output.min[ a ] = Math.min( output.min[ a ], value );
 					output.max[ a ] = Math.max( output.max[ a ], value );
 					output.max[ a ] = Math.max( output.max[ a ], value );
 
 
@@ -265,7 +283,7 @@ GLTFExporter.prototype = {
 			for ( var i = 0, il = normal.count; i < il; i ++ ) {
 			for ( var i = 0, il = normal.count; i < il; i ++ ) {
 
 
 				// 0.0005 is from glTF-validator
 				// 0.0005 is from glTF-validator
-				if ( Math.abs( v.fromArray( normal.array, i * 3 ).length() - 1.0 ) > 0.0005 ) return false;
+				if ( Math.abs( v.fromBufferAttribute( normal, i ).length() - 1.0 ) > 0.0005 ) return false;
 
 
 			}
 			}
 
 
@@ -294,7 +312,7 @@ GLTFExporter.prototype = {
 
 
 			for ( var i = 0, il = attribute.count; i < il; i ++ ) {
 			for ( var i = 0, il = attribute.count; i < il; i ++ ) {
 
 
-				v.fromArray( attribute.array, i * 3 );
+				v.fromBufferAttribute( attribute, i );
 
 
 				if ( v.x === 0 && v.y === 0 && v.z === 0 ) {
 				if ( v.x === 0 && v.y === 0 && v.z === 0 ) {
 
 
@@ -307,7 +325,7 @@ GLTFExporter.prototype = {
 
 
 				}
 				}
 
 
-				v.toArray( attribute.array, i * 3 );
+				attribute.setXYZ( i, v.x, v.y, v.z );
 
 
 			}
 			}
 
 
@@ -752,7 +770,7 @@ GLTFExporter.prototype = {
 		/**
 		/**
 		 * Process image
 		 * Process image
 		 * @param  {Image} image to process
 		 * @param  {Image} image to process
-		 * @param  {Integer} format of the image (e.g. THREE.RGBFormat, RGBAFormat etc)
+		 * @param  {Integer} format of the image (e.g. RGBFormat, RGBAFormat etc)
 		 * @param  {Boolean} flipY before writing out the image
 		 * @param  {Boolean} flipY before writing out the image
 		 * @return {Integer}     Index of the processed texture in the "images" array
 		 * @return {Integer}     Index of the processed texture in the "images" array
 		 */
 		 */
@@ -798,7 +816,46 @@ GLTFExporter.prototype = {
 
 
 				}
 				}
 
 
-				ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
+				if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
+					( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ||
+					( typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap ) ) {
+
+					ctx.drawImage( image, 0, 0, canvas.width, canvas.height );
+
+				} else {
+
+					if ( format !== RGBAFormat && format !== RGBFormat ) {
+
+						console.error( 'GLTFExporter: Only RGB and RGBA formats are supported.' );
+
+					}
+
+					if ( image.width > options.maxTextureSize || image.height > options.maxTextureSize ) {
+
+						console.warn( 'GLTFExporter: Image size is bigger than maxTextureSize', image );
+
+					}
+
+					let data = image.data;
+
+					if ( format === RGBFormat ) {
+
+						data = new Uint8ClampedArray( image.height * image.width * 4 );
+
+						for ( var i = 0, j = 0; i < data.length; i += 4, j += 3 ) {
+
+							data[ i + 0 ] = image.data[ j + 0 ];
+							data[ i + 1 ] = image.data[ j + 1 ];
+							data[ i + 2 ] = image.data[ j + 2 ];
+							data[ i + 3 ] = 255;
+
+						}
+
+					}
+
+					ctx.putImageData( new ImageData( data, image.width, image.height ), 0, 0 );
+
+				}
 
 
 				if ( options.binary === true ) {
 				if ( options.binary === true ) {
 
 

+ 72 - 0
examples/jsm/exporters/OBJExporter.js

@@ -1,9 +1,11 @@
 import {
 import {
 	BufferGeometry,
 	BufferGeometry,
+	Color,
 	Geometry,
 	Geometry,
 	Line,
 	Line,
 	Matrix3,
 	Matrix3,
 	Mesh,
 	Mesh,
+	Points,
 	Vector2,
 	Vector2,
 	Vector3
 	Vector3
 } from "../../../build/three.module.js";
 } from "../../../build/three.module.js";
@@ -23,6 +25,7 @@ OBJExporter.prototype = {
 		var indexNormals = 0;
 		var indexNormals = 0;
 
 
 		var vertex = new Vector3();
 		var vertex = new Vector3();
+		var color = new Color();
 		var normal = new Vector3();
 		var normal = new Vector3();
 		var uv = new Vector2();
 		var uv = new Vector2();
 
 
@@ -245,6 +248,69 @@ OBJExporter.prototype = {
 
 
 		};
 		};
 
 
+		var parsePoints = function ( points ) {
+
+			var nbVertex = 0;
+
+			var geometry = points.geometry;
+
+			if ( geometry instanceof Geometry ) {
+
+				geometry = new BufferGeometry().setFromObject( points );
+
+			}
+
+			if ( geometry instanceof BufferGeometry ) {
+
+				var vertices = geometry.getAttribute( 'position' );
+				var colors = geometry.getAttribute( 'color' );
+
+				output += 'o ' + points.name + '\n';
+
+				if ( vertices !== undefined ) {
+
+					for ( i = 0, l = vertices.count; i < l; i ++, nbVertex ++ ) {
+
+						vertex.fromBufferAttribute( vertices, i );
+						vertex.applyMatrix4( points.matrixWorld );
+
+						output += 'v ' + vertex.x + ' ' + vertex.y + ' ' + vertex.z;
+
+						if ( colors !== undefined ) {
+
+							color.fromBufferAttribute( colors, i );
+
+							output += ' ' + color.r + ' ' + color.g + ' ' + color.b;
+
+						}
+
+						output += '\n';
+
+					}
+
+				}
+
+				output += 'p ';
+
+				for ( j = 1, l = vertices.count; j <= l; j ++ ) {
+
+					output += ( indexVertex + j ) + ' ';
+
+				}
+
+				output += '\n';
+
+			} else {
+
+				console.warn( 'THREE.OBJExporter.parsePoints(): geometry type unsupported', geometry );
+
+			}
+
+			// update index
+			indexVertex += nbVertex;
+
+		};
+
 		object.traverse( function ( child ) {
 		object.traverse( function ( child ) {
 
 
 			if ( child instanceof Mesh ) {
 			if ( child instanceof Mesh ) {
@@ -259,6 +325,12 @@ OBJExporter.prototype = {
 
 
 			}
 			}
 
 
+			if ( child instanceof Points ) {
+
+				parsePoints( child );
+
+			}
+
 		} );
 		} );
 
 
 		return output;
 		return output;

+ 1 - 1
examples/jsm/exporters/PLYExporter.js

@@ -51,7 +51,7 @@ PLYExporter.prototype = {
 
 
 					if ( geometry.isBufferGeometry === true ) {
 					if ( geometry.isBufferGeometry === true ) {
 
 
-						if ( geometry.getAttribute( 'position' ) !== undefined ) {
+						if ( geometry.hasAttribute( 'position' ) === true ) {
 
 
 							cb( mesh, geometry );
 							cb( mesh, geometry );
 
 

+ 39 - 5
examples/jsm/loaders/3DMLoader.js

@@ -442,6 +442,13 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				var points = new Points( geometry, material );
 				var points = new Points( geometry, material );
 				points.userData[ 'attributes' ] = attributes;
 				points.userData[ 'attributes' ] = attributes;
 				points.userData[ 'objectType' ] = obj.objectType;
 				points.userData[ 'objectType' ] = obj.objectType;
+
+				if ( attributes.name ) {
+
+					points.name = attributes.name;
+
+				}
+
 				return points;
 				return points;
 
 
 			case 'Mesh':
 			case 'Mesh':
@@ -469,6 +476,12 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				mesh.userData[ 'attributes' ] = attributes;
 				mesh.userData[ 'attributes' ] = attributes;
 				mesh.userData[ 'objectType' ] = obj.objectType;
 				mesh.userData[ 'objectType' ] = obj.objectType;
 
 
+				if ( attributes.name ) {
+
+					mesh.name = attributes.name;
+
+				}
+
 				return mesh;
 				return mesh;
 
 
 			case 'Brep':
 			case 'Brep':
@@ -489,6 +502,12 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				brepObject.userData[ 'attributes' ] = attributes;
 				brepObject.userData[ 'attributes' ] = attributes;
 				brepObject.userData[ 'objectType' ] = obj.objectType;
 				brepObject.userData[ 'objectType' ] = obj.objectType;
 
 
+				if ( attributes.name ) {
+
+					brepObject.name = attributes.name;
+
+				}
+
 				return brepObject;
 				return brepObject;
 
 
 			case 'Curve':
 			case 'Curve':
@@ -505,6 +524,12 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				lines.userData[ 'attributes' ] = attributes;
 				lines.userData[ 'attributes' ] = attributes;
 				lines.userData[ 'objectType' ] = obj.objectType;
 				lines.userData[ 'objectType' ] = obj.objectType;
 
 
+				if ( attributes.name ) {
+
+					lines.name = attributes.name;
+
+				}
+
 				return lines;
 				return lines;
 
 
 			case 'TextDot':
 			case 'TextDot':
@@ -547,6 +572,12 @@ Rhino3dmLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 				sprite.userData[ 'attributes' ] = attributes;
 				sprite.userData[ 'attributes' ] = attributes;
 				sprite.userData[ 'objectType' ] = obj.objectType;
 				sprite.userData[ 'objectType' ] = obj.objectType;
 
 
+				if ( attributes.name ) {
+
+					sprite.name = attributes.name;
+
+				}
+
 				return sprite;
 				return sprite;
 
 
 			case 'Light':
 			case 'Light':
@@ -815,12 +846,17 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 
 		//Handle objects
 		//Handle objects
 
 
-		for ( var i = 0; i < doc.objects().count; i ++ ) {
+		var objs = doc.objects();
+		var cnt = objs.count;
+
+		for ( var i = 0; i < cnt; i ++ ) {
 
 
-			var _object = doc.objects().get( i );
+			var _object = objs.get( i );
 
 
 			var object = extractObjectData( _object, doc );
 			var object = extractObjectData( _object, doc );
 
 
+			_object.delete();
+
 			if ( object !== undefined ) {
 			if ( object !== undefined ) {
 
 
 				if ( object.attributes.materialIndex >= 0 ) {
 				if ( object.attributes.materialIndex >= 0 ) {
@@ -834,8 +870,6 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 
 			}
 			}
 
 
-			_object.delete();
-
 		}
 		}
 
 
 		// Handle instance definitions
 		// Handle instance definitions
@@ -1303,7 +1337,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 		if ( curve instanceof rhino.ArcCurve ) {
 		if ( curve instanceof rhino.ArcCurve ) {
 
 
 			pointCount = Math.floor( curve.angleDegrees / 5 );
 			pointCount = Math.floor( curve.angleDegrees / 5 );
-			pointCount = pointCount < 1 ? 2 : pointCount;
+			pointCount = pointCount < 2 ? 2 : pointCount;
 			// alternative to this hardcoded version: https://stackoverflow.com/a/18499923/2179399
 			// alternative to this hardcoded version: https://stackoverflow.com/a/18499923/2179399
 
 
 		}
 		}

+ 14 - 1
examples/jsm/loaders/3MFLoader.js

@@ -1411,11 +1411,24 @@ ThreeMFLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 
 		}
 		}
 
 
+		function fetch3DModelPart( rels ) {
+
+			for ( var i = 0; i < rels.length; i ++ ) {
+
+				var rel = rels[ i ];
+				var extension = rel.target.split( '.' ).pop();
+
+				if ( extension.toLowerCase() === 'model' ) return rel;
+
+			}
+
+		}
+
 		function build( objects, data3mf ) {
 		function build( objects, data3mf ) {
 
 
 			var group = new Group();
 			var group = new Group();
 
 
-			var relationship = data3mf[ 'rels' ][ 0 ];
+			var relationship = fetch3DModelPart( data3mf[ 'rels' ] );
 			var buildData = data3mf.model[ relationship[ 'target' ].substring( 1 ) ][ 'build' ];
 			var buildData = data3mf.model[ relationship[ 'target' ].substring( 1 ) ][ 'build' ];
 
 
 			for ( var i = 0; i < buildData.length; i ++ ) {
 			for ( var i = 0; i < buildData.length; i ++ ) {

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

@@ -1,5 +1,4 @@
 import {
 import {
-	AnimationClip,
 	Loader,
 	Loader,
 	LoadingManager,
 	LoadingManager,
 	Scene
 	Scene
@@ -7,7 +6,6 @@ import {
 
 
 
 
 export interface Collada {
 export interface Collada {
-	animations: AnimationClip[];
 	kinematics: object;
 	kinematics: object;
 	library: object;
 	library: object;
 	scene: Scene;
 	scene: Scene;

+ 7 - 1
examples/jsm/loaders/ColladaLoader.js

@@ -3995,6 +3995,7 @@ ColladaLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 		setupKinematics();
 		setupKinematics();
 
 
 		var scene = parseScene( getElementsByTagName( collada, 'scene' )[ 0 ] );
 		var scene = parseScene( getElementsByTagName( collada, 'scene' )[ 0 ] );
+		scene.animations = animations;
 
 
 		if ( asset.upAxis === 'Z_UP' ) {
 		if ( asset.upAxis === 'Z_UP' ) {
 
 
@@ -4005,7 +4006,12 @@ ColladaLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 		scene.scale.multiplyScalar( asset.unit );
 		scene.scale.multiplyScalar( asset.unit );
 
 
 		return {
 		return {
-			animations: animations,
+			get animations() {
+
+				console.warn( 'THREE.ColladaLoader: Please access animations over scene.animations now.' );
+				return animations;
+
+			},
 			kinematics: kinematics,
 			kinematics: kinematics,
 			library: library,
 			library: library,
 			scene: scene
 			scene: scene

+ 4 - 0
examples/jsm/loaders/GLTFLoader.d.ts

@@ -11,6 +11,7 @@ import {
 
 
 import { DRACOLoader } from './DRACOLoader';
 import { DRACOLoader } from './DRACOLoader';
 import { DDSLoader } from './DDSLoader';
 import { DDSLoader } from './DDSLoader';
+import { KTX2Loader } from './KTX2Loader';
 
 
 export interface GLTF {
 export interface GLTF {
 	animations: AnimationClip[];
 	animations: AnimationClip[];
@@ -38,6 +39,9 @@ export class GLTFLoader extends Loader {
 	load( url: string, onLoad: ( gltf: GLTF ) => void, onProgress?: ( event: ProgressEvent ) => void, onError?: ( event: ErrorEvent ) => void ) : void;
 	load( url: string, onLoad: ( gltf: GLTF ) => void, onProgress?: ( event: ProgressEvent ) => void, onError?: ( event: ErrorEvent ) => void ) : void;
 	setDRACOLoader( dracoLoader: DRACOLoader ): GLTFLoader;
 	setDRACOLoader( dracoLoader: DRACOLoader ): GLTFLoader;
 	setDDSLoader( ddsLoader: DDSLoader ): GLTFLoader;
 	setDDSLoader( ddsLoader: DDSLoader ): GLTFLoader;
+	setKTX2Loader( ktx2Loader: KTX2Loader ): GLTFLoader;
+	setMeshoptDecoder( meshoptDecoder: /* MeshoptDecoder */ any ): GLTFLoader;
+
 	parse( data: ArrayBuffer | string, path: string, onLoad: ( gltf: GLTF ) => void, onError?: ( event: ErrorEvent ) => void ) : void;
 	parse( data: ArrayBuffer | string, path: string, onLoad: ( gltf: GLTF ) => void, onError?: ( event: ErrorEvent ) => void ) : void;
 
 
 }
 }

+ 3 - 2
examples/jsm/loaders/LottieLoader.js

@@ -1,7 +1,8 @@
 import {
 import {
 	FileLoader,
 	FileLoader,
 	Loader,
 	Loader,
-	CanvasTexture
+	CanvasTexture,
+	NearestFilter
 } from "../../../build/three.module.js";
 } from "../../../build/three.module.js";
 
 
 var LottieLoader = function ( manager ) {
 var LottieLoader = function ( manager ) {
@@ -25,7 +26,7 @@ LottieLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 		const quality = this._quality || 1;
 		const quality = this._quality || 1;
 
 
 		const texture = new CanvasTexture();
 		const texture = new CanvasTexture();
-		texture.anisotropy = 16;
+		texture.minFilter = NearestFilter;
 
 
 		const loader = new FileLoader( this.manager );
 		const loader = new FileLoader( this.manager );
 		loader.setPath( this.path );
 		loader.setPath( this.path );

+ 115 - 78
examples/jsm/loaders/OBJLoader.js

@@ -391,7 +391,10 @@ var OBJLoader = ( function () {
 
 
 				for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
 				for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
 
 
-					this.addVertexPoint( this.parseVertexIndex( vertices[ vi ], vLen ) );
+					var index = this.parseVertexIndex( vertices[ vi ], vLen );
+
+					this.addVertexPoint( index );
+					this.addColor( index );
 
 
 				}
 				}
 
 
@@ -724,147 +727,181 @@ var OBJLoader = ( function () {
 			var container = new Group();
 			var container = new Group();
 			container.materialLibraries = [].concat( state.materialLibraries );
 			container.materialLibraries = [].concat( state.materialLibraries );
 
 
-			for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
+			var hasPrimitives = ! ( state.objects.length === 1 && state.objects[ 0 ].geometry.vertices.length === 0 );
 
 
-				var object = state.objects[ i ];
-				var geometry = object.geometry;
-				var materials = object.materials;
-				var isLine = ( geometry.type === 'Line' );
-				var isPoints = ( geometry.type === 'Points' );
-				var hasVertexColors = false;
+			if ( hasPrimitives === true ) {
 
 
-				// Skip o/g line declarations that did not follow with any faces
-				if ( geometry.vertices.length === 0 ) continue;
+				for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
 
 
-				var buffergeometry = new BufferGeometry();
+					var object = state.objects[ i ];
+					var geometry = object.geometry;
+					var materials = object.materials;
+					var isLine = ( geometry.type === 'Line' );
+					var isPoints = ( geometry.type === 'Points' );
+					var hasVertexColors = false;
 
 
-				buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );
+					// Skip o/g line declarations that did not follow with any faces
+					if ( geometry.vertices.length === 0 ) continue;
 
 
-				buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );
+					var buffergeometry = new BufferGeometry();
 
 
-				if ( geometry.colors.length > 0 ) {
+					buffergeometry.setAttribute( 'position', new Float32BufferAttribute( geometry.vertices, 3 ) );
 
 
-					hasVertexColors = true;
-					buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );
+					if ( geometry.normals.length > 0 ) {
 
 
-				}
+						buffergeometry.setAttribute( 'normal', new Float32BufferAttribute( geometry.normals, 3 ) );
 
 
-				if ( geometry.hasUVIndices === true ) {
+					}
 
 
-					buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );
+					if ( geometry.colors.length > 0 ) {
 
 
-				}
+						hasVertexColors = true;
+						buffergeometry.setAttribute( 'color', new Float32BufferAttribute( geometry.colors, 3 ) );
+
+					}
+
+					if ( geometry.hasUVIndices === true ) {
 
 
-				// Create materials
+						buffergeometry.setAttribute( 'uv', new Float32BufferAttribute( geometry.uvs, 2 ) );
 
 
-				var createdMaterials = [];
+					}
+
+					// Create materials
 
 
-				for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+					var createdMaterials = [];
 
 
-					var sourceMaterial = materials[ mi ];
-					var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
-					var material = state.materials[ materialHash ];
+					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
 
-					if ( this.materials !== null ) {
+						var sourceMaterial = materials[ mi ];
+						var materialHash = sourceMaterial.name + '_' + sourceMaterial.smooth + '_' + hasVertexColors;
+						var material = state.materials[ materialHash ];
 
 
-						material = this.materials.create( sourceMaterial.name );
+						if ( this.materials !== null ) {
 
 
-						// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
-						if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {
+							material = this.materials.create( sourceMaterial.name );
 
 
-							var materialLine = new LineBasicMaterial();
-							Material.prototype.copy.call( materialLine, material );
-							materialLine.color.copy( material.color );
-							material = materialLine;
+							// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
+							if ( isLine && material && ! ( material instanceof LineBasicMaterial ) ) {
 
 
-						} else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {
+								var materialLine = new LineBasicMaterial();
+								Material.prototype.copy.call( materialLine, material );
+								materialLine.color.copy( material.color );
+								material = materialLine;
 
 
-							var materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );
-							Material.prototype.copy.call( materialPoints, material );
-							materialPoints.color.copy( material.color );
-							materialPoints.map = material.map;
-							material = materialPoints;
+							} else if ( isPoints && material && ! ( material instanceof PointsMaterial ) ) {
+
+								var materialPoints = new PointsMaterial( { size: 10, sizeAttenuation: false } );
+								Material.prototype.copy.call( materialPoints, material );
+								materialPoints.color.copy( material.color );
+								materialPoints.map = material.map;
+								material = materialPoints;
+
+							}
 
 
 						}
 						}
 
 
-					}
+						if ( material === undefined ) {
 
 
-					if ( material === undefined ) {
+							if ( isLine ) {
 
 
-						if ( isLine ) {
+								material = new LineBasicMaterial();
 
 
-							material = new LineBasicMaterial();
+							} else if ( isPoints ) {
 
 
-						} else if ( isPoints ) {
+								material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
 
 
-							material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
+							} else {
 
 
-						} else {
+								material = new MeshPhongMaterial();
 
 
-							material = new MeshPhongMaterial();
+							}
 
 
-						}
+							material.name = sourceMaterial.name;
+							material.flatShading = sourceMaterial.smooth ? false : true;
+							material.vertexColors = hasVertexColors;
 
 
-						material.name = sourceMaterial.name;
-						material.flatShading = sourceMaterial.smooth ? false : true;
-						material.vertexColors = hasVertexColors;
+							state.materials[ materialHash ] = material;
 
 
-						state.materials[ materialHash ] = material;
+						}
+
+						createdMaterials.push( material );
 
 
 					}
 					}
 
 
-					createdMaterials.push( material );
+					// Create mesh
 
 
-				}
+					var mesh;
 
 
-				// Create mesh
+					if ( createdMaterials.length > 1 ) {
 
 
-				var mesh;
+						for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
 
 
-				if ( createdMaterials.length > 1 ) {
+							var sourceMaterial = materials[ mi ];
+							buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
 
 
-					for ( var mi = 0, miLen = materials.length; mi < miLen; mi ++ ) {
+						}
 
 
-						var sourceMaterial = materials[ mi ];
-						buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
+						if ( isLine ) {
 
 
-					}
+							mesh = new LineSegments( buffergeometry, createdMaterials );
 
 
-					if ( isLine ) {
+						} else if ( isPoints ) {
 
 
-						mesh = new LineSegments( buffergeometry, createdMaterials );
+							mesh = new Points( buffergeometry, createdMaterials );
 
 
-					} else if ( isPoints ) {
+						} else {
 
 
-						mesh = new Points( buffergeometry, createdMaterials );
+							mesh = new Mesh( buffergeometry, createdMaterials );
+
+						}
 
 
 					} else {
 					} else {
 
 
-						mesh = new Mesh( buffergeometry, createdMaterials );
+						if ( isLine ) {
 
 
-					}
+							mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );
 
 
-				} else {
+						} else if ( isPoints ) {
 
 
-					if ( isLine ) {
+							mesh = new Points( buffergeometry, createdMaterials[ 0 ] );
 
 
-						mesh = new LineSegments( buffergeometry, createdMaterials[ 0 ] );
+						} else {
 
 
-					} else if ( isPoints ) {
+							mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );
 
 
-						mesh = new Points( buffergeometry, createdMaterials[ 0 ] );
+						}
 
 
-					} else {
+					}
 
 
-						mesh = new Mesh( buffergeometry, createdMaterials[ 0 ] );
+					mesh.name = object.name;
 
 
-					}
+					container.add( mesh );
 
 
 				}
 				}
 
 
-				mesh.name = object.name;
+			} else {
+
+				// if there is only the default parser state object with no geometry data, interpret data as point cloud
+
+				if ( state.vertices.length > 0 ) {
+
+					var material = new PointsMaterial( { size: 1, sizeAttenuation: false } );
+
+					var buffergeometry = new BufferGeometry();
+
+					buffergeometry.setAttribute( 'position', new Float32BufferAttribute( state.vertices, 3 ) );
 
 
-				container.add( mesh );
+					if ( state.colors.length > 0 && state.colors[ 0 ] !== undefined ) {
+
+						buffergeometry.setAttribute( 'color', new Float32BufferAttribute( state.colors, 3 ) );
+						material.vertexColors = true;
+
+					}
+
+					var points = new Points( buffergeometry, material );
+					container.add( points );
+
+				}
 
 
 			}
 			}
 
 

+ 58 - 18
examples/jsm/loaders/TDSLoader.js

@@ -302,16 +302,16 @@ TDSLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 
 			} else if ( next === MAT_SHININESS ) {
 			} else if ( next === MAT_SHININESS ) {
 
 
-				var shininess = this.readWord( data );
-				material.shininess = shininess;
+				var shininess = this.readPercentage( data );
+				material.shininess = shininess * 100;
 				this.debugMessage( '   Shininess : ' + shininess );
 				this.debugMessage( '   Shininess : ' + shininess );
 
 
 			} else if ( next === MAT_TRANSPARENCY ) {
 			} else if ( next === MAT_TRANSPARENCY ) {
 
 
-				var opacity = this.readWord( data );
-				material.opacity = opacity * 0.01;
-				this.debugMessage( '  Opacity : ' + opacity );
-				material.transparent = opacity < 100 ? true : false;
+				var transparency = this.readPercentage( data );
+				material.opacity = 1 - transparency;
+				this.debugMessage( '  Transparency : ' + transparency );
+				material.transparent = material.opacity < 1 ? true : false;
 
 
 			} else if ( next === MAT_TEXMAP ) {
 			} else if ( next === MAT_TEXMAP ) {
 
 
@@ -510,42 +510,49 @@ TDSLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 
 		//The rest of the FACE_ARRAY chunk is subchunks
 		//The rest of the FACE_ARRAY chunk is subchunks
 
 
+		var materialIndex = 0;
+		var start = 0;
+
 		while ( this.position < chunk.end ) {
 		while ( this.position < chunk.end ) {
 
 
-			var chunk = this.readChunk( data );
+			var subchunk = this.readChunk( data );
 
 
-			if ( chunk.id === MSH_MAT_GROUP ) {
+			if ( subchunk.id === MSH_MAT_GROUP ) {
 
 
 				this.debugMessage( '      Material Group' );
 				this.debugMessage( '      Material Group' );
 
 
 				this.resetPosition( data );
 				this.resetPosition( data );
 
 
 				var group = this.readMaterialGroup( data );
 				var group = this.readMaterialGroup( data );
+				var count = group.index.length * 3; // assuming successive indices
 
 
-				var material = this.materials[ group.name ];
+				mesh.geometry.addGroup( start, count, materialIndex );
 
 
-				if ( material !== undefined )	{
+				start += count;
+				materialIndex ++;
 
 
-					mesh.material = material;
+				var material = this.materials[ group.name ];
 
 
-					if ( material.name === '' )		{
+				if ( Array.isArray( mesh.material ) === false ) mesh.material = [];
 
 
-						material.name = mesh.name;
+				if ( material !== undefined )	{
 
 
-					}
+					mesh.material.push( material );
 
 
 				}
 				}
 
 
 			} else {
 			} else {
 
 
-				this.debugMessage( '      Unknown face array chunk: ' + chunk.toString( 16 ) );
+				this.debugMessage( '      Unknown face array chunk: ' + subchunk.toString( 16 ) );
 
 
 			}
 			}
 
 
-			this.endChunk( chunk );
+			this.endChunk( subchunk );
 
 
 		}
 		}
 
 
+		if ( mesh.material.length === 1 ) mesh.material = mesh.material[ 0 ]; // for backwards compatibility
+
 		this.endChunk( chunk );
 		this.endChunk( chunk );
 
 
 	},
 	},
@@ -885,6 +892,39 @@ TDSLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
 
 
 	},
 	},
 
 
+	/**
+	 * Read percentage value.
+	 *
+	 * @method readPercentage
+	 * @param {DataView} data Dataview to read data from.
+	 * @return {Number} Data read from the dataview.
+	 */
+	readPercentage: function ( data ) {
+
+		var chunk = this.readChunk( data );
+		var value;
+
+		switch ( chunk.id ) {
+
+			case INT_PERCENTAGE:
+				value = ( this.readShort( data ) / 100 );
+				break;
+
+			case FLOAT_PERCENTAGE:
+				value = this.readFloat( data );
+				break;
+
+			default:
+				this.debugMessage( '      Unknown percentage chunk: ' + chunk.toString( 16 ) );
+
+		}
+
+		this.endChunk( chunk );
+
+		return value;
+
+	},
+
 	/**
 	/**
 	 * Print debug message to the console.
 	 * Print debug message to the console.
 	 *
 	 *
@@ -918,8 +958,8 @@ var COLOR_F = 0x0010;
 var COLOR_24 = 0x0011;
 var COLOR_24 = 0x0011;
 var LIN_COLOR_24 = 0x0012;
 var LIN_COLOR_24 = 0x0012;
 var LIN_COLOR_F = 0x0013;
 var LIN_COLOR_F = 0x0013;
-// var INT_PERCENTAGE = 0x0030;
-// var FLOAT_PERCENTAGE = 0x0031;
+var INT_PERCENTAGE = 0x0030;
+var FLOAT_PERCENTAGE = 0x0031;
 var MDATA = 0x3D3D;
 var MDATA = 0x3D3D;
 var MESH_VERSION = 0x3D3E;
 var MESH_VERSION = 0x3D3E;
 var MASTER_SCALE = 0x0100;
 var MASTER_SCALE = 0x0100;

+ 3 - 2
examples/jsm/math/MeshSurfaceSampler.d.ts

@@ -1,5 +1,6 @@
 import {
 import {
 	BufferGeometry,
 	BufferGeometry,
+	Color,
 	Mesh,
 	Mesh,
 	Vector3
 	Vector3
 } from '../../../src/Three';
 } from '../../../src/Three';
@@ -14,8 +15,8 @@ export class MeshSurfaceSampler {
 	constructor( mesh: Mesh );
 	constructor( mesh: Mesh );
 	binarySearch( x: number ): number;
 	binarySearch( x: number ): number;
 	build(): this;
 	build(): this;
-	sample( targetPosition: Vector3, targetNormal: Vector3 ): this;
-	sampleFace( faceIndex: number, targetPosition: Vector3, targetNormal: Vector3 ): this;
+	sample( targetPosition: Vector3, targetNormal?: Vector3, targetColor?: Color ): this;
+	sampleFace( faceIndex: number, targetPosition: Vector3, targetNormal?: Vector3, targetColor?: Color ): this;
 	setWeightAttribute( name: string | null ): this;
 	setWeightAttribute( name: string | null ): this;
 
 
 }
 }

+ 30 - 5
examples/jsm/math/MeshSurfaceSampler.js

@@ -1,5 +1,6 @@
 import {
 import {
-	Triangle
+	Triangle,
+	Vector3
 } from "../../../build/three.module.js";
 } from "../../../build/three.module.js";
 
 
 /**
 /**
@@ -15,6 +16,7 @@ import {
 var MeshSurfaceSampler = ( function () {
 var MeshSurfaceSampler = ( function () {
 
 
 	var _face = new Triangle();
 	var _face = new Triangle();
+	var _color = new Vector3();
 
 
 	function MeshSurfaceSampler( mesh ) {
 	function MeshSurfaceSampler( mesh ) {
 
 
@@ -38,6 +40,7 @@ var MeshSurfaceSampler = ( function () {
 		this.randomFunction = Math.random;
 		this.randomFunction = Math.random;
 
 
 		this.positionAttribute = this.geometry.getAttribute( 'position' );
 		this.positionAttribute = this.geometry.getAttribute( 'position' );
+		this.colorAttribute = this.geometry.getAttribute( 'color' );
 		this.weightAttribute = null;
 		this.weightAttribute = null;
 
 
 		this.distribution = null;
 		this.distribution = null;
@@ -112,13 +115,13 @@ var MeshSurfaceSampler = ( function () {
 
 
 		},
 		},
 
 
-		sample: function ( targetPosition, targetNormal ) {
+		sample: function ( targetPosition, targetNormal, targetColor ) {
 
 
 			var cumulativeTotal = this.distribution[ this.distribution.length - 1 ];
 			var cumulativeTotal = this.distribution[ this.distribution.length - 1 ];
 
 
 			var faceIndex = this.binarySearch( this.randomFunction() * cumulativeTotal );
 			var faceIndex = this.binarySearch( this.randomFunction() * cumulativeTotal );
 
 
-			return this.sampleFace( faceIndex, targetPosition, targetNormal );
+			return this.sampleFace( faceIndex, targetPosition, targetNormal, targetColor );
 
 
 		},
 		},
 
 
@@ -156,7 +159,7 @@ var MeshSurfaceSampler = ( function () {
 
 
 		},
 		},
 
 
-		sampleFace: function ( faceIndex, targetPosition, targetNormal ) {
+		sampleFace: function ( faceIndex, targetPosition, targetNormal, targetColor ) {
 
 
 			var u = this.randomFunction();
 			var u = this.randomFunction();
 			var v = this.randomFunction();
 			var v = this.randomFunction();
@@ -178,7 +181,29 @@ var MeshSurfaceSampler = ( function () {
 				.addScaledVector( _face.b, v )
 				.addScaledVector( _face.b, v )
 				.addScaledVector( _face.c, 1 - ( u + v ) );
 				.addScaledVector( _face.c, 1 - ( u + v ) );
 
 
-			_face.getNormal( targetNormal );
+			if ( targetNormal !== undefined ) {
+
+				_face.getNormal( targetNormal );
+
+			}
+
+			if ( targetColor !== undefined && this.colorAttribute !== undefined ) {
+
+				_face.a.fromBufferAttribute( this.colorAttribute, faceIndex * 3 );
+				_face.b.fromBufferAttribute( this.colorAttribute, faceIndex * 3 + 1 );
+				_face.c.fromBufferAttribute( this.colorAttribute, faceIndex * 3 + 2 );
+
+				_color
+					.set( 0, 0, 0 )
+					.addScaledVector( _face.a, u )
+					.addScaledVector( _face.b, v )
+					.addScaledVector( _face.c, 1 - ( u + v ) );
+
+				targetColor.r = _color.x;
+				targetColor.g = _color.y;
+				targetColor.b = _color.z;
+
+			}
 
 
 			return this;
 			return this;
 
 

+ 54 - 48
examples/jsm/modifiers/CurveModifier.js

@@ -119,7 +119,6 @@ export function modifyShader( material, uniforms, numberOfCurves = 1 ) {
 		Object.assign( shader.uniforms, uniforms );
 		Object.assign( shader.uniforms, uniforms );
 
 
 		const vertexShader = `
 		const vertexShader = `
-		#define USE_ENVMAP
 		uniform sampler2D spineTexture;
 		uniform sampler2D spineTexture;
 		uniform float pathOffset;
 		uniform float pathOffset;
 		uniform float pathSegment;
 		uniform float pathSegment;
@@ -131,54 +130,61 @@ export function modifyShader( material, uniforms, numberOfCurves = 1 ) {
 		float textureStacks = ${TEXTURE_HEIGHT / 4}.;
 		float textureStacks = ${TEXTURE_HEIGHT / 4}.;
 
 
 		${shader.vertexShader}
 		${shader.vertexShader}
-		`.replace(
-		'#include <defaultnormal_vertex>',
 		`
 		`
-		vec4 worldPos = modelMatrix * vec4(position, 1.);
-
-		bool bend = flow > 0;
-		float xWeight = bend ? 0. : 1.;
-
-		#ifdef USE_INSTANCING
-		float pathOffsetFromInstanceMatrix = instanceMatrix[3][2];
-		float spineLengthFromInstanceMatrix = instanceMatrix[3][0];
-		float spinePortion = bend ? (worldPos.x + spineOffset) / spineLengthFromInstanceMatrix : 0.;
-		float mt = (spinePortion * pathSegment + pathOffset + pathOffsetFromInstanceMatrix)*textureStacks;
-		#else
-		float spinePortion = bend ? (worldPos.x + spineOffset) / spineLength : 0.;
-		float mt = (spinePortion * pathSegment + pathOffset)*textureStacks;
-		#endif
-
-		mt = mod(mt, textureStacks);
-		float rowOffset = floor(mt);
-
-		#ifdef USE_INSTANCING
-		rowOffset += instanceMatrix[3][1] * ${TEXTURE_HEIGHT}.;
-		#endif
-
-		vec3 spinePos = texture(spineTexture, vec2(mt, (0. + rowOffset + 0.5) / textureLayers)).xyz;
-		vec3 a =        texture(spineTexture, vec2(mt, (1. + rowOffset + 0.5) / textureLayers)).xyz;
-		vec3 b =        texture(spineTexture, vec2(mt, (2. + rowOffset + 0.5) / textureLayers)).xyz;
-		vec3 c =        texture(spineTexture, vec2(mt, (3. + rowOffset + 0.5) / textureLayers)).xyz;
-		mat3 basis = mat3(a, b, c);
-
-		vec3 transformed = basis
-			* vec3(worldPos.x * xWeight, worldPos.y * 1., worldPos.z * 1.)
-			+ spinePos;
-
-		vec3 transformedNormal = normalMatrix * (basis * objectNormal);
-		`
-	).replace(
-		'#include <begin_vertex>',
-		''
-	).replace(
-		'#include <project_vertex>',
-		`
-			vec4 mvPosition = viewMatrix * vec4( transformed, 1.0 );
-			// vec4 mvPosition = viewMatrix * worldPos;
-			gl_Position = projectionMatrix * mvPosition;
-			`
-	);
+		// chunk import moved in front of modified shader below
+			.replace( '#include <beginnormal_vertex>', `` )
+
+			// vec3 transformedNormal declaration overriden below
+			.replace( '#include <defaultnormal_vertex>', `` )
+
+			// vec3 transformed declaration overriden below
+			.replace( '#include <begin_vertex>', `` )
+
+			// shader override
+			.replace(
+				/void\s*main\s*\(\)\s*\{/,
+				`
+void main() {
+#include <beginnormal_vertex>
+
+vec4 worldPos = modelMatrix * vec4(position, 1.);
+
+bool bend = flow > 0;
+float xWeight = bend ? 0. : 1.;
+
+#ifdef USE_INSTANCING
+float pathOffsetFromInstanceMatrix = instanceMatrix[3][2];
+float spineLengthFromInstanceMatrix = instanceMatrix[3][0];
+float spinePortion = bend ? (worldPos.x + spineOffset) / spineLengthFromInstanceMatrix : 0.;
+float mt = (spinePortion * pathSegment + pathOffset + pathOffsetFromInstanceMatrix)*textureStacks;
+#else
+float spinePortion = bend ? (worldPos.x + spineOffset) / spineLength : 0.;
+float mt = (spinePortion * pathSegment + pathOffset)*textureStacks;
+#endif
+
+mt = mod(mt, textureStacks);
+float rowOffset = floor(mt);
+
+#ifdef USE_INSTANCING
+rowOffset += instanceMatrix[3][1] * ${TEXTURE_HEIGHT}.;
+#endif
+
+vec3 spinePos = texture(spineTexture, vec2(mt, (0. + rowOffset + 0.5) / textureLayers)).xyz;
+vec3 a =        texture(spineTexture, vec2(mt, (1. + rowOffset + 0.5) / textureLayers)).xyz;
+vec3 b =        texture(spineTexture, vec2(mt, (2. + rowOffset + 0.5) / textureLayers)).xyz;
+vec3 c =        texture(spineTexture, vec2(mt, (3. + rowOffset + 0.5) / textureLayers)).xyz;
+mat3 basis = mat3(a, b, c);
+
+vec3 transformed = basis
+	* vec3(worldPos.x * xWeight, worldPos.y * 1., worldPos.z * 1.)
+	+ spinePos;
+
+vec3 transformedNormal = normalMatrix * (basis * objectNormal);
+			` ).replace(
+				'#include <project_vertex>',
+				`vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );
+				gl_Position = projectionMatrix * mvPosition;`
+			);
 
 
 		shader.vertexShader = vertexShader;
 		shader.vertexShader = vertexShader;
 
 

+ 1 - 0
examples/jsm/postprocessing/EffectComposer.d.ts

@@ -23,6 +23,7 @@ export class EffectComposer {
 	swapBuffers(): void;
 	swapBuffers(): void;
 	addPass( pass: Pass ): void;
 	addPass( pass: Pass ): void;
 	insertPass( pass: Pass, index: number ): void;
 	insertPass( pass: Pass, index: number ): void;
+	removePass( pass: Pass ): void;
 	isLastEnabledPass( passIndex: number ): boolean;
 	isLastEnabledPass( passIndex: number ): boolean;
 	render( deltaTime?: number ): void;
 	render( deltaTime?: number ): void;
 	reset( renderTarget?: WebGLRenderTarget ): void;
 	reset( renderTarget?: WebGLRenderTarget ): void;

+ 12 - 0
examples/jsm/postprocessing/EffectComposer.js

@@ -96,6 +96,18 @@ Object.assign( EffectComposer.prototype, {
 
 
 	},
 	},
 
 
+	removePass: function ( pass ) {
+
+		const index = this.passes.indexOf( pass );
+
+		if ( index !== - 1 ) {
+
+			this.passes.splice( index, 1 );
+
+		}
+
+	},
+
 	isLastEnabledPass: function ( passIndex ) {
 	isLastEnabledPass: function ( passIndex ) {
 
 
 		for ( var i = passIndex + 1; i < this.passes.length; i ++ ) {
 		for ( var i = passIndex + 1; i < this.passes.length; i ++ ) {

+ 10 - 8
examples/jsm/postprocessing/SAOPass.d.ts

@@ -13,8 +13,16 @@ import {
 
 
 import { Pass } from './Pass';
 import { Pass } from './Pass';
 
 
+export enum OUTPUT {
+	Beauty,
+	Default,
+	SAO,
+	Depth,
+	Normal
+}
+
 interface SAOPassParams {
 interface SAOPassParams {
-	output: SAOPass.OUTPUT;
+	output: OUTPUT;
 	saoBias: number;
 	saoBias: number;
 	saoIntensity: number;
 	saoIntensity: number;
 	saoScale: number;
 	saoScale: number;
@@ -52,13 +60,7 @@ export class SAOPass extends Pass {
 	fsQuad: object;
 	fsQuad: object;
 	params: SAOPassParams;
 	params: SAOPassParams;
 
 
-	static OUTPUT: {
-		Beauty: number;
-		Default: number;
-		SAO: number;
-		Depth: number;
-		Normal: number;
-	};
+	static OUTPUT: OUTPUT;
 
 
 	renderPass( renderer: WebGLRenderer, passMaterial: Material, renderTarget: WebGLRenderTarget, clearColor?: Color | string | number, clearAlpha?: number ): void;
 	renderPass( renderer: WebGLRenderer, passMaterial: Material, renderTarget: WebGLRenderTarget, clearColor?: Color | string | number, clearAlpha?: number ): void;
 	renderOverride( renderer: WebGLRenderer, overrideMaterial: Material, renderTarget: WebGLRenderTarget, clearColor?: Color | string | number, clearAlpha?: number ): void;
 	renderOverride( renderer: WebGLRenderer, overrideMaterial: Material, renderTarget: WebGLRenderTarget, clearColor?: Color | string | number, clearAlpha?: number ): void;

+ 3 - 1
examples/jsm/postprocessing/SSAARenderPass.d.ts

@@ -2,7 +2,8 @@ import {
 	Scene,
 	Scene,
 	Camera,
 	Camera,
 	Color,
 	Color,
-	ShaderMaterial
+	ShaderMaterial,
+	WebGLRenderTarget
 } from '../../../src/Three';
 } from '../../../src/Three';
 
 
 import { Pass } from './Pass';
 import { Pass } from './Pass';
@@ -19,5 +20,6 @@ export class SSAARenderPass extends Pass {
 	copyUniforms: object;
 	copyUniforms: object;
 	copyMaterial: ShaderMaterial;
 	copyMaterial: ShaderMaterial;
 	fsQuad: object;
 	fsQuad: object;
+	sampleRenderTarget: undefined | WebGLRenderTarget;
 
 
 }
 }

+ 11 - 9
examples/jsm/postprocessing/SSAOPass.d.ts

@@ -13,6 +13,15 @@ import {
 
 
 import { Pass } from './Pass';
 import { Pass } from './Pass';
 
 
+export enum OUTPUT {
+	Default,
+	SSAO,
+	Blur,
+	Beauty,
+	Depth,
+	Normal
+}
+
 export class SSAOPass extends Pass {
 export class SSAOPass extends Pass {
 
 
 	constructor( scene: Scene, camera: Camera, width?: number, height?: number );
 	constructor( scene: Scene, camera: Camera, width?: number, height?: number );
@@ -25,7 +34,7 @@ export class SSAOPass extends Pass {
 	kernelSize: number;
 	kernelSize: number;
 	kernel: Vector3[];
 	kernel: Vector3[];
 	noiseTexture: DataTexture;
 	noiseTexture: DataTexture;
-	output: number;
+	output: OUTPUT;
 	minDistance: number;
 	minDistance: number;
 	maxDistance: number;
 	maxDistance: number;
 	beautyRenderTarget: WebGLRenderTarget;
 	beautyRenderTarget: WebGLRenderTarget;
@@ -40,14 +49,7 @@ export class SSAOPass extends Pass {
 	fsQuad: object;
 	fsQuad: object;
 	originalClearColor: Color;
 	originalClearColor: Color;
 
 
-	static OUTPUT: {
-		Default: number;
-		SSAO: number;
-		Blur: number;
-		Beauty: number;
-		Depth: number;
-		Normal: number;
-	};
+	static OUTPUT: OUTPUT;
 
 
 	dipose(): void;
 	dipose(): void;
 	generateSampleKernel(): Vector3[];
 	generateSampleKernel(): Vector3[];

+ 1 - 1
examples/misc_exporter_draco.html

@@ -163,7 +163,7 @@
 
 
 			function exportFile() {
 			function exportFile() {
 
 
-				const result = exporter.parse( mesh.geometry );
+				const result = exporter.parse( mesh );
 				saveArrayBuffer( result, 'file.drc' );
 				saveArrayBuffer( result, 'file.drc' );
 
 
 			}
 			}

+ 18 - 2
examples/misc_exporter_gltf.html

@@ -20,7 +20,6 @@
 			<label><input id="option_visible" name="visible" type="checkbox" checked="checked"/>Only Visible</label>
 			<label><input id="option_visible" name="visible" type="checkbox" checked="checked"/>Only Visible</label>
 			<label><input id="option_drawrange" name="visible" type="checkbox" checked="checked"/>Truncate drawRange</label><br/>
 			<label><input id="option_drawrange" name="visible" type="checkbox" checked="checked"/>Truncate drawRange</label><br/>
 			<label><input id="option_binary" name="visible" type="checkbox">Binary (<code>.glb</code>)</label>
 			<label><input id="option_binary" name="visible" type="checkbox">Binary (<code>.glb</code>)</label>
-			<label><input id="option_forcepot" name="visible" type="checkbox">Force POT textures</label>
 			<label><input id="option_maxsize" name="maxSize" type="number" value="4096" min="2" max="8192" step="1"> Max texture size</label>
 			<label><input id="option_maxsize" name="maxSize" type="number" value="4096" min="2" max="8192" step="1"> Max texture size</label>
 		</div>
 		</div>
 
 
@@ -40,7 +39,6 @@
 					onlyVisible: document.getElementById( 'option_visible' ).checked,
 					onlyVisible: document.getElementById( 'option_visible' ).checked,
 					truncateDrawRange: document.getElementById( 'option_drawrange' ).checked,
 					truncateDrawRange: document.getElementById( 'option_drawrange' ).checked,
 					binary: document.getElementById( 'option_binary' ).checked,
 					binary: document.getElementById( 'option_binary' ).checked,
-					forcePowerOfTwoTextures: document.getElementById( 'option_forcepot' ).checked,
 					maxTextureSize: Number( document.getElementById( 'option_maxsize' ).value ) || Infinity // To prevent NaN value
 					maxTextureSize: Number( document.getElementById( 'option_maxsize' ).value ) || Infinity // To prevent NaN value
 				};
 				};
 				gltfExporter.parse( input, function ( result ) {
 				gltfExporter.parse( input, function ( result ) {
@@ -138,6 +136,23 @@
 				container = document.createElement( 'div' );
 				container = document.createElement( 'div' );
 				document.body.appendChild( container );
 				document.body.appendChild( container );
 
 
+				// Make linear gradient texture
+				const data = new Uint8ClampedArray( 100 * 100 * 3 );
+				for ( let y = 0; y < 100; y ++ ) {
+
+					for ( let x = 0; x < 100; x ++ ) {
+
+						data[ 3 * ( 100 * y + x ) ] = Math.round( 255 * y / 99 );
+						data[ 3 * ( 100 * y + x ) + 1 ] = Math.round( 255 - 255 * y / 99 );
+
+					}
+
+				}
+
+				const gradientTexture = new THREE.DataTexture( data, 100, 100, THREE.RGBFormat );
+				gradientTexture.minFilter = THREE.LinearFilter;
+				gradientTexture.magFilter = THREE.LinearFilter;
+
 				scene1 = new THREE.Scene();
 				scene1 = new THREE.Scene();
 				scene1.name = 'Scene1';
 				scene1.name = 'Scene1';
 
 
@@ -230,6 +245,7 @@
 					roughness: 1.0,
 					roughness: 1.0,
 					flatShading: true
 					flatShading: true
 				} );
 				} );
+				material.map = gradientTexture;
 				sphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 70, 10, 10 ), material );
 				sphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 70, 10, 10 ), material );
 				sphere.position.set( 0, 0, 0 );
 				sphere.position.set( 0, 0, 0 );
 				sphere.name = "Sphere";
 				sphere.name = "Sphere";

+ 23 - 2
examples/misc_exporter_obj.html

@@ -29,7 +29,8 @@
 			<button id="cube">cube</button>
 			<button id="cube">cube</button>
 			<button id="cylinder">cylinder</button>
 			<button id="cylinder">cylinder</button>
 			<button id="multiple">multiple</button>
 			<button id="multiple">multiple</button>
-			<button id="transformed">transformed</button><br /><br />
+			<button id="transformed">transformed</button>
+			<button id="points">points</button><br /><br />
 			<button id="export">export to obj</button>
 			<button id="export">export to obj</button>
 		</div>
 		</div>
 
 
@@ -58,7 +59,7 @@
 
 
 					const child = scene.children[ i ];
 					const child = scene.children[ i ];
 
 
-					if ( child.isMesh ) {
+					if ( child.isMesh || child.isPoints ) {
 
 
 						child.geometry.dispose();
 						child.geometry.dispose();
 						scene.remove( child );
 						scene.remove( child );
@@ -114,6 +115,21 @@
 
 
 					}
 					}
 
 
+				} else if ( type === 6 ) {
+
+					const points = [ 0, 0, 0, 100, 0, 0, 100, 100, 0, 0, 100, 0 ];
+					const colors = [ 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0 ];
+
+					const geometry = new THREE.BufferGeometry();
+					geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( points, 3 ) );
+					geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
+
+					const material = new THREE.PointsMaterial( { size: 10, vertexColors: true } );
+
+					const pointCloud = new THREE.Points( geometry, material );
+					pointCloud.name = 'point cloud';
+					scene.add( pointCloud );
+
 				}
 				}
 
 
 			}
 			}
@@ -164,6 +180,11 @@
 
 
 					addGeometry( 5 );
 					addGeometry( 5 );
 
 
+				} );
+				document.getElementById( 'points' ).addEventListener( 'click', function () {
+
+					addGeometry( 6 );
+
 				} );
 				} );
 
 
 				exportButton = document.getElementById( 'export' );
 				exportButton = document.getElementById( 'export' );

+ 18 - 0
examples/models/gltf/MaterialsVariantsShoe/README.md

@@ -0,0 +1,18 @@
+# Materials Variants Shoe
+
+## Screenshot
+
+![screenshot](screenshot/screenshot.jpg)
+
+## Description
+
+This model uses the KHR_materials_variants extension. It is a shoe with 3 color variants in it: "Beach", "Midnight", and "Street".
+
+If each variant was a separate model, they would be 5.4 MB each. Combined they make up a single model that is 7.8MB since they share geometry and all textures except the base color texture.
+
+note: The textures in this repository have been resized to save space. 
+      See https://github.com/pushmatrix/glTF-Sample-Models/tree/master/2.0/MaterialsVariantsShoe for the original.
+
+## License Information
+Copyright 2020 Shopify, Inc. 
+CC BY 4.0 https://creativecommons.org/licenses/by/4.0/

BIN
examples/models/gltf/MaterialsVariantsShoe/glTF/MaterialsVariantsShoe.bin


+ 393 - 0
examples/models/gltf/MaterialsVariantsShoe/glTF/MaterialsVariantsShoe.gltf

@@ -0,0 +1,393 @@
+{
+  "accessors": [
+    {
+      "bufferView": 0,
+      "byteOffset": 0,
+      "count": 13540,
+      "componentType": 5126,
+      "type": "VEC3",
+      "min": [
+        -0.999970018863678,
+        -1,
+        -0.9999949932098388
+      ],
+      "max": [
+        0.999809980392456,
+        0.9999020099639891,
+        0.9999240040779114
+      ]
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 0,
+      "count": 13540,
+      "componentType": 5126,
+      "type": "VEC3",
+      "min": [
+        -1,
+        -0.5139704942703247,
+        -0.38874176144599915
+      ],
+      "max": [
+        1.0000001192092896,
+        0.5139704942703247,
+        0.38874176144599915
+      ]
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 0,
+      "count": 13540,
+      "componentType": 5126,
+      "type": "VEC2",
+      "min": [
+        0.001952999969944358,
+        0.03520399332046509
+      ],
+      "max": [
+        0.9980469942092896,
+        0.9980469942092896
+      ]
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 0,
+      "count": 68100,
+      "componentType": 5125,
+      "type": "SCALAR",
+      "min": [
+        0
+      ],
+      "max": [
+        13539
+      ]
+    }
+  ],
+  "asset": {
+    "generator": "THREE.GLTFExporter",
+    "version": "2.0"
+  },
+  "buffers": [
+    {
+      "name": "shoes-processed",
+      "byteLength": 705680,
+      "uri": "MaterialsVariantsShoe.bin"
+    }
+  ],
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 162480,
+      "byteOffset": 0,
+      "byteStride": 12,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteLength": 162480,
+      "byteOffset": 162480,
+      "byteStride": 12,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteLength": 108320,
+      "byteOffset": 324960,
+      "byteStride": 8,
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteLength": 272400,
+      "byteOffset": 433280,
+      "target": 34963
+    }
+  ],
+  "scene": 0,
+  "extensions": {
+    "KHR_materials_variants": {
+      "variants": [
+          {"name": "midnight"},
+          {"name": "beach" },
+          {"name": "street" }
+        ]
+    }
+  },
+  "extensionsUsed": [
+    "KHR_materials_variants"
+  ],
+  "images": [
+    {
+      "mimeType": "image/jpeg",
+      "uri": "occlusionRougnessMetalness.jpg"
+    },
+    {
+      "mimeType": "image/jpeg",
+      "uri": "diffuseMidnight.jpg"
+    },
+    {
+      "mimeType": "image/jpeg",
+      "uri": "normal.jpg"
+    },
+    {
+      "mimeType": "image/jpeg",
+      "uri": "diffuseBeach.jpg"
+    },
+    {
+      "mimeType": "image/jpeg",
+      "uri": "diffuseStreet.jpg"
+    }
+  ],
+  "materials": [
+    {
+      "alphaMode": "OPAQUE",
+      "doubleSided": false,
+      "name": "phong1SG",
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          1,
+          1,
+          1,
+          1
+        ],
+        "baseColorTexture": {
+          "index": 1,
+          "texCoord": 0
+        },
+        "metallicFactor": 1,
+        "roughnessFactor": 1,
+        "metallicRoughnessTexture": {
+          "index": 0,
+          "texCoord": 0
+        }
+      },
+      "normalTexture": {
+        "index": 2,
+        "scale": 1,
+        "texCoord": 0
+      },
+      "occlusionTexture": {
+        "index": 0,
+        "strength": 1,
+        "texCoord": 0
+      },
+      "emissiveFactor": [
+        0,
+        0,
+        0
+      ]
+    },
+    {
+      "alphaMode": "OPAQUE",
+      "doubleSided": false,
+      "name": "phong1SG",
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          1,
+          1,
+          1,
+          1
+        ],
+        "baseColorTexture": {
+          "index": 3,
+          "texCoord": 0
+        },
+        "metallicFactor": 1,
+        "roughnessFactor": 1,
+        "metallicRoughnessTexture": {
+          "index": 0,
+          "texCoord": 0
+        }
+      },
+      "normalTexture": {
+        "index": 2,
+        "scale": 1,
+        "texCoord": 0
+      },
+      "occlusionTexture": {
+        "index": 0,
+        "strength": 1,
+        "texCoord": 0
+      },
+      "emissiveFactor": [
+        0,
+        0,
+        0
+      ]
+    },
+    {
+      "alphaMode": "OPAQUE",
+      "doubleSided": false,
+      "name": "phong1SG",
+      "pbrMetallicRoughness": {
+        "baseColorFactor": [
+          1,
+          1,
+          1,
+          1
+        ],
+        "baseColorTexture": {
+          "index": 4,
+          "texCoord": 0
+        },
+        "metallicFactor": 1,
+        "roughnessFactor": 1,
+        "metallicRoughnessTexture": {
+          "index": 0,
+          "texCoord": 0
+        }
+      },
+      "normalTexture": {
+        "index": 2,
+        "scale": 1,
+        "texCoord": 0
+      },
+      "occlusionTexture": {
+        "index": 0,
+        "strength": 1,
+        "texCoord": 0
+      },
+      "emissiveFactor": [
+        0,
+        0,
+        0
+      ]
+    }
+  ],
+  "meshes": [
+    {
+      "name": "shoe",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 0,
+            "POSITION": 1,
+            "TEXCOORD_0": 2
+          },
+          "extensions": {
+            "KHR_materials_variants": {
+              "mappings": [
+                {
+                  "material": 0,
+                  "variants": [
+                    0
+                  ]
+                },
+                {
+                  "material": 1,
+                  "variants": [
+                    1
+                  ]
+                },
+                {
+                  "material": 2,
+                  "variants": [
+                    2
+                  ]
+                }
+              ]
+            }
+          },
+          "indices": 3,
+          "material": 0,
+          "mode": 4
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "extras": {
+        "name": "Shoe"
+      },
+      "mesh": 0,
+      "name": "Shoe"
+    },
+    {
+      "children": [
+        0
+      ],
+      "extras": {
+        "name": "g Shoe"
+      },
+      "name": "g_Shoe"
+    },
+    {
+      "children": [
+        1
+      ],
+      "extras": {
+        "name": "Shoe.obj"
+      },
+      "matrix": [
+        0.14893949,
+        0,
+        0,
+        0,
+        0,
+        0.14893949,
+        0,
+        0,
+        0,
+        0,
+        0.14893949,
+        0,
+        0.0016104877,
+        0.07590251,
+        0.0048509985,
+        1
+      ],
+      "name": "Shoeobj"
+    }
+  ],
+  "samplers": [
+    {
+      "magFilter": 9729,
+      "minFilter": 9985,
+      "wrapS": 10497,
+      "wrapT": 10497
+    },
+    {
+      "magFilter": 9729,
+      "minFilter": 9985,
+      "wrapS": 10497,
+      "wrapT": 10497
+    },
+    {
+      "magFilter": 9729,
+      "minFilter": 9985,
+      "wrapS": 10497,
+      "wrapT": 10497
+    }
+  ],
+  "scenes": [
+    {
+      "name": "Scene",
+      "nodes": [
+        2
+      ]
+    }
+  ],
+  "textures": [
+    {
+      "sampler": 0,
+      "source": 0
+    },
+    {
+      "sampler": 1,
+      "source": 1
+    },
+    {
+      "sampler": 2,
+      "source": 2
+    },
+    {
+      "sampler": 0,
+      "source": 3
+    },
+    {
+      "sampler": 0,
+      "source": 4
+    }
+  ]
+}

BIN
examples/models/gltf/MaterialsVariantsShoe/glTF/diffuseBeach.jpg


BIN
examples/models/gltf/MaterialsVariantsShoe/glTF/diffuseMidnight.jpg


BIN
examples/models/gltf/MaterialsVariantsShoe/glTF/diffuseStreet.jpg


BIN
examples/models/gltf/MaterialsVariantsShoe/glTF/normal.jpg


BIN
examples/models/gltf/MaterialsVariantsShoe/glTF/occlusionRougnessMetalness.jpg


BIN
examples/models/gltf/MaterialsVariantsShoe/screenshot/screenshot.jpg


BIN
examples/screenshots/webgl_framebuffer_texture.jpg


BIN
examples/screenshots/webgl_loader_gltf_variants.jpg


BIN
examples/screenshots/webgl_postprocessing_ssaa.jpg


BIN
examples/screenshots/webgl_postprocessing_ssaa_unbiased.jpg


+ 2 - 2
examples/tags.json

@@ -1,5 +1,5 @@
 {
 {
-	"webgl_animation_cloth": [ "physics", "integration" ],
+	"webgl_animation_cloth": [ "physics", "integration", "shadow" ],
 	"webgl_clipping": [ "solid" ],
 	"webgl_clipping": [ "solid" ],
 	"webgl_clipping_advanced": [ "solid" ],
 	"webgl_clipping_advanced": [ "solid" ],
 	"webgl_clipping_intersection": [ "solid" ],
 	"webgl_clipping_intersection": [ "solid" ],
@@ -37,7 +37,7 @@
 	"webgl_materials_channels": [ "normal", "depth", "rgba packing" ],
 	"webgl_materials_channels": [ "normal", "depth", "rgba packing" ],
 	"webgl_materials_cubemap_mipmaps": [ "envmap" ],
 	"webgl_materials_cubemap_mipmaps": [ "envmap" ],
 	"webgl_materials_envmaps_parallax": [ "onBeforeCompile" ],
 	"webgl_materials_envmaps_parallax": [ "onBeforeCompile" ],
-	"webgl_materials_lightmap": [ "shadows" ],
+	"webgl_materials_lightmap": [ "shadow" ],
 	"webgl_materials_physical_clearcoat": [ "anisotropy" ],
 	"webgl_materials_physical_clearcoat": [ "anisotropy" ],
 	"webgl_materials_physical_transmission": [ "alpha" ],
 	"webgl_materials_physical_transmission": [ "alpha" ],
 	"webgl_materials_shaders_fresnel": [ "refraction" ],
 	"webgl_materials_shaders_fresnel": [ "refraction" ],

+ 0 - 1
examples/webgl_decals.html

@@ -91,7 +91,6 @@
 
 
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
 				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 );
 				camera.position.z = 120;
 				camera.position.z = 120;
-				camera.target = new THREE.Vector3();
 
 
 				const controls = new OrbitControls( camera, renderer.domElement );
 				const controls = new OrbitControls( camera, renderer.domElement );
 				controls.minDistance = 50;
 				controls.minDistance = 50;

+ 133 - 14
examples/webgl_framebuffer_texture.html

@@ -42,14 +42,17 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 
 
 			let camera, scene, renderer;
 			let camera, scene, renderer;
-			let mesh, sprite, texture;
+			let line, sprite, texture;
 
 
 			let cameraOrtho, sceneOrtho;
 			let cameraOrtho, sceneOrtho;
 
 
+			let offset = 0;
+
 			const dpr = window.devicePixelRatio;
 			const dpr = window.devicePixelRatio;
 
 
 			const textureSize = 128 * dpr;
 			const textureSize = 128 * dpr;
 			const vector = new THREE.Vector2();
 			const vector = new THREE.Vector2();
+			const color = new THREE.Color();
 
 
 			init();
 			init();
 			animate();
 			animate();
@@ -68,24 +71,26 @@
 				cameraOrtho.position.z = 10;
 				cameraOrtho.position.z = 10;
 
 
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 0x20252f );
 				sceneOrtho = new THREE.Scene();
 				sceneOrtho = new THREE.Scene();
 
 
 				//
 				//
 
 
-				const geometry = new THREE.TorusKnotBufferGeometry( 3, 1, 256, 32 );
-				const material = new THREE.MeshStandardMaterial( { color: 0x6083c2 } );
+				const points = generatePoints();
 
 
-				mesh = new THREE.Mesh( geometry, material );
-				scene.add( mesh );
+				const geometry = new THREE.BufferGeometry();
+				const positionAttribute = new THREE.Float32BufferAttribute( points, 3 );
+				geometry.setAttribute( 'position', positionAttribute );
+				geometry.center();
 
 
-				//
-				const ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
-				scene.add( ambientLight );
+				const colorAttribute = new THREE.BufferAttribute( new Float32Array( positionAttribute.array.length ), 3 );
+				colorAttribute.setUsage( THREE.DynamicDrawUsage );
+				geometry.setAttribute( 'color', colorAttribute );
 
 
-				const pointLight = new THREE.PointLight( 0xffffff, 0.8 );
-				camera.add( pointLight );
-				scene.add( camera );
+				const material = new THREE.LineBasicMaterial( { vertexColors: true } );
+
+				line = new THREE.Line( geometry, material );
+				line.scale.setScalar( 0.05 );
+				scene.add( line );
 
 
 				//
 				//
 
 
@@ -160,8 +165,10 @@
 
 
 				requestAnimationFrame( animate );
 				requestAnimationFrame( animate );
 
 
-				mesh.rotation.x += 0.005;
-				mesh.rotation.y += 0.01;
+				const colorAttribute = line.geometry.getAttribute( 'color' );
+				updateColors( colorAttribute );
+
+				// scene rendering
 
 
 				renderer.clear();
 				renderer.clear();
 				renderer.render( scene, camera );
 				renderer.render( scene, camera );
@@ -178,6 +185,118 @@
 
 
 			}
 			}
 
 
+			function updateColors( colorAttribute ) {
+
+				const l = colorAttribute.count;
+
+				for ( let i = 0; i < l; i ++ ) {
+
+					const h = ( ( offset + i ) % l ) / l;
+
+					color.setHSL( h, 1, 0.5 );
+					colorAttribute.setX( i, color.r );
+					colorAttribute.setY( i, color.g );
+					colorAttribute.setZ( i, color.b );
+
+				}
+
+				colorAttribute.needsUpdate = true;
+
+				offset -= 25;
+
+			}
+
+			//
+
+			function generatePoints() {
+
+				// generate gosper curve (from https://gist.github.com/nitaku/6521802)
+
+				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,
+					side: 8,
+					angle: Math.PI / 3
+				} );
+
+				return points;
+
+			}
+
+			function fractalize( config ) {
+
+				let output;
+				let input = config.axiom;
+
+				for ( let i = 0, il = config.steps; 0 <= il ? i < il : i > il; 0 <= il ? i ++ : i -- ) {
+
+					output = '';
+
+					for ( let j = 0, jl = input.length; j < jl; j ++ ) {
+
+						const char = input[ j ];
+
+						if ( char in config.rules ) {
+
+							output += config.rules[ char ];
+
+						} else {
+
+							output += char;
+
+						}
+
+					}
+
+					input = output;
+
+				}
+
+				return output;
+
+			}
+
+			function toPoints( config ) {
+
+				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 ++ ) {
+
+					const char = fractal[ i ];
+
+					if ( char === '+' ) {
+
+						angle += config.angle;
+
+					} else if ( char === '-' ) {
+
+						angle -= config.angle;
+
+					} else if ( char === 'F' ) {
+
+						currX += config.side * Math.cos( angle );
+						currY += - config.side * Math.sin( angle );
+						path.push( currX, currY, 0 );
+
+					}
+
+				}
+
+				return path;
+
+			}
+
 		</script>
 		</script>
 
 
 	</body>
 	</body>

+ 1 - 1
examples/webgl_loader_collada_skinning.html

@@ -45,8 +45,8 @@
 				const loader = new ColladaLoader();
 				const loader = new ColladaLoader();
 				loader.load( './models/collada/stormtrooper/stormtrooper.dae', function ( collada ) {
 				loader.load( './models/collada/stormtrooper/stormtrooper.dae', function ( collada ) {
 
 
-					const animations = collada.animations;
 					const avatar = collada.scene;
 					const avatar = collada.scene;
+					const animations = avatar.animations;
 
 
 					avatar.traverse( function ( node ) {
 					avatar.traverse( function ( node ) {
 
 

+ 170 - 0
examples/webgl_loader_gltf_variants.html

@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - glTF loader</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader + <a href="https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants" target="_blank" rel="noopener">KHR_materials_variants</a> extension<br />
+			<a href="https://github.com/pushmatrix/glTF-Sample-Models/tree/master/2.0/MaterialsVariantsShoe" target="_blank" rel="noopener">Materials Variants Shoe</a> by
+			<a href="https://github.com/Shopify" target="_blank" rel="noopener">Shopify, Inc</a><br />
+			<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> by <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
+		</div>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import { GUI } from './jsm/libs/dat.gui.module.js';
+			import { OrbitControls } from './jsm/controls/OrbitControls.js';
+			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
+			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
+
+			let camera, scene, renderer;
+			let gui;
+
+			const state = { variant: 'midnight' };
+
+			init();
+			render();
+
+			function init() {
+
+				const container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
+				camera.position.set( 2.5, 1.5, 3.0 );
+
+				scene = new THREE.Scene();
+
+				new RGBELoader()
+					.setDataType( THREE.UnsignedByteType )
+					.setPath( 'textures/equirectangular/' )
+					.load( 'quarry_01_1k.hdr', function ( texture ) {
+
+						const envMap = pmremGenerator.fromEquirectangular( texture ).texture;
+
+						scene.background = envMap;
+						scene.environment = envMap;
+
+						texture.dispose();
+						pmremGenerator.dispose();
+
+						render();
+
+						// model
+
+						const loader = new GLTFLoader().setPath( 'models/gltf/MaterialsVariantsShoe/glTF/' );
+						loader.load( 'MaterialsVariantsShoe.gltf', function ( gltf ) {
+
+							gltf.scene.scale.set( 10.0, 10.0, 10.0 );
+
+							scene.add( gltf.scene );
+
+							// GUI
+							gui = new GUI();
+
+							// Details of the KHR_materials_variants extension used here can be found below
+							// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants
+							const parser = gltf.parser;
+							const variantsExtension = gltf.userData.gltfExtensions[ 'KHR_materials_variants' ];
+							const variants = variantsExtension.variants.map( ( variant ) => variant.name );
+							const variantsCtrl = gui.add( state, 'variant', variants ).name( 'Variant' );
+
+							selectVariant( scene, parser, variantsExtension, state.variant );
+
+							variantsCtrl.onChange( ( value ) => selectVariant( scene, parser, variantsExtension, value ) );
+
+							render();
+
+						} );
+
+					} );
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.toneMappingExposure = 1;
+				renderer.outputEncoding = THREE.sRGBEncoding;
+				container.appendChild( renderer.domElement );
+
+				const pmremGenerator = new THREE.PMREMGenerator( renderer );
+				pmremGenerator.compileEquirectangularShader();
+
+				const controls = new OrbitControls( camera, renderer.domElement );
+				controls.addEventListener( 'change', render ); // use if there is no animation loop
+				controls.minDistance = 2;
+				controls.maxDistance = 10;
+				controls.target.set( 0, 0.5, - 0.2 );
+				controls.update();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function selectVariant( scene, parser, extension, variantName ) {
+
+				const variantIndex = extension.variants.findIndex( ( v ) => v.name.includes( variantName ) );
+
+				scene.traverse( async ( object ) => {
+
+					if ( ! object.isMesh || ! object.userData.gltfExtensions ) return;
+
+					const meshVariantDef = object.userData.gltfExtensions[ 'KHR_materials_variants' ];
+
+					if ( ! meshVariantDef ) return;
+
+					if ( ! object.userData.originalMaterial ) {
+
+						object.userData.originalMaterial = object.material;
+
+					}
+
+					const mapping = meshVariantDef.mappings
+						.find( ( mapping ) => mapping.variants.includes( variantIndex ) );
+
+					if ( mapping ) {
+
+						object.material = await parser.getDependency( 'material', mapping.material );
+
+					} else {
+
+						object.material = object.originalMaterial;
+
+					}
+
+					render();
+
+				} );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				render();
+
+			}
+
+			//
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 0 - 1
examples/webgl_loader_nodes.html

@@ -51,7 +51,6 @@
 					camera.position.x = 50;
 					camera.position.x = 50;
 					camera.position.z = - 50;
 					camera.position.z = - 50;
 					camera.position.y = 30;
 					camera.position.y = 30;
-					camera.target = new THREE.Vector3();
 
 
 					cloud = new THREE.TextureLoader().load( 'textures/lava/cloud.png' );
 					cloud = new THREE.TextureLoader().load( 'textures/lava/cloud.png' );
 					cloud.wrapS = cloud.wrapT = THREE.RepeatWrapping;
 					cloud.wrapS = cloud.wrapT = THREE.RepeatWrapping;

+ 1 - 1
examples/webgl_loader_texture_rgbm.html

@@ -54,7 +54,7 @@
 
 
 					const material = new THREE.MeshBasicMaterial( { map: texture } );
 					const material = new THREE.MeshBasicMaterial( { map: texture } );
 
 
-					const quad = new THREE.PlaneBufferGeometry( 1.5 * texture.width / texture.height, 1.5 );
+					const quad = new THREE.PlaneBufferGeometry( 1, 1.5 );
 
 
 					const mesh = new THREE.Mesh( quad, material );
 					const mesh = new THREE.Mesh( quad, material );
 
 

+ 0 - 1
examples/webgl_materials_compile.html

@@ -94,7 +94,6 @@
 				camera.position.x = 0;
 				camera.position.x = 0;
 				camera.position.z = - 300;
 				camera.position.z = - 300;
 				camera.position.y = 200;
 				camera.position.y = 200;
-				camera.target = new THREE.Vector3();
 
 
 				controls = new OrbitControls( camera, renderer.domElement );
 				controls = new OrbitControls( camera, renderer.domElement );
 				controls.minDistance = 50;
 				controls.minDistance = 50;

+ 0 - 2
examples/webgl_materials_nodes.html

@@ -128,8 +128,6 @@
 				camera.position.x = 50;
 				camera.position.x = 50;
 				camera.position.z = - 50;
 				camera.position.z = - 50;
 				camera.position.y = 30;
 				camera.position.y = 30;
-				camera.target = new THREE.Vector3();
-
 
 
 				controls = new OrbitControls( camera, renderer.domElement );
 				controls = new OrbitControls( camera, renderer.domElement );
 				controls.minDistance = 50;
 				controls.minDistance = 50;

+ 1 - 1
examples/webgl_modifier_curve.html

@@ -58,7 +58,7 @@
 				];
 				];
 
 
 				const boxGeometry = new THREE.BoxBufferGeometry( 0.1, 0.1, 0.1 );
 				const boxGeometry = new THREE.BoxBufferGeometry( 0.1, 0.1, 0.1 );
-				const boxMaterial = new THREE.MeshBasicMaterial( 0x99ff99 );
+				const boxMaterial = new THREE.MeshBasicMaterial();
 
 
 				for ( const handlePos of initialPoints ) {
 				for ( const handlePos of initialPoints ) {
 
 

+ 1 - 1
examples/webgl_modifier_curve_instanced.html

@@ -51,7 +51,7 @@
 				camera.lookAt( scene.position );
 				camera.lookAt( scene.position );
 
 
 				const boxGeometry = new THREE.BoxBufferGeometry( 0.1, 0.1, 0.1 );
 				const boxGeometry = new THREE.BoxBufferGeometry( 0.1, 0.1, 0.1 );
-				const boxMaterial = new THREE.MeshBasicMaterial( 0x99ff99 );
+				const boxMaterial = new THREE.MeshBasicMaterial();
 
 
 				const curves = [[
 				const curves = [[
 					{ x: 1, y: - 0.5, z: - 1 },
 					{ x: 1, y: - 0.5, z: - 1 },

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