Mr.doob 3 years ago
parent
commit
460aea4555
100 changed files with 2090 additions and 2939 deletions
  1. 40 6
      build/three.js
  2. 0 0
      build/three.min.js
  3. 47 5
      build/three.module.js
  4. 5 1
      docs/api/en/extras/core/Curve.html
  5. 0 119
      docs/api/en/extras/objects/ImmediateRenderObject.html
  6. 10 0
      docs/api/en/loaders/LoaderUtils.html
  7. 15 4
      docs/api/en/materials/MeshPhysicalMaterial.html
  8. 0 3
      docs/api/en/objects/InstancedMesh.html
  9. 0 10
      docs/api/en/renderers/WebGLRenderer.html
  10. 5 0
      docs/api/en/textures/Texture.html
  11. 0 110
      docs/api/ko/extras/objects/ImmediateRenderObject.html
  12. 0 118
      docs/api/zh/extras/objects/ImmediateRenderObject.html
  13. 2 2
      docs/api/zh/geometries/BoxGeometry.html
  14. 24 4
      docs/api/zh/materials/MeshPhysicalMaterial.html
  15. 0 3
      docs/api/zh/objects/InstancedMesh.html
  16. 0 10
      docs/api/zh/renderers/WebGLRenderer.html
  17. 5 0
      docs/api/zh/textures/Texture.html
  18. 27 7
      docs/examples/en/controls/ArcballControls.html
  19. 0 95
      docs/examples/en/controls/DeviceOrientationControls.html
  20. 15 1
      docs/examples/en/renderers/CSS2DRenderer.html
  21. 15 1
      docs/examples/en/renderers/CSS3DRenderer.html
  22. 0 95
      docs/examples/ko/controls/DeviceOrientationControls.html
  23. 0 95
      docs/examples/zh/controls/DeviceOrientationControls.html
  24. 1 1
      docs/index.html
  25. 0 12
      docs/list.json
  26. 2 2
      editor/js/Sidebar.Material.js
  27. 3 3
      editor/js/Strings.js
  28. 0 12
      editor/js/libs/tern-threejs/threejs.js
  29. 1 1
      editor/sw.js
  30. 4 3
      examples/files.json
  31. 54 15
      examples/js/controls/ArcballControls.js
  32. 0 147
      examples/js/controls/DeviceOrientationControls.js
  33. 1 1
      examples/js/controls/OrbitControls.js
  34. 3 2
      examples/js/controls/TrackballControls.js
  35. 1 1
      examples/js/exporters/GLTFExporter.js
  36. 25 14
      examples/js/exporters/USDZExporter.js
  37. 52 19
      examples/js/lines/LineMaterial.js
  38. 1 1
      examples/js/loaders/3MFLoader.js
  39. 61 3
      examples/js/loaders/ColladaLoader.js
  40. 1 1
      examples/js/loaders/FBXLoader.js
  41. 82 29
      examples/js/loaders/GLTFLoader.js
  42. 12 0
      examples/js/loaders/KTX2Loader.js
  43. 2 1
      examples/js/loaders/RGBELoader.js
  44. 237 271
      examples/js/loaders/TDSLoader.js
  45. 42 128
      examples/js/objects/MarchingCubes.js
  46. 4 4
      examples/js/renderers/CSS2DRenderer.js
  47. 4 4
      examples/js/renderers/CSS3DRenderer.js
  48. 1 0
      examples/js/utils/RoughnessMipmapper.js
  49. 50 17
      examples/jsm/controls/ArcballControls.js
  50. 0 153
      examples/jsm/controls/DeviceOrientationControls.js
  51. 1 1
      examples/jsm/controls/OrbitControls.js
  52. 1 1
      examples/jsm/controls/TrackballControls.js
  53. 1 1
      examples/jsm/exporters/GLTFExporter.js
  54. 27 14
      examples/jsm/exporters/USDZExporter.js
  55. 0 78
      examples/jsm/libs/glslang.js
  56. BIN
      examples/jsm/libs/glslang.wasm
  57. 52 19
      examples/jsm/lines/LineMaterial.js
  58. 1 1
      examples/jsm/loaders/3MFLoader.js
  59. 61 3
      examples/jsm/loaders/ColladaLoader.js
  60. 1 1
      examples/jsm/loaders/FBXLoader.js
  61. 86 33
      examples/jsm/loaders/GLTFLoader.js
  62. 19 0
      examples/jsm/loaders/KTX2Loader.js
  63. 225 285
      examples/jsm/loaders/TDSLoader.js
  64. 1 1
      examples/jsm/nodes/materials/StandardNodeMaterial.js
  65. 6 6
      examples/jsm/nodes/materials/nodes/StandardNode.js
  66. 46 152
      examples/jsm/objects/MarchingCubes.js
  67. 5 4
      examples/jsm/renderers/CSS2DRenderer.js
  68. 5 4
      examples/jsm/renderers/CSS3DRenderer.js
  69. 11 13
      examples/jsm/renderers/nodes/Nodes.js
  70. 162 45
      examples/jsm/renderers/nodes/ShaderNode.js
  71. 6 6
      examples/jsm/renderers/nodes/accessors/MaterialNode.js
  72. 5 5
      examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js
  73. 15 3
      examples/jsm/renderers/nodes/accessors/NormalNode.js
  74. 3 3
      examples/jsm/renderers/nodes/accessors/PointUVNode.js
  75. 16 5
      examples/jsm/renderers/nodes/accessors/PositionNode.js
  76. 107 0
      examples/jsm/renderers/nodes/accessors/SkinningNode.js
  77. 7 5
      examples/jsm/renderers/nodes/accessors/UVNode.js
  78. 0 7
      examples/jsm/renderers/nodes/consts/MathConsts.js
  79. 3 3
      examples/jsm/renderers/nodes/core/ArrayInputNode.js
  80. 6 0
      examples/jsm/renderers/nodes/core/AttributeNode.js
  81. 38 0
      examples/jsm/renderers/nodes/core/BypassNode.js
  82. 3 3
      examples/jsm/renderers/nodes/core/CodeNode.js
  83. 0 39
      examples/jsm/renderers/nodes/core/ConstNode.js
  84. 4 19
      examples/jsm/renderers/nodes/core/ContextNode.js
  85. 13 2
      examples/jsm/renderers/nodes/core/ExpressionNode.js
  86. 19 132
      examples/jsm/renderers/nodes/core/FunctionNode.js
  87. 13 3
      examples/jsm/renderers/nodes/core/InputNode.js
  88. 32 10
      examples/jsm/renderers/nodes/core/Node.js
  89. 143 124
      examples/jsm/renderers/nodes/core/NodeBuilder.js
  90. 22 0
      examples/jsm/renderers/nodes/core/NodeFunction.js
  91. 4 4
      examples/jsm/renderers/nodes/core/NodeFunctionInput.js
  92. 18 179
      examples/jsm/renderers/nodes/core/NodeKeywords.js
  93. 11 0
      examples/jsm/renderers/nodes/core/NodeParser.js
  94. 0 13
      examples/jsm/renderers/nodes/core/NodeSlot.js
  95. 2 2
      examples/jsm/renderers/nodes/core/NodeVar.js
  96. 14 2
      examples/jsm/renderers/nodes/core/PropertyNode.js
  97. 0 80
      examples/jsm/renderers/nodes/core/StructNode.js
  98. 0 75
      examples/jsm/renderers/nodes/core/StructVarNode.js
  99. 9 12
      examples/jsm/renderers/nodes/core/TempNode.js
  100. 12 7
      examples/jsm/renderers/nodes/core/VarNode.js

File diff suppressed because it is too large
+ 40 - 6
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
+ 47 - 5
build/three.module.js


+ 5 - 1
docs/api/en/extras/core/Curve.html

@@ -69,7 +69,11 @@
 		<p>Get list of cumulative segment lengths.</p>
 
 		<h3>[method:null updateArcLengths]()</h3>
-		<p>Update the cumlative segment distance cache.</p>
+		<p>
+			Update the cumlative segment distance cache. The method must be called every time curve parameters are changed.
+			If an updated curve is part of a composed curve like [page:CurvePath], [page:.updateArcLengths]() must be 
+			called on the composed curve, too.
+		</p>
 
 		<h3>[method:Float getUtoTmapping]( [param:Float u], [param:Float distance] )</h3>
 		<p>

+ 0 - 119
docs/api/en/extras/objects/ImmediateRenderObject.html

@@ -1,119 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../../../" />
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:Object3D] &rarr;
-
-		<h1>[name]</h1>
-
-		<p class="desc">
-			This experimental class provides a fast code path for rendering meshes with frequently updated
-			geometry data. When the renderer encounters an instance of [name], it only takes care about
-			the most primitive rendering operations (e.g. binding vertex attributes, determining correct shader
-			program or perfoming the actual draw call). Features like view frustum culling, wireframe rendering
-			or using multiple materials are not supported. Besides [name] can only be used to render triangles.
-		</p>
-
-		<p class="desc">
-			[name] does not work with instances of [page:BufferGeometry]. The
-			raw geometry data have to be maintained as properties of the [name].
-		</p>
-
-		<p class="desc">
-			Using [name] makes only sense if you are updating your geometry data per frame. You can then
-			benefit of a faster code path compared to the default mesh redering logic.
-		</p>
-
-		<h2>Examples</h2>
-		<p>
-			[example:webgl_marchingcubes Marching Cubes]
-		</p>
-
-		<h2>Constructor</h2>
-
-
-		<h3>[name]( [param:Material material] )</h3>
-		<p>
-		[page:Material material] — The material of the [name].
-		</p>
-
-		<h2>Properties</h2>
-		<p>See the base [page:Object3D] class for common properties.</p>
-
-		<h3>[property:Boolean material]</h3>
-		<p>
-			The material of the [name]. Assigning multiple materials is not supported.
-		</p>
-
-		<h3>[property:Boolean hasPositions]</h3>
-		<p>
-			Whether position data are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Boolean hasNormals]</h3>
-		<p>
-			Whether normal data are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Boolean hasColors]</h3>
-		<p>
-			Whether color data are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Boolean hasUvs]</h3>
-		<p>
-			Whether texture coordinates are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Float32Array positionArray]</h3>
-		<p>
-			The buffer holding position data. Default is *null*.
-		</p>
-
-		<h3>[property:Float32Array normalArray]</h3>
-		<p>
-			The buffer holding normal data. Default is *null*.
-		</p>
-
-		<h3>[property:Float32Array colorArray]</h3>
-		<p>
-			The buffer holding color data. Default is *null*.
-		</p>
-
-		<h3>[property:Float32Array uvArray]</h3>
-		<p>
-			The buffer holding texture coordinates. Default is *null*.
-		</p>
-
-		<h3>[property:Integer count]</h3>
-		<p>
-			The number of primitives to be rendered. Default is *0*.
-			This property will be set to *0* after each rendering so you usually
-			set it in the implementatio of [page:.render]().
-		</p>
-
-		<h2>Methods</h2>
-
-		<p>See the base [page:Object3D] class for common methods.</p>
-
-		<h3>[method:null render]([param:Function renderCallback])</h3>
-		<p>
-		renderCallback -- A function to render the generated geometry data.
-		</p>
-		<p>
-		This method needs to be implemented by the deriving class of [name]. You normally want to implement the
-		vertex buffer update logic and execute *renderCallback* at the end of your implementation.
-		</p>
-
-		<h2>Source</h2>
-
-		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-		</p>
-	</body>
-</html>

+ 10 - 0
docs/api/en/loaders/LoaderUtils.html

@@ -30,6 +30,16 @@
 		</p>
 
 
+		<h3>[method:String resolveURL]( [param:String url], [param:String path]  )</h3>
+		<p>
+		[page:String url] —  The absolute or relative url resolve.
+		[page:String path] —  The base path for relative urls to be resolved against.
+		</p>
+		<p>
+		Resolves relative urls against the given path. Absolute paths, data urls, and blob urls will be returned as is. Invalid urls will return an empty string.
+		</p>
+
+
 		<h2>Source</h2>
 
 		<p>

+ 15 - 4
docs/api/en/materials/MeshPhysicalMaterial.html

@@ -61,7 +61,6 @@
 			[example:webgl_materials_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<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]
 		</p>
 
@@ -135,19 +134,31 @@
 
 		<h3>[property:Float sheen]</h3>
 		<p>
-			The intensity of the sheen layer, from *0.0* to *1.0*. Default is *0.0*.</p>
+			The intensity of the sheen layer, from *0.0* to *1.0*. Default is *0.0*.
 		</p>
 
 		<h3>[property:Float sheenRoughness]</h3>
 		<p>
-			Roughness of the sheen layer, from *0.0* to *1.0*. Default is *1.0*.</p>
+			Roughness of the sheen layer, from *0.0* to *1.0*. Default is *1.0*.
 		</p>
 
-		<h3>[property:Color sheenTint]</h3>
+		<h3>[property:Texture sheenRoughnessMap]</h3>
+		<p>
+			The alpha channel of this texture is multiplied against [page:.sheenRoughness], for per-pixel control
+			over sheen roughness. Default is *null*.
+		</p>
+
+		<h3>[property:Color sheenColor]</h3>
 		<p>
 			The sheen tint. Default is *0xffffff*, white.
 		</p>
 
+		<h3>[property:Texture sheenColorMap]</h3>
+		<p>
+			The RGB channels of this texture are multiplied against [page:.sheenColor], for per-pixel control
+			over sheen tint. Default is *null*.
+		</p>
+
 		<h3>[property:Float transmission]</h3>
 		<p>
 		Degree of transmission (or optical transparency), from *0.0* to *1.0*. Default is *0.0*.<br />

+ 0 - 3
docs/api/en/objects/InstancedMesh.html

@@ -16,9 +16,6 @@
 		objects with the same geometry and material but with different world transformations. The usage of [name] will help you
 		to reduce the number of draw calls and thus improve the overall rendering performance in your application.
 		</p>
-		<p>
-		The current implementation requires that materials are not shared between [name] and other 3D objects.
-		</p>
 
 		<h2>Examples</h2>
 

+ 0 - 10
docs/api/en/renderers/WebGLRenderer.html

@@ -414,16 +414,6 @@
 			[page:WebGLRenderer.autoClearDepth autoClearDepth] properties to false. To forcibly clear one ore more buffers call [page:WebGLRenderer.clear .clear].
 		</p>
 
-		<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Scene scene], [param:BufferGeometry geometry], [param:Material material], [param:Object3D object], [param:Object group] )</h3>
-		<p>Render a buffer geometry group using the camera and with the specified material.</p>
-
-		<h3>[method:null renderBufferImmediate]( [param:Object3D object], [param:WebGLProgram program] )</h3>
-		<p>object - an instance of [page:Object3D]<br />
-		program - an instance of [page:WebGLProgram]<br />
-
-		Renders an instance of [page:ImmediateRenderObject]. Gets called by renderObjectImmediate().
-		</p>
-
 		<h3>[method:null resetState]()</h3>
 		<p>Can be used to reset the internal WebGL state. This method is mostly relevant for applications which share a single WebGL context across multiple WebGL libraries.</p>
 

+ 5 - 0
docs/api/en/textures/Texture.html

@@ -257,6 +257,11 @@
 		Set this to *true* to trigger an update next time the texture is used. Particularly important for setting the wrap mode.
 		</p>
 
+		<h3>[property:Object userData]</h3>
+		<p>
+		An object that can be used to store custom data about the texture. It should not hold
+		references to functions as these will not be cloned.
+		</p>
 
 		<h2>Methods</h2>
 

+ 0 - 110
docs/api/ko/extras/objects/ImmediateRenderObject.html

@@ -1,110 +0,0 @@
-<!DOCTYPE html>
-<html lang="ko">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../../../" />
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:Object3D] &rarr;
-
-		<h1>[name]</h1>
-
-		<p class="desc">
-			이 실험 클래스는 자주 업데이트되는 기하학 데이터로 메쉬를 렌더링하기 위한 빠른 코드 패스를 제공합니다. 렌더러가 [name]의 인스턴스를 만나면, 가장 원시적인 렌더링 작업(예: 바인딩 꼭짓점 속성, 정확한 셰이더 프로그램 결정 또는 실제 드로우 콜 수행)에만 신경을 씁니다. 뷰 펑퍼텀 도금, 와이어프레임 렌더링 또는 다중 재료 사용과 같은 기능은 지원되지 않습니다. [name]은 삼각형을 렌더링하는 데에만 사용할 수 있습니다.
-		</p>
-
-		<p class="desc">
-			[name]는 [page:BufferGeometry] 인스턴스와 함께 작동되지 않습니다. 원시 기하학 데이터가 [name]의 프로퍼티로 저장돼야 합니다.
-		</p>
-
-		<p class="desc">
-			[name]은 기하학 데이터를 프레임마다 업데이트할 때만 작동합니다. 기본 메쉬 렌더링 로직에 비해 빠른 코드 패스를 경험할 수 있습니다.
-		</p>
-
-		<h2>예제</h2>
-		<p>
-			[example:webgl_marchingcubes Marching Cubes]
-		</p>
-
-		<h2>생성자</h2>
-
-
-		<h3>[name]( [param:Material material] )</h3>
-		<p>
-		[page:Material material] — [name]의 재질.
-		</p>
-
-		<h2>프로퍼티</h2>
-		<p>일반 프로퍼니틑 기본 [page:Object3D] 클래스를 참고하세요.</p>
-
-		<h3>[property:Boolean material]</h3>
-		<p>
-			[name]의 재질. 여러 재질을 할당하는 것은 지원되지 않습니다.
-		</p>
-
-		<h3>[property:Boolean hasPositions]</h3>
-		<p>
-			위치 데이터가 정의되었는지입니다. 기본값은 *false*입니다.
-		</p>
-
-		<h3>[property:Boolean hasNormals]</h3>
-		<p>
-			법선 데이터가 정의되었는지입니다. 기본값은 *false*입니다.
-		</p>
-
-		<h3>[property:Boolean hasColors]</h3>
-		<p>
-			색상 데이터가 정의되었는지입니다. 기본값은 *false*입니다.
-		</p>
-
-		<h3>[property:Boolean hasUvs]</h3>
-		<p>
-			텍스쳐 좌표가 정의되었는지입니다. 기본값은 *false*입니다.
-		</p>
-
-		<h3>[property:Float32Array positionArray]</h3>
-		<p>
-			위치 데이터를 담고 있는 버퍼입니다. 기본값은 *null*입니다.
-		</p>
-
-		<h3>[property:Float32Array normalArray]</h3>
-		<p>
-			법선 데이터를 담고 있는 버퍼입니다. 기본값은 *null*입니다.
-		</p>
-
-		<h3>[property:Float32Array colorArray]</h3>
-		<p>
-			색상 데이터를 담고 있는 버퍼입니다. 기본값은 *null*입니다.
-		</p>
-
-		<h3>[property:Float32Array uvArray]</h3>
-		<p>
-			텍스쳐 좌표를 담고 있는 버퍼입니다. 기본값은 *null*입니다.
-		</p>
-
-		<h3>[property:Integer count]</h3>
-		<p>
-			렌더링될 개체의 수입니다. 기본값은 *0*입니다.
-			매 렌더링 이후에 이 프로퍼티는 *0*으로 설정될 것이기 때문에 [page:.render]() 실행 범위 안에 주로 설정하곤 합니다.
-		</p>
-
-		<h2>메서드</h2>
-
-		<p>일반 메서드는 기본 [page:Object3D] 클래스를 참고하세요.</p>
-
-		<h3>[method:null render]([param:Function renderCallback])</h3>
-		<p>
-		renderCallback -- 생성된 기하학 데이터를 렌더링하는 함수입니다.
-		</p>
-		<p>
-		이 메서드는 [name]의 파생클래스에서 상속받아야합니다. 일반적으로 실행 마지막 부분에 꼭짓점 버퍼 업데이트 로직을 구현하고 *renderCallback*을 사용합니다.
-
-		<h2>소스코드</h2>
-
-		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-		</p>
-	</body>
-</html>

+ 0 - 118
docs/api/zh/extras/objects/ImmediateRenderObject.html

@@ -1,118 +0,0 @@
-<!DOCTYPE html>
-<html lang="zh">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../../../" />
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-		[page:Object3D] &rarr;
-
-		<h1>即时渲染对象([name])</h1>
-
-		<p class="desc">
-			This experimental class provides a fast code path for rendering meshes with frequently updated
-			geometry data. When the renderer encounters an instance of [name], it only takes care about
-			the most primitive rendering operations (e.g. binding vertex attributes, determining correct shader
-			program or perfoming the actual draw call). Features like view frustum culling, wireframe rendering
-			or using multiple materials are not supported. Besides [name] can only be used to render triangles.
-		</p>
-
-		<p class="desc">
-			[name] does not work with instances of [page:BufferGeometry]. The
-			raw geometry data have to be maintained as properties of the [name].
-		</p>
-
-		<p class="desc">
-			Using [name] makes only sense if you are updating your geometry data per frame. You can then
-			benefit of a faster code path compared to the default mesh redering logic.
-		</p>
-
-		<h2>例子</h2>
-		<p>
-			[example:webgl_marchingcubes Marching Cubes]
-		</p>
-
-		<h2>构造函数(Constructor)</h2>
-
-		<h3>[name]( [param:Material material] )</h3>
-		<p>
-		[page:Material material] — The material of the [name].
-		</p>
-
-		<h2>Properties</h2>
-		<p>See the base [page:Object3D] class for common properties.</p>
-
-		<h3>[property:Boolean material]</h3>
-		<p>
-			The material of the [name]. Assigning multiple materials is not supported.
-		</p>
-
-		<h3>[property:Boolean hasPositions]</h3>
-		<p>
-			Whether position data are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Boolean hasNormals]</h3>
-		<p>
-			Whether normal data are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Boolean hasColors]</h3>
-		<p>
-			Whether color data are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Boolean hasUvs]</h3>
-		<p>
-			Whether texture coordinates are defined or not. Default is *false*.
-		</p>
-
-		<h3>[property:Float32Array positionArray]</h3>
-		<p>
-			The buffer holding position data. Default is *null*.
-		</p>
-
-		<h3>[property:Float32Array normalArray]</h3>
-		<p>
-			The buffer holding normal data. Default is *null*.
-		</p>
-
-		<h3>[property:Float32Array colorArray]</h3>
-		<p>
-			The buffer holding color data. Default is *null*.
-		</p>
-
-		<h3>[property:Float32Array uvArray]</h3>
-		<p>
-			The buffer holding texture coordinates. Default is *null*.
-		</p>
-
-		<h3>[property:Integer count]</h3>
-		<p>
-			The number of primitives to be rendered. Default is *0*.
-			This property will be set to *0* after each rendering so you usually
-			set it in the implementatio of [page:.render]().
-		</p>
-
-		<h2>方法(Methods)</h2>
-
-		<p>See the base [page:Object3D] class for common methods.</p>
-
-		<h3>[method:null render]([param:Function renderCallback])</h3>
-		<p>
-		renderCallback -- A function to render the generated geometry data.
-		</p>
-		<p>
-		This method needs to be implemented by the deriving class of [name]. You normally want to implement the
-		vertex buffer update logic and execute *renderCallback* at the end of your implementation.
-		</p>
-
-		<h2>源码(Source)</h2>
-
-		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]
-		</p>
-	</body>
-</html>

+ 2 - 2
docs/api/zh/geometries/BoxGeometry.html

@@ -47,8 +47,8 @@
 		height — Y轴上面的高度,默认值为1。<br />
 		depth — Z轴上面的深度,默认值为1。<br />
 		widthSegments — (可选)宽度的分段数,默认值是1。<br />
-		heightSegments — (可选)度的分段数,默认值是1。<br />
-		depthSegments — (可选)度的分段数,默认值是1。
+		heightSegments — (可选)度的分段数,默认值是1。<br />
+		depthSegments — (可选)度的分段数,默认值是1。
 		</p>
 
 		<h2>属性</h2>

+ 24 - 4
docs/api/zh/materials/MeshPhysicalMaterial.html

@@ -62,7 +62,6 @@
 			[example:webgl_materials_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<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]
 		</p>
 
@@ -130,10 +129,31 @@
 			这模拟了非金属材质的反射率。当[page:MeshStandardMaterial]为*1.0*时,此属性无效。
 		</p>
 
-		<h3>[property:Color sheen]</h3>
+		<h3>[property:Float 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.
+			The intensity of the sheen layer, from *0.0* to *1.0*. Default is *0.0*.
+		</p>
+
+		<h3>[property:Float sheenRoughness]</h3>
+		<p>
+			Roughness of the sheen layer, from *0.0* to *1.0*. Default is *1.0*.
+		</p>
+
+		<h3>[property:Texture sheenRoughnessMap]</h3>
+		<p>
+			The alpha channel of this texture is multiplied against [page:.sheenRoughness], for per-pixel control
+			over sheen roughness. Default is *null*.
+		</p>
+
+		<h3>[property:Color sheenColor]</h3>
+		<p>
+			The sheen tint. Default is *0xffffff*, white.
+		</p>
+
+		<h3>[property:Texture sheenColorMap]</h3>
+		<p>
+			The RGB channels of this texture are multiplied against [page:.sheenColor], for per-pixel control
+			over sheen tint. Default is *null*.
 		</p>
 
 		<h3>[property:Float transmission]</h3>

+ 0 - 3
docs/api/zh/objects/InstancedMesh.html

@@ -15,9 +15,6 @@
 		一种具有实例化渲染支持的特殊版本的[page:Mesh]。你可以使用 [name] 来渲染大量具有相同几何体与材质、但具有不同世界变换的物体。
 		使用 [name] 将帮助你减少 draw call 的数量,从而提升你应用程序的整体渲染性能。
 		</p>
-		<p>
-		当前的实现需要[name]和其它3D物体间不共享材质。
-		</p>
 
 		<h2>示例</h2>
 

+ 0 - 10
docs/api/zh/renderers/WebGLRenderer.html

@@ -368,16 +368,6 @@
 			即便forceClear设为true, 也可以通过将[page:WebGLRenderer.autoClearColor autoClearColor]、[page:WebGLRenderer.autoClearStencil autoClearStencil]或[page:WebGLRenderer.autoClearDepth autoClearDepth]属性的值设为false来阻止对应缓存被清除。
 		</p>
 
-		<h3>[method:null renderBufferDirect]( [param:Camera camera], [param:Scene scene], [param:BufferGeometry geometry], [param:Material material], [param:Object3D object], [param:Object group] )</h3>
-		<p>使用相机和指定材质渲染缓冲几何组。</p>
-
-		<h3>[method:null renderBufferImmediate]( [param:Object3D object], [param:WebGLProgram program] )</h3>
-		<p>object - an instance of [page:Object3D]<br />
-		program - an instance of [page:WebGLProgram]<br />
-
-		Renders an instance of [page:ImmediateRenderObject],由renderObjectImmediate对象调用。
-		</p>
-
 		<h3>[method:null resetState]()</h3>
 		<p>Can be used to reset the internal WebGL state. This method is mostly relevant for applications which share a single WebGL context across multiple WebGL libraries.</p>
 

+ 5 - 0
docs/api/zh/textures/Texture.html

@@ -256,6 +256,11 @@
 			这对于设置包裹模式尤其重要。
 		</p>
 
+		<h3>[property:Object userData]</h3>
+		<p>
+		An object that can be used to store custom data about the texture. It should not hold
+		references to functions as these will not be cloned.
+		</p>
 
 		<h2>方法</h2>
 

+ 27 - 7
docs/examples/en/controls/ArcballControls.html

@@ -7,22 +7,24 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
+		[page:EventDispatcher] &rarr;
+
 		<h1>[name]</h1>
 
 		<p class="desc">
 		Arcball controls allow the camera to be controlled by a virtual trackball with full touch support and advanced navigation functionality. <br>
 		Cursor/finger positions and movements are mapped over a virtual trackball surface
-		represented by a gizmo and mapped in intuitive and consistent camera movements. 
+		represented by a gizmo and mapped in intuitive and consistent camera movements.
 		Dragging cursor/fingers will cause camera to orbit around the center of the trackball in a conservative way (returning to the starting point
 		will make the camera to return to its starting orientation).<br><br>
-		
+
 		In addition to supporting pan, zoom and pinch gestures, Arcball controls provide <i>focus</i> functionality with a double click/tap for
-		intuitively moving the object's point of interest in the center of the virtual trackball.   
-		Focus allows a much better inspection and navigation in complex environment. 
+		intuitively moving the object's point of interest in the center of the virtual trackball.
+		Focus allows a much better inspection and navigation in complex environment.
 		Moreover Arcball controls allow FOV manipulation (in a vertigo-style method) and z-rotation.
-		Saving and restoring of Camera State is supported also through clipboard 
+		Saving and restoring of Camera State is supported also through clipboard
 		(use ctrl+c and ctrl+v shortcuts for copy and paste the state).<br><br>
-		
+
 		Unlike [page:OrbitControls] and [page:TrackballControls], [name] doesn't require [page:.update] to be called externally in an animation loop when animations
 		are on.<br><br>
 
@@ -189,6 +191,11 @@
 			Maximum angular velocity allowed on rotation animation start.
 		</p>
 
+		<h3>[property:Float radiusFactor]</h3>
+		<p>
+			The size of the gizmo relative to the screen width and height. Default is 0.67.
+		</p>
+
 
 		<h2>Methods</h2>
 
@@ -232,6 +239,11 @@
 			Set the visible property of gizmos.
 		</p>
 
+		<h3>[method:null setTbRadius] ( [param:Float value] )</h3>
+		<p>
+			Update the `radiusFactor` value, redraw the gizmo and send a `changeEvent` to visualise the changes.
+		</p>
+
 		<h3>[method:Boolean setMouseAction] ( [param:String operation], mouse, key )</h3>
 		<p>
 			Set a new mouse action by specifying the operation to be performed and a mouse/key combination. In case of conflict, replaces the existing one.<br><br>
@@ -251,12 +263,20 @@
 			Mouse inputs can be specified as mouse buttons 0, 1 and 2 or 'WHEEL' for wheel notches.<br>
 			Keyboard modifiers can be specified as 'CTRL', 'SHIFT' or null if not needed.
 		</p>
-		
+
 		<h3>[method:null update] ()</h3>
 		<p>
 			Update the controls. Must be called after any manual changes to the camera's transform.
 		</p>
 
+		<h3>[method:Raycaster getRaycaster] ()</h3>
+		<p>
+			Returns the [page:Raycaster] object that is used for user interaction. This object is shared between all instances of
+			ArcballControls. If you set the [page:Object3D.layers .layers] property of the [name], you will also want to
+			set the [page:Raycaster.layers .layers] property on the [page:Raycaster] with a matching value, or else the [name]
+			won't work as expected.
+		</p>
+
 		<h2>Source</h2>
 
 		<p>

+ 0 - 95
docs/examples/en/controls/DeviceOrientationControls.html

@@ -1,95 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../../" />
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-
-		<h1>[name]</h1>
-
-		<p class="desc">
-			Can be used to orient the camera based on the mobile device's orientation.
-		</p>
-
-		<h2>Examples</h2>
-
-		<p>[example:misc_controls_deviceorientation misc / controls / deviceorientation ]</p>
-
-		<h2>Constructor</h2>
-
-		<h3>[name]( [param:Camera object] )</h3>
-		<p>
-			<p>
-				[page:Camera object]: The camera to be controlled.
-			</p>
-			<p>
-				Creates a new instance of [name].
-			</p>
-		</p>
-
-		<h2>Events</h2>
-
-		<h3>change</h3>
-		<p>
-			Fires when the camera has been transformed by the controls.
-		</p>
-
-		<h2>Properties</h2>
-
-		<h3>[property:Number alphaOffset]</h3>
-		<p>
-			The alpha offset in radians. Default is *0*.
-		</p>
-
-		<h3>[property:Object deviceOrientation]</h3>
-		<p>
-			The current *deviceorientation* event object.
-		</p>
-
-		<h3>[property:Boolean enabled]</h3>
-		<p>
-			Whether or not the controls are enabled.
-		</p>
-
-		<h3>[property:Camera object]</h3>
-		<p>
-			The camera to be controlled.
-		</p>
-
-		<h3>[property:Number screenOrientation]</h3>
-		<p>
-			The orientation in degrees (in 90-degree increments) of the viewport relative to the device's natural orientation. Default is *0*.
-		</p>
-
-		<h2>Methods</h2>
-
-		<h3>[method:null connect] ()</h3>
-		<p>
-			Adds the event listeners of the controls and enables it.
-		</p>
-
-		<h3>[method:null disconnect] ()</h3>
-		<p>
-			Removes the event listeners of the controls and disables it.
-		</p>
-
-		<h3>[method:null dispose] ()</h3>
-		<p>
-			Should be called if the controls is no longer required.
-		</p>
-
-		<h3>[method:null update] ()</h3>
-		<p>
-			Updates the controls. Usually called in the animation loop.
-		</p>
-
-		<h2>Source</h2>
-
-		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/DeviceOrientationControls.js examples/jsm/controls/DeviceOrientationControls.js]
-		</p>
-	</body>
-</html>

+ 15 - 1
docs/examples/en/renderers/CSS2DRenderer.html

@@ -22,7 +22,21 @@
 
 		<h2>Constructor</h2>
 
-		<h3>[name]()</h3>
+		<h3>[name]( [param:Object parameters] )</h3>
+		<p>
+		[page:DOMElement element] - A [link:https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement HTMLElement]
+		where the renderer appends its child-elements.
+		This corresponds to the [page:CSS2DRenderer.domElement domElement] property below.
+		If not passed in here, a new div element will be created.
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:DOMElement domElement]</h3>
+		<p>
+			A [link:https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement HTMLElement] where the renderer appends its child-elements.<br />
+			This is automatically created by the renderer in the constructor (if not provided already).
+		</p>
 
 		<h2>Methods</h2>
 

+ 15 - 1
docs/examples/en/renderers/CSS3DRenderer.html

@@ -33,7 +33,21 @@
 
 		<h2>Constructor</h2>
 
-		<h3>[name]()</h3>
+		<h3>[name]( [param:Object parameters] )</h3>
+		<p>
+			[page:DOMElement element] - A [link:https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement HTMLElement]
+			where the renderer appends its child-elements.
+			This corresponds to the [page:CSS3DRenderer.domElement domElement] property below.
+			If not passed in here, a new div element will be created.
+		</p>
+
+		<h2>Properties</h2>
+
+		<h3>[property:DOMElement domElement]</h3>
+		<p>
+			A [link:https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement HTMLElement] where the renderer appends its child-elements.<br />
+			This is automatically created by the renderer in the constructor (if not provided already).
+		</p>
 
 		<h2>Methods</h2>
 

+ 0 - 95
docs/examples/ko/controls/DeviceOrientationControls.html

@@ -1,95 +0,0 @@
-<!DOCTYPE html>
-<html lang="ko">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../../" />
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-
-		<h1>[name]</h1>
-
-		<p class="desc">
-			모바일 장치의 방향에 따라 카메라 방향을 조정하는 데 사용 됩니다.
-		</p>
-
-		<h2>예시</h2>
-
-		<p>[example:misc_controls_deviceorientation misc / controls / deviceorientation ]</p>
-
-		<h2>생성자</h2>
-
-		<h3>[name]( [param:Camera object] )</h3>
-		<p>
-			<p>
-				[page:Camera object]: 제어 할 카메라 객체 입니다.
-			</p>
-			<p>
-				 새로운 [name] 객체를 생성합니다.
-			</p>
-		</p>
-
-		<h2>이벤트</h2>
-
-		<h3>change</h3>
-		<p>
-			컨트롤에 의해 카메라가 변환되면 실행합니다.
-		</p>
-
-		<h2>속성</h2>
-
-		<h3>[property:Number alphaOffset]</h3>
-		<p>
-			알파 값의 오프셋의 단위는 라디안 입니다. 초기값은 0으로 지정됩니다.
-		</p>
-
-		<h3>[property:Object deviceOrientation]</h3>
-		<p>
-			현재 deviceorientation 이벤트 객체입니다.
-		</p>
-
-		<h3>[property:Boolean enabled]</h3>
-		<p>
-			컨트롤의 활성화 여부를 지정합니다.
-		</p>
-
-		<h3>[property:Camera object]</h3>
-		<p>
-			제어 할 카메라입니다.
-		</p>
-
-		<h3>[property:Number screenOrientation]</h3>
-		<p>
-			장치의 실제 방향을 기준으로 뷰포트의 방향을 지정합니다(90도 단위 기준). 초기값은 *0* 입니다.
-		</p>
-
-		<h2>메소드</h2>
-
-		<h3>[method:null connect] ()</h3>
-		<p>
-			컨트롤의 이벤트 리스너에 추가한 다음 활성화 합니다.
-		</p>
-
-		<h3>[method:null disconnect] ()</h3>
-		<p>
-			컨트롤의 이벤트 리스너에 제거한 다음 비활성화 합니다.
-		</p>
-
-		<h3>[method:null dispose] ()</h3>
-		<p>
-			컨트롤을 더이상 필요하지 않을 경우 호출해야 합니다.
-		</p>
-
-		<h3>[method:null update] ()</h3>
-		<p>
-			컨트롤을 업데이트 합니다. 보통 애니메이션 루프에서 호출 됩니다.
-		</p>
-
-		<h2>Source</h2>
-
-		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/DeviceOrientationControls.js examples/jsm/controls/DeviceOrientationControls.js]
-		</p>
-	</body>
-</html>

+ 0 - 95
docs/examples/zh/controls/DeviceOrientationControls.html

@@ -1,95 +0,0 @@
-<!DOCTYPE html>
-<html lang="zh">
-	<head>
-		<meta charset="utf-8" />
-		<base href="../../../" />
-		<script src="page.js"></script>
-		<link type="text/css" rel="stylesheet" href="page.css" />
-	</head>
-	<body>
-
-		<h1>设备朝向控制器([name])</h1>
-
-		<p class="desc">
-			通过使用设备朝向来确定摄像机的朝向。
-		</p>
-
-		<h2>例子</h2>
-
-		<p>[example:misc_controls_deviceorientation misc / controls / deviceorientation ]</p>
-
-		<h2>构造函数</h2>
-
-		<h3>[name]( [param:Camera object] )</h3>
-		<p>
-			<p>
-				[page:Camera object]: 被控制的摄像机。
-			</p>
-			<p>
-				创建一个新的 [name] 实例。
-			</p>
-		</p>
-
-		<h2>Events</h2>
-
-		<h3>change</h3>
-		<p>
-			Fires when the camera has been transformed by the controls.
-		</p>
-
-		<h2>属性</h2>
-
-		<h3>[property:Number alphaOffset]</h3>
-		<p>
-			alpha角偏移量,以弧度表示,默认为*0*。
-		</p>
-
-		<h3>[property:Object deviceOrientation]</h3>
-		<p>
-			当前 *deviceorientation* 事件的对象。
-		</p>
-
-		<h3>[property:Boolean enabled]</h3>
-		<p>
-			是否启用控制器。
-		</p>
-
-		<h3>[property:Camera object]</h3>
-		<p>
-			被控制的摄像机。
-		</p>
-
-		<h3>[property:Number screenOrientation]</h3>
-		<p>
-			相对于设备自然朝向的视口朝向,以角度表示(增量为90)。默认为*0*。
-		</p>
-
-		<h2>方法</h2>
-
-		<h3>[method:null connect] ()</h3>
-		<p>
-			增加控制器的事件监听,并启用控制器。
-		</p>
-
-		<h3>[method:null disconnect] ()</h3>
-		<p>
-			移除控制器的事件监听,并禁用控制器。
-		</p>
-
-		<h3>[method:null dispose] ()</h3>
-		<p>
-			若不再需要该控制器,则应当调用此函数。
-		</p>
-
-		<h3>[method:null update] ()</h3>
-		<p>
-			更新控制器,常被用在动画循环中。
-		</p>
-
-		<h2>源代码</h2>
-
-		<p>
-			[link:https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/DeviceOrientationControls.js examples/jsm/controls/DeviceOrientationControls.js]
-		</p>
-	</body>
-</html>

+ 1 - 1
docs/index.html

@@ -86,7 +86,7 @@
 
 			// *BufferGeometry to *Geometry
 
-			if ( /Instanced/.exec( window.location.hash ) !== null && /([\w]+)BufferGeometry$/.exec( window.location.hash ) ) {
+			if ( /Instanced/.exec( window.location.hash ) === null && /([\w]+)BufferGeometry$/.exec( window.location.hash ) ) {
 
 				window.location.hash = window.location.hash.replace( 'BufferGeometry', 'Geometry' );
 

+ 0 - 12
docs/list.json

@@ -132,10 +132,6 @@
 				"SplineCurve": "api/en/extras/curves/SplineCurve"
 			},
 
-			"Extras / Objects": {
-				"ImmediateRenderObject": "api/en/extras/objects/ImmediateRenderObject"
-			},
-
 			"Geometries": {
 				"BoxGeometry": "api/en/geometries/BoxGeometry",
 				"CircleGeometry": "api/en/geometries/CircleGeometry",
@@ -335,7 +331,6 @@
 
 			"Controls": {
 				"ArcballControls": "examples/en/controls/ArcballControls",
-				"DeviceOrientationControls": "examples/en/controls/DeviceOrientationControls",
 				"DragControls": "examples/en/controls/DragControls",
 				"FirstPersonControls": "examples/en/controls/FirstPersonControls",
 				"FlyControls": "examples/en/controls/FlyControls",
@@ -641,10 +636,6 @@
 				"SplineCurve": "api/zh/extras/curves/SplineCurve"
 			},
 
-			"附件 / 物体": {
-				"ImmediateRenderObject": "api/zh/extras/objects/ImmediateRenderObject"
-			},
-
 			"几何体": {
 				"BoxGeometry": "api/zh/geometries/BoxGeometry",
 				"CircleGeometry": "api/zh/geometries/CircleGeometry",
@@ -843,7 +834,6 @@
 			},
 
 			"控制": {
-				"DeviceOrientationControls": "examples/zh/controls/DeviceOrientationControls",
 				"DragControls": "examples/zh/controls/DragControls",
 				"FirstPersonControls": "examples/zh/controls/FirstPersonControls",
 				"FlyControls": "examples/zh/controls/FlyControls",
@@ -980,7 +970,6 @@
 		"레퍼런스": {
 
 			"애니메이션": {
-				"AnimationAction": "api/ko/animation/AnimationAction",
 				"AnimationAction": "api/ko/animation/AnimationAction",
 				"AnimationClip": "api/ko/animation/AnimationClip",
 				"AnimationMixer": "api/ko/animation/AnimationMixer",
@@ -1030,7 +1019,6 @@
 		"예제": {
 
 			"컨트롤": {
-				"DeviceOrientationControls": "examples/ko/controls/DeviceOrientationControls",
 				"DragControls": "examples/ko/controls/DragControls",
 				"FirstPersonControls": "examples/ko/controls/FirstPersonControls",
 				"FlyControls": "examples/ko/controls/FlyControls",

+ 2 - 2
editor/js/Sidebar.Material.js

@@ -142,8 +142,8 @@ function SidebarMaterial( editor ) {
 
 	// attenuation tint
 
-	const materialAttenuationTint = new SidebarMaterialColorProperty( editor, 'attenuationTint', strings.getKey( 'sidebar/material/attenuationTint' ) );
-	container.add( materialAttenuationTint );
+	const materialAttenuationColor = new SidebarMaterialColorProperty( editor, 'attenuationColor', strings.getKey( 'sidebar/material/attenuationColor' ) );
+	container.add( materialAttenuationColor );
 
 	// thickness
 

+ 3 - 3
editor/js/Strings.js

@@ -251,7 +251,7 @@ function Strings( config ) {
 			'sidebar/material/clearcoatroughness': 'Clearcoat Roughness',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/attenuationDistance': 'Attenuation Distance',
-			'sidebar/material/attenuationTint': 'Attenuation Tint',
+			'sidebar/material/attenuationColor': 'Attenuation Color',
 			'sidebar/material/thickness': 'Thickness',
 			'sidebar/material/vertexcolors': 'Vertex Colors',
 			'sidebar/material/matcap': 'Matcap',
@@ -579,7 +579,7 @@ function Strings( config ) {
 			'sidebar/material/clearcoatroughness': 'Rugosité du vernis',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/attenuationDistance': 'Attenuation Distance',
-			'sidebar/material/attenuationTint': 'Attenuation Tint',
+			'sidebar/material/attenuationColor': 'Attenuation Color',
 			'sidebar/material/thickness': 'Thickness',
 			'sidebar/material/vertexcolors': 'Couleurs aux Sommets',
 			'sidebar/material/matcap': 'Matcap',
@@ -907,7 +907,7 @@ function Strings( config ) {
 			'sidebar/material/clearcoatroughness': '清漆粗糙度',
 			'sidebar/material/transmission': '透光',
 			'sidebar/material/attenuationDistance': '衰减距离',
-			'sidebar/material/attenuationTint': '衰减色彩',
+			'sidebar/material/attenuationColor': 'Attenuation Color',
 			'sidebar/material/thickness': '厚度',
 			'sidebar/material/vertexcolors': '顶点颜色',
 			'sidebar/material/matcap': '材质捕获',

+ 0 - 12
editor/js/libs/tern-threejs/threejs.js

@@ -1499,18 +1499,6 @@
       "!doc": "Creates a wireframe object that shows the edges of another object's geometry. To draw a  wireframe image showing only \"hard\" edges (edges between non-coplanar faces), see [page:EdgesHelper].",
       "!type": "fn(object: +THREE.Object3D, color: +THREE.Color)"
     },
-    "ImmediateRenderObject": {
-      "!url": "http://threejs.org/docs/#Reference/extras/objects/ImmediateRenderObject",
-      "prototype": {
-        "!proto": "THREE.Object3D.prototype",
-        "render": {
-          "!type": "fn(renderCallback: function)",
-          "!doc": "This function needs to be overridden to start the creation of the object and should call renderCallback when finished."
-        }
-      },
-      "!doc": "base class for immediate rendering objects.",
-      "!type": "fn()"
-    },
     "AmbientLight": {
       "!url": "http://threejs.org/docs/#Reference/lights/AmbientLight",
       "prototype": {

+ 1 - 1
editor/sw.js

@@ -1,4 +1,4 @@
-// r133
+// r134
 
 const cacheName = 'threejs-editor';
 

+ 4 - 3
examples/files.json

@@ -84,6 +84,7 @@
 		"webgl_loader_gcode",
 		"webgl_loader_gltf",
 		"webgl_loader_gltf_compressed",
+		"webgl_loader_gltf_sheen",
 		"webgl_loader_gltf_transmission",
 		"webgl_loader_gltf_variants",
 		"webgl_loader_ifc",
@@ -146,7 +147,6 @@
 		"webgl_materials_normalmap_object_space",
 		"webgl_materials_physical_clearcoat",
 		"webgl_materials_physical_reflectivity",
-		"webgl_materials_physical_sheen",
 		"webgl_materials_physical_transmission",
 		"webgl_materials_standard",
 		"webgl_materials_subsurface_scattering",
@@ -322,7 +322,9 @@
 		"webgpu_lights_selective",
 		"webgpu_materials",
 		"webgpu_rtt",
-		"webgpu_sandbox"
+		"webgpu_sandbox",
+		"webgpu_skinning",
+		"webgpu_skinning_points"
 	],
 	"webaudio": [
 		"webaudio_orientation",
@@ -371,7 +373,6 @@
 		"misc_animation_keys",
 		"misc_boxselection",
 		"misc_controls_arcball",
-		"misc_controls_deviceorientation",
 		"misc_controls_drag",
 		"misc_controls_fly",
 		"misc_controls_map",

+ 54 - 15
examples/js/controls/ArcballControls.js

@@ -40,6 +40,8 @@
 	const _endEvent = {
 		type: 'end'
 	};
+
+	const _raycaster = new THREE.Raycaster();
 	/**
  *
  * @param {Camera} camera Virtual camera used in the scene
@@ -47,7 +49,8 @@
  * @param {Scene} scene The scene to be rendered
  */
 
-	class ArcballControls extends THREE.Object3D {
+
+	class ArcballControls extends THREE.EventDispatcher {
 
 		constructor( _camera, domElement, scene = null ) {
 
@@ -1591,7 +1594,6 @@
 
 			this.calculateTbRadius = camera => {
 
-				const factor = 0.67;
 				const distance = camera.position.distanceTo( this._gizmos.position );
 
 				if ( camera.type == 'PerspectiveCamera' ) {
@@ -1600,11 +1602,11 @@
 
 					const halfFovH = Math.atan( camera.aspect * Math.tan( halfFovV ) ); //horizontal fov/2 in radians
 
-					return Math.tan( Math.min( halfFovV, halfFovH ) ) * distance * factor;
+					return Math.tan( Math.min( halfFovV, halfFovH ) ) * distance * this.radiusFactor;
 
 				} else if ( camera.type == 'OrthographicCamera' ) {
 
-					return Math.min( camera.top, camera.right ) * factor;
+					return Math.min( camera.top, camera.right ) * this.radiusFactor;
 
 				}
 
@@ -1707,8 +1709,8 @@
 				window.removeEventListener( 'pointermove', this.onPointerMove );
 				window.removeEventListener( 'pointerup', this.onPointerUp );
 				window.removeEventListener( 'resize', this.onWindowResize );
-				window.addEventListener( 'keydown', this.onKeyDown );
-				this.scene.remove( this._gizmos );
+				window.removeEventListener( 'keydown', this.onKeyDown );
+				if ( this.scene !== null ) this.scene.remove( this._gizmos );
 				this.disposeGrid();
 
 			};
@@ -1788,7 +1790,7 @@
 
 			this.setCamera = camera => {
 
-				camera.lookAt( this._tbCenter );
+				camera.lookAt( this.target );
 				camera.updateMatrix(); //setting state
 
 				if ( camera.type == 'PerspectiveCamera' ) {
@@ -1807,10 +1809,10 @@
 				this._zoom0 = camera.zoom;
 				this._zoomState = this._zoom0;
 				this._initialNear = camera.near;
-				this._nearPos0 = camera.position.distanceTo( this._tbCenter ) - camera.near;
+				this._nearPos0 = camera.position.distanceTo( this.target ) - camera.near;
 				this._nearPos = this._initialNear;
 				this._initialFar = camera.far;
-				this._farPos0 = camera.position.distanceTo( this._tbCenter ) - camera.far;
+				this._farPos0 = camera.position.distanceTo( this.target ) - camera.far;
 				this._farPos = this._initialFar;
 
 				this._up0.copy( camera.up );
@@ -1821,7 +1823,7 @@
 				this.camera.updateProjectionMatrix(); //making gizmos
 
 				this._tbRadius = this.calculateTbRadius( camera );
-				this.makeGizmos( this._tbCenter, this._tbRadius );
+				this.makeGizmos( this.target, this._tbRadius );
 
 			};
 
@@ -2283,14 +2285,14 @@
 
 			this.setTarget = ( x, y, z ) => {
 
-				this._tbCenter.set( x, y, z );
+				this.target.set( x, y, z );
 
 				this._gizmos.position.set( x, y, z ); //for correct radius calculation
 
 
 				this._tbRadius = this.calculateTbRadius( this.camera );
-				this.makeGizmos( this._tbCenter, this._tbRadius );
-				this.camera.lookAt( this._tbCenter );
+				this.makeGizmos( this.target, this._tbRadius );
+				this.camera.lookAt( this.target );
 
 			};
 
@@ -2323,7 +2325,7 @@
 
 			this.unprojectOnObj = ( cursor, camera ) => {
 
-				const raycaster = new THREE.Raycaster();
+				const raycaster = this.getRaycaster();
 				raycaster.near = camera.near;
 				raycaster.far = camera.far;
 				raycaster.setFromCamera( cursor, camera );
@@ -2680,6 +2682,8 @@
 			this.camera = null;
 			this.domElement = domElement;
 			this.scene = scene;
+			this.target = new THREE.Vector3( 0, 0, 0 );
+			this.radiusFactor = 0.67;
 			this.mouseActions = [];
 			this._mouseOp = null; //global vectors and matrices that are used in some operations to avoid creating new objects every time (e.g. every time cursor moves)
 
@@ -2803,7 +2807,6 @@
 			this.minZoom = 0;
 			this.maxZoom = Infinity; //trackball parameters
 
-			this._tbCenter = new THREE.Vector3( 0, 0, 0 );
 			this._tbRadius = 1; //FSA
 
 			this._state = STATE.IDLE;
@@ -2928,6 +2931,29 @@
 			this._gizmos.visible = value;
 			this.dispatchEvent( _changeEvent );
 
+		}
+		/**
+   * Set gizmos radius factor and redraws gizmos
+   * @param {Float} value Value of radius factor
+   */
+
+
+		setTbRadius( value ) {
+
+			this.radiusFactor = value;
+			this._tbRadius = this.calculateTbRadius( this.camera );
+			const curve = new THREE.EllipseCurve( 0, 0, this._tbRadius, this._tbRadius );
+			const points = curve.getPoints( this._curvePts );
+			const curveGeometry = new THREE.BufferGeometry().setFromPoints( points );
+
+			for ( const gizmo in this._gizmos.children ) {
+
+				this._gizmos.children[ gizmo ].geometry = curveGeometry;
+
+			}
+
+			this.dispatchEvent( _changeEvent );
+
 		}
 		/**
    * Creates the rotation gizmos matching trackball center and radius
@@ -2988,6 +3014,19 @@
    */
 
 
+		getRaycaster() {
+
+			return _raycaster;
+
+		}
+		/**
+   * Unproject the cursor on the 3D object surface
+   * @param {Vector2} cursor Cursor coordinates in NDC
+   * @param {Camera} camera Virtual camera
+   * @returns {Vector3} The point of intersection with the model, if exist, null otherwise
+   */
+
+
 	}
 
 	THREE.ArcballControls = ArcballControls;

+ 0 - 147
examples/js/controls/DeviceOrientationControls.js

@@ -1,147 +0,0 @@
-( function () {
-
-	const _zee = new THREE.Vector3( 0, 0, 1 );
-
-	const _euler = new THREE.Euler();
-
-	const _q0 = new THREE.Quaternion();
-
-	const _q1 = new THREE.Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
-
-
-	const _changeEvent = {
-		type: 'change'
-	};
-
-	class DeviceOrientationControls extends THREE.EventDispatcher {
-
-		constructor( object ) {
-
-			super();
-
-			if ( window.isSecureContext === false ) {
-
-				console.error( 'THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)' );
-
-			}
-
-			const scope = this;
-			const EPS = 0.000001;
-			const lastQuaternion = new THREE.Quaternion();
-			this.object = object;
-			this.object.rotation.reorder( 'YXZ' );
-			this.enabled = true;
-			this.deviceOrientation = {};
-			this.screenOrientation = 0;
-			this.alphaOffset = 0; // radians
-
-			const onDeviceOrientationChangeEvent = function ( event ) {
-
-				scope.deviceOrientation = event;
-
-			};
-
-			const onScreenOrientationChangeEvent = function () {
-
-				scope.screenOrientation = window.orientation || 0;
-
-			}; // The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
-
-
-			const setObjectQuaternion = function ( quaternion, alpha, beta, gamma, orient ) {
-
-				_euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
-
-
-				quaternion.setFromEuler( _euler ); // orient the device
-
-				quaternion.multiply( _q1 ); // camera looks out the back of the device, not the top
-
-				quaternion.multiply( _q0.setFromAxisAngle( _zee, - orient ) ); // adjust for screen orientation
-
-			};
-
-			this.connect = function () {
-
-				onScreenOrientationChangeEvent(); // run once on load
-				// iOS 13+
-
-				if ( window.DeviceOrientationEvent !== undefined && typeof window.DeviceOrientationEvent.requestPermission === 'function' ) {
-
-					window.DeviceOrientationEvent.requestPermission().then( function ( response ) {
-
-						if ( response == 'granted' ) {
-
-							window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-							window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-
-						}
-
-					} ).catch( function ( error ) {
-
-						console.error( 'THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:', error );
-
-					} );
-
-				} else {
-
-					window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-					window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-
-				}
-
-				scope.enabled = true;
-
-			};
-
-			this.disconnect = function () {
-
-				window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-				window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-				scope.enabled = false;
-
-			};
-
-			this.update = function () {
-
-				if ( scope.enabled === false ) return;
-				const device = scope.deviceOrientation;
-
-				if ( device ) {
-
-					const alpha = device.alpha ? THREE.MathUtils.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
-
-					const beta = device.beta ? THREE.MathUtils.degToRad( device.beta ) : 0; // X'
-
-					const gamma = device.gamma ? THREE.MathUtils.degToRad( device.gamma ) : 0; // Y''
-
-					const orient = scope.screenOrientation ? THREE.MathUtils.degToRad( scope.screenOrientation ) : 0; // O
-
-					setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
-
-					if ( 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
-
-						lastQuaternion.copy( scope.object.quaternion );
-						scope.dispatchEvent( _changeEvent );
-
-					}
-
-				}
-
-			};
-
-			this.dispose = function () {
-
-				scope.disconnect();
-
-			};
-
-			this.connect();
-
-		}
-
-	}
-
-	THREE.DeviceOrientationControls = DeviceOrientationControls;
-
-} )();

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

@@ -892,7 +892,7 @@
 
 			function onMouseWheel( event ) {
 
-				if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE && state !== STATE.ROTATE ) return;
+				if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
 				event.preventDefault();
 				scope.dispatchEvent( _startEvent );
 				handleMouseWheel( event );

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

@@ -117,7 +117,8 @@
 				const vector = new THREE.Vector2();
 				return function getMouseOnCircle( pageX, pageY ) {
 
-					vector.set( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ), ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width );
+					vector.set( ( pageX - scope.screen.width * 0.5 - scope.screen.left ) / ( scope.screen.width * 0.5 ), ( scope.screen.height + 2 * ( scope.screen.top - pageY ) ) / scope.screen.width // screen.width intentional
+					);
 					return vector;
 
 				};
@@ -194,7 +195,7 @@
 
 					} else if ( scope.object.isOrthographicCamera ) {
 
-						scope.object.zoom *= factor;
+						scope.object.zoom /= factor;
 						scope.object.updateProjectionMatrix();
 
 					} else {

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

@@ -2143,7 +2143,7 @@
 			}
 
 			extensionDef.attenuationDistance = material.attenuationDistance;
-			extensionDef.attenuationColor = material.attenuationTint.toArray();
+			extensionDef.attenuationColor = material.attenuationColor.toArray();
 			materialDef.extensions = materialDef.extensions || {};
 			materialDef.extensions[ this.name ] = extensionDef;
 			extensionsUsed[ this.name ] = true;

+ 25 - 14
examples/js/exporters/USDZExporter.js

@@ -228,25 +228,33 @@ def "Geometry"
 
 	function buildMeshVertexCount( geometry ) {
 
-		const count = geometry.index !== null ? geometry.index.array.length : geometry.attributes.position.count;
+		const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
 		return Array( count / 3 ).fill( 3 ).join( ', ' );
 
 	}
 
 	function buildMeshVertexIndices( geometry ) {
 
-		if ( geometry.index !== null ) {
+		const index = geometry.index;
+		const array = [];
 
-			return geometry.index.array.join( ', ' );
+		if ( index !== null ) {
 
-		}
+			for ( let i = 0; i < index.count; i ++ ) {
 
-		const array = [];
-		const length = geometry.attributes.position.count;
+				array.push( index.getX( i ) );
+
+			}
 
-		for ( let i = 0; i < length; i ++ ) {
+		} else {
+
+			const length = geometry.attributes.position.count;
+
+			for ( let i = 0; i < length; i ++ ) {
 
-			array.push( i );
+				array.push( i );
+
+			}
 
 		}
 
@@ -264,11 +272,13 @@ def "Geometry"
 		}
 
 		const array = [];
-		const data = attribute.array;
 
-		for ( let i = 0; i < data.length; i += 3 ) {
+		for ( let i = 0; i < attribute.count; i ++ ) {
 
-			array.push( `(${data[ i + 0 ].toPrecision( PRECISION )}, ${data[ i + 1 ].toPrecision( PRECISION )}, ${data[ i + 2 ].toPrecision( PRECISION )})` );
+			const x = attribute.getX( i );
+			const y = attribute.getY( i );
+			const z = attribute.getZ( i );
+			array.push( `(${x.toPrecision( PRECISION )}, ${y.toPrecision( PRECISION )}, ${z.toPrecision( PRECISION )})` );
 
 		}
 
@@ -286,11 +296,12 @@ def "Geometry"
 		}
 
 		const array = [];
-		const data = attribute.array;
 
-		for ( let i = 0; i < data.length; i += 2 ) {
+		for ( let i = 0; i < attribute.count; i ++ ) {
 
-			array.push( `(${data[ i + 0 ].toPrecision( PRECISION )}, ${1 - data[ i + 1 ].toPrecision( PRECISION )})` );
+			const x = attribute.getX( i );
+			const y = attribute.getY( i );
+			array.push( `(${x.toPrecision( PRECISION )}, ${1 - y.toPrecision( PRECISION )})` );
 
 		}
 

+ 52 - 19
examples/js/lines/LineMaterial.js

@@ -52,10 +52,23 @@
 		attribute vec3 instanceColorStart;
 		attribute vec3 instanceColorEnd;
 
-		varying vec2 vUv;
-		varying vec4 worldPos;
-		varying vec3 worldStart;
-		varying vec3 worldEnd;
+		#ifdef WORLD_UNITS
+
+			varying vec4 worldPos;
+			varying vec3 worldStart;
+			varying vec3 worldEnd;
+
+			#ifdef USE_DASH
+
+				varying vec2 vUv;
+
+			#endif
+
+		#else
+
+			varying vec2 vUv;
+
+		#endif
 
 		#ifdef USE_DASH
 
@@ -92,19 +105,26 @@
 			#ifdef USE_DASH
 
 				vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
+				vUv = uv;
 
 			#endif
 
 			float aspect = resolution.x / resolution.y;
 
-			vUv = uv;
-
 			// camera space
 			vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
 			vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
 
-			worldStart = start.xyz;
-			worldEnd = end.xyz;
+			#ifdef WORLD_UNITS
+
+				worldStart = start.xyz;
+				worldEnd = end.xyz;
+
+			#else
+
+				vUv = uv;
+
+			#endif
 
 			// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
 			// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
@@ -260,9 +280,24 @@
 		#endif
 
 		varying float vLineDistance;
-		varying vec4 worldPos;
-		varying vec3 worldStart;
-		varying vec3 worldEnd;
+
+		#ifdef WORLD_UNITS
+
+			varying vec4 worldPos;
+			varying vec3 worldStart;
+			varying vec3 worldEnd;
+
+			#ifdef USE_DASH
+
+				varying vec2 vUv;
+
+			#endif
+
+		#else
+
+			varying vec2 vUv;
+
+		#endif
 
 		#include <common>
 		#include <color_pars_fragment>
@@ -270,8 +305,6 @@
 		#include <logdepthbuf_pars_fragment>
 		#include <clipping_planes_pars_fragment>
 
-		varying vec2 vUv;
-
 		vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
 
 			float mua;
@@ -330,7 +363,7 @@
 
 				#ifndef USE_DASH
 
-					#ifdef ALPHA_TO_COVERAGE
+					#ifdef USE_ALPHA_TO_COVERAGE
 
 						float dnorm = fwidth( norm );
 						alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
@@ -349,7 +382,7 @@
 
 			#else
 
-				#ifdef ALPHA_TO_COVERAGE
+				#ifdef USE_ALPHA_TO_COVERAGE
 
 					// artifacts appear on some hardware if a derivative is taken within a conditional
 					float a = vUv.x;
@@ -566,12 +599,12 @@
 					enumerable: true,
 					get: function () {
 
-						return Boolean( 'ALPHA_TO_COVERAGE' in this.defines );
+						return Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines );
 
 					},
 					set: function ( value ) {
 
-						if ( Boolean( value ) !== Boolean( 'ALPHA_TO_COVERAGE' in this.defines ) ) {
+						if ( Boolean( value ) !== Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines ) ) {
 
 							this.needsUpdate = true;
 
@@ -579,12 +612,12 @@
 
 						if ( value === true ) {
 
-							this.defines.ALPHA_TO_COVERAGE = '';
+							this.defines.USE_ALPHA_TO_COVERAGE = '';
 							this.extensions.derivatives = true;
 
 						} else {
 
-							delete this.defines.ALPHA_TO_COVERAGE;
+							delete this.defines.USE_ALPHA_TO_COVERAGE;
 							this.extensions.derivatives = false;
 
 						}

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

@@ -1299,7 +1299,7 @@
 				for ( let i = 0; i < buildData.length; i ++ ) {
 
 					const buildItem = buildData[ i ];
-					const object3D = objects[ buildItem[ 'objectId' ] ]; // apply transform
+					const object3D = objects[ buildItem[ 'objectId' ] ].clone(); // apply transform
 
 					const transform = buildItem[ 'transform' ];
 

+ 61 - 3
examples/js/loaders/ColladaLoader.js

@@ -1110,6 +1110,10 @@
 							data.parameters = parseEffectParameters( child );
 							break;
 
+						case 'extra':
+							data.extra = parseEffectExtra( child );
+							break;
+
 					}
 
 				}
@@ -1267,6 +1271,10 @@
 
 							break;
 
+						case 'bump':
+							data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
+							break;
+
 					}
 
 				}
@@ -1311,6 +1319,37 @@
 							data[ child.nodeName ] = parseInt( child.textContent );
 							break;
 
+						case 'bump':
+							data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
+							break;
+
+					}
+
+				}
+
+				return data;
+
+			}
+
+			function parseEffectExtraTechniqueBump( xml ) {
+
+				var data = {};
+
+				for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) {
+
+					var child = xml.childNodes[ i ];
+					if ( child.nodeType !== 1 ) continue;
+
+					switch ( child.nodeName ) {
+
+						case 'texture':
+							data[ child.nodeName ] = {
+								id: child.getAttribute( 'texture' ),
+								texcoord: child.getAttribute( 'texcoord' ),
+								extra: parseEffectParameterTexture( child )
+							};
+							break;
+
 					}
 
 				}
@@ -1383,7 +1422,6 @@
 
 				const effect = getEffect( data.url );
 				const technique = effect.profile.technique;
-				const extra = effect.profile.extra;
 				let material;
 
 				switch ( technique.type ) {
@@ -1559,6 +1597,7 @@
 								break;
 
 							default:
+								material.opacity = 1 - transparency.float;
 								console.warn( 'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque );
 
 						}
@@ -1570,9 +1609,28 @@
 				} //
 
 
-				if ( extra !== undefined && extra.technique !== undefined && extra.technique.double_sided === 1 ) {
+				if ( technique.extra !== undefined && technique.extra.technique !== undefined ) {
+
+					const techniques = technique.extra.technique;
 
-					material.side = THREE.DoubleSide;
+					for ( const k in techniques ) {
+
+						const v = techniques[ k ];
+
+						switch ( k ) {
+
+							case 'double_sided':
+								material.side = v === 1 ? THREE.DoubleSide : THREE.FrontSide;
+								break;
+
+							case 'bump':
+								material.normalMap = getTexture( v.texture );
+								material.normalScale = new THREE.Vector2( 1, 1 );
+								break;
+
+						}
+
+					}
 
 				}
 

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

@@ -1310,7 +1310,7 @@
 
 				for ( const nodeID in BindPoseNode ) {
 
-					if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) {
+					if ( BindPoseNode[ nodeID ].attrType === 'BindPose' && BindPoseNode[ nodeID ].NbPoseNodes > 0 ) {
 
 						const poseNodes = BindPoseNode[ nodeID ].PoseNode;
 

+ 82 - 29
examples/js/loaders/GLTFLoader.js

@@ -23,6 +23,11 @@
 
 				return new GLTFTextureWebPExtension( parser );
 
+			} );
+			this.register( function ( parser ) {
+
+				return new GLTFMaterialsSheenExtension( parser );
+
 			} );
 			this.register( function ( parser ) {
 
@@ -336,6 +341,7 @@
 		KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 		KHR_MATERIALS_IOR: 'KHR_materials_ior',
 		KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
+		KHR_MATERIALS_SHEEN: 'KHR_materials_sheen',
 		KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 		KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 		KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
@@ -592,6 +598,77 @@
 
 		}
 
+	}
+	/**
+ * Sheen Materials Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen
+ */
+
+
+	class GLTFMaterialsSheenExtension {
+
+		constructor( parser ) {
+
+			this.parser = parser;
+			this.name = EXTENSIONS.KHR_MATERIALS_SHEEN;
+
+		}
+
+		getMaterialType( materialIndex ) {
+
+			const parser = this.parser;
+			const materialDef = parser.json.materials[ materialIndex ];
+			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
+			return THREE.MeshPhysicalMaterial;
+
+		}
+
+		extendMaterialParams( materialIndex, materialParams ) {
+
+			const parser = this.parser;
+			const materialDef = parser.json.materials[ materialIndex ];
+
+			if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
+
+				return Promise.resolve();
+
+			}
+
+			const pending = [];
+			materialParams.sheenColor = new THREE.Color( 0, 0, 0 );
+			materialParams.sheenRoughness = 0;
+			materialParams.sheen = 1;
+			const extension = materialDef.extensions[ this.name ];
+
+			if ( extension.sheenColorFactor !== undefined ) {
+
+				materialParams.sheenColor.fromArray( extension.sheenColorFactor );
+
+			}
+
+			if ( extension.sheenRoughnessFactor !== undefined ) {
+
+				materialParams.sheenRoughness = extension.sheenRoughnessFactor;
+
+			}
+
+			if ( extension.sheenColorTexture !== undefined ) {
+
+				pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture ) );
+
+			}
+
+			if ( extension.sheenRoughnessTexture !== undefined ) {
+
+				pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) );
+
+			}
+
+			return Promise.all( pending );
+
+		}
+
 	}
 	/**
  * Transmission Materials Extension
@@ -698,7 +775,7 @@
 
 			materialParams.attenuationDistance = extension.attenuationDistance || 0;
 			const colorArray = extension.attenuationColor || [ 1, 1, 1 ];
-			materialParams.attenuationTint = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
+			materialParams.attenuationColor = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
 			return Promise.all( pending );
 
 		}
@@ -794,11 +871,11 @@
 			}
 
 			const colorArray = extension.specularColorFactor || [ 1, 1, 1 ];
-			materialParams.specularTint = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
+			materialParams.specularColor = new THREE.Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
 
 			if ( extension.specularColorTexture !== undefined ) {
 
-				pending.push( parser.assignTexture( materialParams, 'specularTintMap', extension.specularColorTexture ).then( function ( texture ) {
+				pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture ).then( function ( texture ) {
 
 					texture.encoding = THREE.sRGBEncoding;
 
@@ -1658,34 +1735,10 @@
 		MASK: 'MASK',
 		BLEND: 'BLEND'
 	};
-	/* UTILITY FUNCTIONS */
-
-	function resolveURL( url, path ) {
-
-		// Invalid URL
-		if ( typeof url !== 'string' || url === '' ) return ''; // Host Relative URL
-
-		if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) {
-
-			path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' );
-
-		} // Absolute URL http://,https://,//
-
-
-		if ( /^(https?:)?\/\//i.test( url ) ) return url; // Data URI
-
-		if ( /^data:.*,.*$/i.test( url ) ) return url; // Blob URL
-
-		if ( /^blob:.*$/i.test( url ) ) return url; // Relative URL
-
-		return path + url;
-
-	}
 	/**
  * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
  */
 
-
 	function createDefaultMaterial( cache ) {
 
 		if ( cache[ 'DefaultMaterial' ] === undefined ) {
@@ -2312,7 +2365,7 @@
 			const options = this.options;
 			return new Promise( function ( resolve, reject ) {
 
-				loader.load( resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () {
+				loader.load( THREE.LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () {
 
 					reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) );
 
@@ -2547,7 +2600,7 @@
 
 					}
 
-					loader.load( resolveURL( sourceURI, options.path ), onLoad, undefined, reject );
+					loader.load( THREE.LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject );
 
 				} );
 

+ 12 - 0
examples/js/loaders/KTX2Loader.js

@@ -17,6 +17,8 @@
 
 	const _taskCache = new WeakMap();
 
+	let _activeLoaders = 0;
+
 	class KTX2Loader extends THREE.Loader {
 
 		constructor( manager ) {
@@ -109,6 +111,15 @@
 
 				} );
 
+				if ( _activeLoaders > 0 ) {
+
+					// Each instance loads a transcoder and allocates workers, increasing network and memory cost.
+					console.warn( 'THREE.KTX2Loader: Multiple active KTX2 loaders may cause performance issues.' + ' Use a single KTX2Loader instance, or call .dispose() on old instances.' );
+
+				}
+
+				_activeLoaders ++;
+
 			}
 
 			return this.transcoderPending;
@@ -207,6 +218,7 @@
 
 			URL.revokeObjectURL( this.workerSourceURL );
 			this.workerPool.dispose();
+			_activeLoaders --;
 			return this;
 
 		}

+ 2 - 1
examples/js/loaders/RGBELoader.js

@@ -220,7 +220,8 @@
 					const scanline_width = w;
 
 					if ( // run length encoding is not allowed so read flat
-						scanline_width < 8 || scanline_width > 0x7fff || 2 !== buffer[ 0 ] || 2 !== buffer[ 1 ] || buffer[ 2 ] & 0x80 ) {
+						scanline_width < 8 || scanline_width > 0x7fff || // this file is not run length encoded
+      2 !== buffer[ 0 ] || 2 !== buffer[ 1 ] || buffer[ 2 ] & 0x80 ) {
 
 						// return the flat buffer
 						return new Uint8Array( buffer );

+ 237 - 271
examples/js/loaders/TDSLoader.js

@@ -16,7 +16,6 @@
 			super( manager );
 			this.debug = false;
 			this.group = null;
-			this.position = 0;
 			this.materials = [];
 			this.meshes = [];
 
@@ -79,7 +78,6 @@
 		parse( arraybuffer, path ) {
 
 			this.group = new THREE.Group();
-			this.position = 0;
 			this.materials = [];
 			this.meshes = [];
 			this.readFile( arraybuffer, path );
@@ -105,31 +103,30 @@
 		readFile( arraybuffer, path ) {
 
 			const data = new DataView( arraybuffer );
-			const chunk = this.readChunk( data );
+			const chunk = new Chunk( data, 0, this.debugMessage );
 
 			if ( chunk.id === MLIBMAGIC || chunk.id === CMAGIC || chunk.id === M3DMAGIC ) {
 
-				let next = this.nextChunk( data, chunk );
+				let next = chunk.readChunk();
 
-				while ( next !== 0 ) {
+				while ( next ) {
 
-					if ( next === M3D_VERSION ) {
+					if ( next.id === M3D_VERSION ) {
 
-						const version = this.readDWord( data );
+						const version = next.readDWord();
 						this.debugMessage( '3DS file version: ' + version );
 
-					} else if ( next === MDATA ) {
+					} else if ( next.id === MDATA ) {
 
-						this.resetPosition( data );
-						this.readMeshData( data, path );
+						this.readMeshData( next, path );
 
 					} else {
 
-						this.debugMessage( 'Unknown main chunk: ' + next.toString( 16 ) );
+						this.debugMessage( 'Unknown main chunk: ' + next.hexId );
 
 					}
 
-					next = this.nextChunk( data, chunk );
+					next = chunk.readChunk();
 
 				}
 
@@ -142,48 +139,45 @@
    * Read mesh data chunk.
    *
    * @method readMeshData
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk to read mesh from
    * @param {String} path Path for external resources.
    */
 
 
-		readMeshData( data, path ) {
+		readMeshData( chunk, path ) {
 
-			const chunk = this.readChunk( data );
-			let next = this.nextChunk( data, chunk );
+			let next = chunk.readChunk();
 
-			while ( next !== 0 ) {
+			while ( next ) {
 
-				if ( next === MESH_VERSION ) {
+				if ( next.id === MESH_VERSION ) {
 
-					const version = + this.readDWord( data );
+					const version = + next.readDWord();
 					this.debugMessage( 'Mesh Version: ' + version );
 
-				} else if ( next === MASTER_SCALE ) {
+				} else if ( next.id === MASTER_SCALE ) {
 
-					const scale = this.readFloat( data );
+					const scale = next.readFloat();
 					this.debugMessage( 'Master scale: ' + scale );
 					this.group.scale.set( scale, scale, scale );
 
-				} else if ( next === NAMED_OBJECT ) {
+				} else if ( next.id === NAMED_OBJECT ) {
 
 					this.debugMessage( 'Named Object' );
-					this.resetPosition( data );
-					this.readNamedObject( data );
+					this.readNamedObject( next );
 
-				} else if ( next === MAT_ENTRY ) {
+				} else if ( next.id === MAT_ENTRY ) {
 
 					this.debugMessage( 'Material' );
-					this.resetPosition( data );
-					this.readMaterialEntry( data, path );
+					this.readMaterialEntry( next, path );
 
 				} else {
 
-					this.debugMessage( 'Unknown MDATA chunk: ' + next.toString( 16 ) );
+					this.debugMessage( 'Unknown MDATA chunk: ' + next.hexId );
 
 				}
 
-				next = this.nextChunk( data, chunk );
+				next = chunk.readChunk();
 
 			}
 
@@ -192,145 +186,134 @@
    * Read named object chunk.
    *
    * @method readNamedObject
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    */
 
 
-		readNamedObject( data ) {
+		readNamedObject( chunk ) {
 
-			const chunk = this.readChunk( data );
-			const name = this.readString( data, 64 );
-			chunk.cur = this.position;
-			let next = this.nextChunk( data, chunk );
+			const name = chunk.readString();
+			let next = chunk.readChunk();
 
-			while ( next !== 0 ) {
+			while ( next ) {
 
-				if ( next === N_TRI_OBJECT ) {
+				if ( next.id === N_TRI_OBJECT ) {
 
-					this.resetPosition( data );
-					const mesh = this.readMesh( data );
+					const mesh = this.readMesh( next );
 					mesh.name = name;
 					this.meshes.push( mesh );
 
 				} else {
 
-					this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) );
+					this.debugMessage( 'Unknown named object chunk: ' + next.hexId );
 
 				}
 
-				next = this.nextChunk( data, chunk );
+				next = chunk.readChunk();
 
 			}
 
-			this.endChunk( chunk );
-
 		}
 		/**
    * Read material data chunk and add it to the material list.
    *
    * @method readMaterialEntry
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @param {String} path Path for external resources.
    */
 
 
-		readMaterialEntry( data, path ) {
+		readMaterialEntry( chunk, path ) {
 
-			const chunk = this.readChunk( data );
-			let next = this.nextChunk( data, chunk );
+			let next = chunk.readChunk();
 			const material = new THREE.MeshPhongMaterial();
 
-			while ( next !== 0 ) {
+			while ( next ) {
 
-				if ( next === MAT_NAME ) {
+				if ( next.id === MAT_NAME ) {
 
-					material.name = this.readString( data, 64 );
+					material.name = next.readString();
 					this.debugMessage( '   Name: ' + material.name );
 
-				} else if ( next === MAT_WIRE ) {
+				} else if ( next.id === MAT_WIRE ) {
 
 					this.debugMessage( '   Wireframe' );
 					material.wireframe = true;
 
-				} else if ( next === MAT_WIRE_SIZE ) {
+				} else if ( next.id === MAT_WIRE_SIZE ) {
 
-					const value = this.readByte( data );
+					const value = next.readByte();
 					material.wireframeLinewidth = value;
 					this.debugMessage( '   Wireframe Thickness: ' + value );
 
-				} else if ( next === MAT_TWO_SIDE ) {
+				} else if ( next.id === MAT_TWO_SIDE ) {
 
 					material.side = THREE.DoubleSide;
 					this.debugMessage( '   DoubleSided' );
 
-				} else if ( next === MAT_ADDITIVE ) {
+				} else if ( next.id === MAT_ADDITIVE ) {
 
 					this.debugMessage( '   Additive Blending' );
 					material.blending = THREE.AdditiveBlending;
 
-				} else if ( next === MAT_DIFFUSE ) {
+				} else if ( next.id === MAT_DIFFUSE ) {
 
 					this.debugMessage( '   Diffuse THREE.Color' );
-					material.color = this.readColor( data );
+					material.color = this.readColor( next );
 
-				} else if ( next === MAT_SPECULAR ) {
+				} else if ( next.id === MAT_SPECULAR ) {
 
 					this.debugMessage( '   Specular THREE.Color' );
-					material.specular = this.readColor( data );
+					material.specular = this.readColor( next );
 
-				} else if ( next === MAT_AMBIENT ) {
+				} else if ( next.id === MAT_AMBIENT ) {
 
 					this.debugMessage( '   Ambient color' );
-					material.color = this.readColor( data );
+					material.color = this.readColor( next );
 
-				} else if ( next === MAT_SHININESS ) {
+				} else if ( next.id === MAT_SHININESS ) {
 
-					const shininess = this.readPercentage( data );
+					const shininess = this.readPercentage( next );
 					material.shininess = shininess * 100;
 					this.debugMessage( '   Shininess : ' + shininess );
 
-				} else if ( next === MAT_TRANSPARENCY ) {
+				} else if ( next.id === MAT_TRANSPARENCY ) {
 
-					const transparency = this.readPercentage( data );
+					const transparency = this.readPercentage( next );
 					material.opacity = 1 - transparency;
 					this.debugMessage( '  Transparency : ' + transparency );
 					material.transparent = material.opacity < 1 ? true : false;
 
-				} else if ( next === MAT_TEXMAP ) {
+				} else if ( next.id === MAT_TEXMAP ) {
 
 					this.debugMessage( '   ColorMap' );
-					this.resetPosition( data );
-					material.map = this.readMap( data, path );
+					material.map = this.readMap( next, path );
 
-				} else if ( next === MAT_BUMPMAP ) {
+				} else if ( next.id === MAT_BUMPMAP ) {
 
 					this.debugMessage( '   BumpMap' );
-					this.resetPosition( data );
-					material.bumpMap = this.readMap( data, path );
+					material.bumpMap = this.readMap( next, path );
 
-				} else if ( next === MAT_OPACMAP ) {
+				} else if ( next.id === MAT_OPACMAP ) {
 
 					this.debugMessage( '   OpacityMap' );
-					this.resetPosition( data );
-					material.alphaMap = this.readMap( data, path );
+					material.alphaMap = this.readMap( next, path );
 
-				} else if ( next === MAT_SPECMAP ) {
+				} else if ( next.id === MAT_SPECMAP ) {
 
 					this.debugMessage( '   SpecularMap' );
-					this.resetPosition( data );
-					material.specularMap = this.readMap( data, path );
+					material.specularMap = this.readMap( next, path );
 
 				} else {
 
-					this.debugMessage( '   Unknown material chunk: ' + next.toString( 16 ) );
+					this.debugMessage( '   Unknown material chunk: ' + next.hexId );
 
 				}
 
-				next = this.nextChunk( data, chunk );
+				next = chunk.readChunk();
 
 			}
 
-			this.endChunk( chunk );
 			this.materials[ material.name ] = material;
 
 		}
@@ -338,68 +321,66 @@
    * Read mesh data chunk.
    *
    * @method readMesh
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @return {Mesh} The parsed mesh.
    */
 
 
-		readMesh( data ) {
+		readMesh( chunk ) {
 
-			const chunk = this.readChunk( data );
-			let next = this.nextChunk( data, chunk );
+			let next = chunk.readChunk();
 			const geometry = new THREE.BufferGeometry();
 			const material = new THREE.MeshPhongMaterial();
 			const mesh = new THREE.Mesh( geometry, material );
 			mesh.name = 'mesh';
 
-			while ( next !== 0 ) {
+			while ( next ) {
 
-				if ( next === POINT_ARRAY ) {
+				if ( next.id === POINT_ARRAY ) {
 
-					const points = this.readWord( data );
+					const points = next.readWord();
 					this.debugMessage( '   Vertex: ' + points ); //BufferGeometry
 
 					const vertices = [];
 
 					for ( let i = 0; i < points; i ++ ) {
 
-						vertices.push( this.readFloat( data ) );
-						vertices.push( this.readFloat( data ) );
-						vertices.push( this.readFloat( data ) );
+						vertices.push( next.readFloat() );
+						vertices.push( next.readFloat() );
+						vertices.push( next.readFloat() );
 
 					}
 
 					geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
 
-				} else if ( next === FACE_ARRAY ) {
+				} else if ( next.id === FACE_ARRAY ) {
 
-					this.resetPosition( data );
-					this.readFaceArray( data, mesh );
+					this.readFaceArray( next, mesh );
 
-				} else if ( next === TEX_VERTS ) {
+				} else if ( next.id === TEX_VERTS ) {
 
-					const texels = this.readWord( data );
+					const texels = next.readWord();
 					this.debugMessage( '   UV: ' + texels ); //BufferGeometry
 
 					const uvs = [];
 
 					for ( let i = 0; i < texels; i ++ ) {
 
-						uvs.push( this.readFloat( data ) );
-						uvs.push( this.readFloat( data ) );
+						uvs.push( next.readFloat() );
+						uvs.push( next.readFloat() );
 
 					}
 
 					geometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
 
-				} else if ( next === MESH_MATRIX ) {
+				} else if ( next.id === MESH_MATRIX ) {
 
 					this.debugMessage( '   Tranformation Matrix (TODO)' );
 					const values = [];
 
 					for ( let i = 0; i < 12; i ++ ) {
 
-						values[ i ] = this.readFloat( data );
+						values[ i ] = next.readFloat();
 
 					}
 
@@ -432,15 +413,14 @@
 
 				} else {
 
-					this.debugMessage( '   Unknown mesh chunk: ' + next.toString( 16 ) );
+					this.debugMessage( '   Unknown mesh chunk: ' + next.hexId );
 
 				}
 
-				next = this.nextChunk( data, chunk );
+				next = chunk.readChunk();
 
 			}
 
-			this.endChunk( chunk );
 			geometry.computeVertexNormals();
 			return mesh;
 
@@ -449,22 +429,21 @@
    * Read face array data chunk.
    *
    * @method readFaceArray
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @param {Mesh} mesh THREE.Mesh to be filled with the data read.
    */
 
 
-		readFaceArray( data, mesh ) {
+		readFaceArray( chunk, mesh ) {
 
-			const chunk = this.readChunk( data );
-			const faces = this.readWord( data );
+			const faces = chunk.readWord();
 			this.debugMessage( '   Faces: ' + faces );
 			const index = [];
 
 			for ( let i = 0; i < faces; ++ i ) {
 
-				index.push( this.readWord( data ), this.readWord( data ), this.readWord( data ) );
-				this.readWord( data ); // visibility
+				index.push( chunk.readWord(), chunk.readWord(), chunk.readWord() );
+				chunk.readWord(); // visibility
 
 			}
 
@@ -473,15 +452,14 @@
 			let materialIndex = 0;
 			let start = 0;
 
-			while ( this.position < chunk.end ) {
+			while ( ! chunk.endOfChunk ) {
 
-				const subchunk = this.readChunk( data );
+				const subchunk = chunk.readChunk();
 
 				if ( subchunk.id === MSH_MAT_GROUP ) {
 
 					this.debugMessage( '      Material THREE.Group' );
-					this.resetPosition( data );
-					const group = this.readMaterialGroup( data );
+					const group = this.readMaterialGroup( subchunk );
 					const count = group.index.length * 3; // assuming successive indices
 
 					mesh.geometry.addGroup( start, count, materialIndex );
@@ -498,76 +476,70 @@
 
 				} else {
 
-					this.debugMessage( '      Unknown face array chunk: ' + subchunk.toString( 16 ) );
+					this.debugMessage( '      Unknown face array chunk: ' + subchunk.hexId );
 
 				}
 
-				this.endChunk( subchunk );
-
 			}
 
 			if ( mesh.material.length === 1 ) mesh.material = mesh.material[ 0 ]; // for backwards compatibility
 
-			this.endChunk( chunk );
-
 		}
 		/**
    * Read texture map data chunk.
    *
    * @method readMap
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @param {String} path Path for external resources.
    * @return {Texture} Texture read from this data chunk.
    */
 
 
-		readMap( data, path ) {
+		readMap( chunk, path ) {
 
-			const chunk = this.readChunk( data );
-			let next = this.nextChunk( data, chunk );
+			let next = chunk.readChunk();
 			let texture = {};
 			const loader = new THREE.TextureLoader( this.manager );
 			loader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
 
-			while ( next !== 0 ) {
+			while ( next ) {
 
-				if ( next === MAT_MAPNAME ) {
+				if ( next.id === MAT_MAPNAME ) {
 
-					const name = this.readString( data, 128 );
+					const name = next.readString();
 					texture = loader.load( name );
 					this.debugMessage( '      File: ' + path + name );
 
-				} else if ( next === MAT_MAP_UOFFSET ) {
+				} else if ( next.id === MAT_MAP_UOFFSET ) {
 
-					texture.offset.x = this.readFloat( data );
+					texture.offset.x = next.readFloat();
 					this.debugMessage( '      OffsetX: ' + texture.offset.x );
 
-				} else if ( next === MAT_MAP_VOFFSET ) {
+				} else if ( next.id === MAT_MAP_VOFFSET ) {
 
-					texture.offset.y = this.readFloat( data );
+					texture.offset.y = next.readFloat();
 					this.debugMessage( '      OffsetY: ' + texture.offset.y );
 
-				} else if ( next === MAT_MAP_USCALE ) {
+				} else if ( next.id === MAT_MAP_USCALE ) {
 
-					texture.repeat.x = this.readFloat( data );
+					texture.repeat.x = next.readFloat();
 					this.debugMessage( '      RepeatX: ' + texture.repeat.x );
 
-				} else if ( next === MAT_MAP_VSCALE ) {
+				} else if ( next.id === MAT_MAP_VSCALE ) {
 
-					texture.repeat.y = this.readFloat( data );
+					texture.repeat.y = next.readFloat();
 					this.debugMessage( '      RepeatY: ' + texture.repeat.y );
 
 				} else {
 
-					this.debugMessage( '      Unknown map chunk: ' + next.toString( 16 ) );
+					this.debugMessage( '      Unknown map chunk: ' + next.hexId );
 
 				}
 
-				next = this.nextChunk( data, chunk );
+				next = chunk.readChunk();
 
 			}
 
-			this.endChunk( chunk );
 			return texture;
 
 		}
@@ -575,23 +547,22 @@
    * Read material group data chunk.
    *
    * @method readMaterialGroup
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @return {Object} Object with name and index of the object.
    */
 
 
-		readMaterialGroup( data ) {
+		readMaterialGroup( chunk ) {
 
-			this.readChunk( data );
-			const name = this.readString( data, 64 );
-			const numFaces = this.readWord( data );
+			const name = chunk.readString();
+			const numFaces = chunk.readWord();
 			this.debugMessage( '         Name: ' + name );
 			this.debugMessage( '         Faces: ' + numFaces );
 			const index = [];
 
 			for ( let i = 0; i < numFaces; ++ i ) {
 
-				index.push( this.readWord( data ) );
+				index.push( chunk.readWord() );
 
 			}
 
@@ -605,132 +576,191 @@
    * Read a color value.
    *
    * @method readColor
-   * @param {DataView} data Dataview.
+   * @param {Chunk} chunk Chunk.
    * @return {Color} THREE.Color value read..
    */
 
 
-		readColor( data ) {
+		readColor( chunk ) {
 
-			const chunk = this.readChunk( data );
+			const subChunk = chunk.readChunk();
 			const color = new THREE.Color();
 
-			if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) {
+			if ( subChunk.id === COLOR_24 || subChunk.id === LIN_COLOR_24 ) {
 
-				const r = this.readByte( data );
-				const g = this.readByte( data );
-				const b = this.readByte( data );
+				const r = subChunk.readByte();
+				const g = subChunk.readByte();
+				const b = subChunk.readByte();
 				color.setRGB( r / 255, g / 255, b / 255 );
 				this.debugMessage( '      THREE.Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 
-			} else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) {
+			} else if ( subChunk.id === COLOR_F || subChunk.id === LIN_COLOR_F ) {
 
-				const r = this.readFloat( data );
-				const g = this.readFloat( data );
-				const b = this.readFloat( data );
+				const r = subChunk.readFloat();
+				const g = subChunk.readFloat();
+				const b = subChunk.readFloat();
 				color.setRGB( r, g, b );
 				this.debugMessage( '      THREE.Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 
 			} else {
 
-				this.debugMessage( '      Unknown color chunk: ' + chunk.toString( 16 ) );
+				this.debugMessage( '      Unknown color chunk: ' + subChunk.hexId );
 
 			}
 
-			this.endChunk( chunk );
 			return color;
 
 		}
 		/**
-   * Read next chunk of data.
+   * Read percentage value.
    *
-   * @method readChunk
-   * @param {DataView} data Dataview.
-   * @return {Object} Chunk of data read.
+   * @method readPercentage
+   * @param {Chunk} chunk Chunk to read data from.
+   * @return {Number} Data read from the dataview.
    */
 
 
-		readChunk( data ) {
+		readPercentage( chunk ) {
 
-			const chunk = {};
-			chunk.cur = this.position;
-			chunk.id = this.readWord( data );
-			chunk.size = this.readDWord( data );
-			chunk.end = chunk.cur + chunk.size;
-			chunk.cur += 6;
-			return chunk;
+			const subChunk = chunk.readChunk();
+
+			switch ( subChunk.id ) {
+
+				case INT_PERCENTAGE:
+					return subChunk.readShort() / 100;
+					break;
+
+				case FLOAT_PERCENTAGE:
+					return subChunk.readFloat();
+					break;
+
+				default:
+					this.debugMessage( '      Unknown percentage chunk: ' + subChunk.hexId );
+					return 0;
+
+			}
 
 		}
 		/**
-   * Set position to the end of the current chunk of data.
+   * Print debug message to the console.
+   *
+   * Is controlled by a flag to show or hide debug messages.
    *
-   * @method endChunk
-   * @param {Object} chunk Data chunk.
+   * @method debugMessage
+   * @param {Object} message Debug message to print to the console.
    */
 
 
-		endChunk( chunk ) {
+		debugMessage( message ) {
+
+			if ( this.debug ) {
 
-			this.position = chunk.end;
+				console.log( message );
+
+			}
 
 		}
+
+	}
+	/** Read data/sub-chunks from chunk */
+
+
+	class Chunk {
+
 		/**
-   * Move to the next data chunk.
+   * Create a new chunk
    *
-   * @method nextChunk
-   * @param {DataView} data Dataview.
-   * @param {Object} chunk Data chunk.
+   * @class Chunk
+   * @param {DataView} data DataView to read from.
+   * @param {Number} position in data.
+   * @param {Function} debugMessage logging callback.
    */
+		constructor( data, position, debugMessage ) {
 
+			this.data = data; // the offset to the begin of this chunk
 
-		nextChunk( data, chunk ) {
+			this.offset = position; // the current reading position
 
-			if ( chunk.cur >= chunk.end ) {
+			this.position = position;
+			this.debugMessage = debugMessage;
 
-				return 0;
+			if ( this.debugMessage instanceof Function ) {
+
+				this.debugMessage = function () {};
+
+			}
+
+			this.id = this.readWord();
+			this.size = this.readDWord();
+			this.end = this.offset + this.size;
+
+			if ( this.end > data.byteLength ) {
+
+				this.debugMessage( 'Bad chunk size for chunk at ' + position );
 
 			}
 
-			this.position = chunk.cur;
+		}
+		/**
+   * read a sub cchunk.
+   *
+   * @method readChunk
+   * @return {Chunk | null} next sub chunk
+   */
+
+
+		readChunk() {
+
+			if ( this.endOfChunk ) {
+
+				return null;
+
+			}
 
 			try {
 
-				const next = this.readChunk( data );
-				chunk.cur += next.size;
-				return next.id;
+				const next = new Chunk( this.data, this.position, this.debugMessage );
+				this.position += next.size;
+				return next;
 
 			} catch ( e ) {
 
 				this.debugMessage( 'Unable to read chunk at ' + this.position );
-				return 0;
+				return null;
 
 			}
 
 		}
 		/**
-   * Reset dataview position.
+   * return the ID of this chunk as Hex
    *
-   * @method resetPosition
+   * @method idToString
+   * @return {String} hex-string of id
    */
 
 
-		resetPosition() {
+		get hexId() {
 
-			this.position -= 6;
+			return this.id.toString( 16 );
+
+		}
+
+		get endOfChunk() {
+
+			return this.position >= this.end;
 
 		}
 		/**
    * Read byte value.
    *
    * @method readByte
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    */
 
 
-		readByte( data ) {
+		readByte() {
 
-			const v = data.getUint8( this.position, true );
+			const v = this.data.getUint8( this.position, true );
 			this.position += 1;
 			return v;
 
@@ -739,22 +769,22 @@
    * Read 32 bit float value.
    *
    * @method readFloat
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    */
 
 
-		readFloat( data ) {
+		readFloat() {
 
 			try {
 
-				const v = data.getFloat32( this.position, true );
+				const v = this.data.getFloat32( this.position, true );
 				this.position += 4;
 				return v;
 
 			} catch ( e ) {
 
-				this.debugMessage( e + ' ' + this.position + ' ' + data.byteLength );
+				this.debugMessage( e + ' ' + this.position + ' ' + this.data.byteLength );
+				return 0;
 
 			}
 
@@ -763,14 +793,13 @@
    * Read 32 bit signed integer value.
    *
    * @method readInt
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    */
 
 
-		readInt( data ) {
+		readInt() {
 
-			const v = data.getInt32( this.position, true );
+			const v = this.data.getInt32( this.position, true );
 			this.position += 4;
 			return v;
 
@@ -779,14 +808,13 @@
    * Read 16 bit signed integer value.
    *
    * @method readShort
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    */
 
 
-		readShort( data ) {
+		readShort() {
 
-			const v = data.getInt16( this.position, true );
+			const v = this.data.getInt16( this.position, true );
 			this.position += 2;
 			return v;
 
@@ -795,14 +823,13 @@
    * Read 64 bit unsigned integer value.
    *
    * @method readDWord
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    */
 
 
-		readDWord( data ) {
+		readDWord() {
 
-			const v = data.getUint32( this.position, true );
+			const v = this.data.getUint32( this.position, true );
 			this.position += 4;
 			return v;
 
@@ -811,101 +838,40 @@
    * Read 32 bit unsigned integer value.
    *
    * @method readWord
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    */
 
 
-		readWord( data ) {
+		readWord() {
 
-			const v = data.getUint16( this.position, true );
+			const v = this.data.getUint16( this.position, true );
 			this.position += 2;
 			return v;
 
 		}
 		/**
-   * Read string value.
+   * Read NULL terminated ASCII string value from chunk-pos.
    *
    * @method readString
-   * @param {DataView} data Dataview to read data from.
-   * @param {Number} maxLength Max size of the string to be read.
    * @return {String} Data read from the dataview.
    */
 
 
-		readString( data, maxLength ) {
+		readString() {
 
 			let s = '';
+			let c = this.readByte();
 
-			for ( let i = 0; i < maxLength; i ++ ) {
-
-				const c = this.readByte( data );
-
-				if ( ! c ) {
-
-					break;
-
-				}
+			while ( c ) {
 
 				s += String.fromCharCode( c );
+				c = this.readByte();
 
 			}
 
 			return s;
 
 		}
-		/**
-   * Read percentage value.
-   *
-   * @method readPercentage
-   * @param {DataView} data Dataview to read data from.
-   * @return {Number} Data read from the dataview.
-   */
-
-
-		readPercentage( data ) {
-
-			const chunk = this.readChunk( data );
-			let 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.
-   *
-   * Is controlled by a flag to show or hide debug messages.
-   *
-   * @method debugMessage
-   * @param {Object} message Debug message to print to the console.
-   */
-
-
-		debugMessage( message ) {
-
-			if ( this.debug ) {
-
-				console.log( message );
-
-			}
-
-		}
 
 	} // const NULL_CHUNK = 0x0000;
 

+ 42 - 128
examples/js/objects/MarchingCubes.js

@@ -4,18 +4,19 @@
  * Port of http://webglsamples.org/blob/blob.html
  */
 
-	class MarchingCubes extends THREE.ImmediateRenderObject {
+	class MarchingCubes extends THREE.Mesh {
 
-		constructor( resolution, material, enableUvs, enableColors ) {
+		constructor( resolution, material, enableUvs = false, enableColors = false, maxPolyCount = 10000 ) {
 
-			super( material );
+			const geometry = new THREE.BufferGeometry();
+			super( geometry, material );
 			const scope = this; // temp buffers used in polygonize
 
 			const vlist = new Float32Array( 12 * 3 );
 			const nlist = new Float32Array( 12 * 3 );
 			const clist = new Float32Array( 12 * 3 );
-			this.enableUvs = enableUvs !== undefined ? enableUvs : false;
-			this.enableColors = enableColors !== undefined ? enableColors : false; // functions have to be object properties
+			this.enableUvs = enableUvs;
+			this.enableColors = enableColors; // functions have to be object properties
 			// prototype functions kill performance
 			// (tested and it was 4x slower !!!)
 
@@ -35,27 +36,34 @@
 				this.zd = this.size2;
 				this.field = new Float32Array( this.size3 );
 				this.normal_cache = new Float32Array( this.size3 * 3 );
-				this.palette = new Float32Array( this.size3 * 3 ); // immediate render mode simulator
-
-				this.maxCount = 4096; // TODO: find the fastest size for this buffer
+				this.palette = new Float32Array( this.size3 * 3 ); //
 
 				this.count = 0;
-				this.hasPositions = false;
-				this.hasNormals = false;
-				this.hasColors = false;
-				this.hasUvs = false;
-				this.positionArray = new Float32Array( this.maxCount * 3 );
-				this.normalArray = new Float32Array( this.maxCount * 3 );
+				const maxVertexCount = maxPolyCount * 3;
+				this.positionArray = new Float32Array( maxVertexCount * 3 );
+				const positionAttribute = new THREE.BufferAttribute( this.positionArray, 3 );
+				positionAttribute.setUsage( THREE.DynamicDrawUsage );
+				geometry.setAttribute( 'position', positionAttribute );
+				this.normalArray = new Float32Array( maxVertexCount * 3 );
+				const normalAttribute = new THREE.BufferAttribute( this.normalArray, 3 );
+				normalAttribute.setUsage( THREE.DynamicDrawUsage );
+				geometry.setAttribute( 'normal', normalAttribute );
 
 				if ( this.enableUvs ) {
 
-					this.uvArray = new Float32Array( this.maxCount * 2 );
+					this.uvArray = new Float32Array( maxVertexCount * 2 );
+					const uvAttribute = new THREE.BufferAttribute( this.uvArray, 2 );
+					uvAttribute.setUsage( THREE.DynamicDrawUsage );
+					geometry.setAttribute( 'uv', uvAttribute );
 
 				}
 
 				if ( this.enableColors ) {
 
-					this.colorArray = new Float32Array( this.maxCount * 3 );
+					this.colorArray = new Float32Array( maxVertexCount * 3 );
+					const colorAttribute = new THREE.BufferAttribute( this.colorArray, 3 );
+					colorAttribute.setUsage( THREE.DynamicDrawUsage );
+					geometry.setAttribute( 'color', colorAttribute );
 
 				}
 
@@ -136,7 +144,7 @@
 			// (this is where most of time is spent - it's inner work of O(n3) loop )
 
 
-			function polygonize( fx, fy, fz, q, isol, renderCallback ) {
+			function polygonize( fx, fy, fz, q, isol ) {
 
 				// cache indices
 				const q1 = q + 1,
@@ -282,7 +290,7 @@
 					o1 = cubeindex + i;
 					o2 = o1 + 1;
 					o3 = o1 + 2;
-					posnormtriv( vlist, nlist, clist, 3 * triTable[ o1 ], 3 * triTable[ o2 ], 3 * triTable[ o3 ], renderCallback );
+					posnormtriv( vlist, nlist, clist, 3 * triTable[ o1 ], 3 * triTable[ o2 ], 3 * triTable[ o3 ] );
 					i += 3;
 					numtris ++;
 
@@ -290,12 +298,9 @@
 
 				return numtris;
 
-			} /////////////////////////////////////
-			// Immediate render mode simulator
-			/////////////////////////////////////
-
+			}
 
-			function posnormtriv( pos, norm, colors, o1, o2, o3, renderCallback ) {
+			function posnormtriv( pos, norm, colors, o1, o2, o3 ) {
 
 				const c = scope.count * 3; // positions
 
@@ -368,67 +373,7 @@
 
 				scope.count += 3;
 
-				if ( scope.count >= scope.maxCount - 3 ) {
-
-					scope.hasPositions = true;
-					scope.hasNormals = true;
-
-					if ( scope.enableUvs ) {
-
-						scope.hasUvs = true;
-
-					}
-
-					if ( scope.enableColors ) {
-
-						scope.hasColors = true;
-
-					}
-
-					renderCallback( scope );
-
-				}
-
-			}
-
-			this.begin = function () {
-
-				this.count = 0;
-				this.hasPositions = false;
-				this.hasNormals = false;
-				this.hasUvs = false;
-				this.hasColors = false;
-
-			};
-
-			this.end = function ( renderCallback ) {
-
-				if ( this.count === 0 ) return;
-
-				for ( let i = this.count * 3; i < this.positionArray.length; i ++ ) {
-
-					this.positionArray[ i ] = 0.0;
-
-				}
-
-				this.hasPositions = true;
-				this.hasNormals = true;
-
-				if ( this.enableUvs && this.material.map ) {
-
-					this.hasUvs = true;
-
-				}
-
-				if ( this.enableColors && this.material.vertexColors !== THREE.NoColors ) {
-
-					this.hasColors = true;
-
-				}
-
-				renderCallback( this );
-
-			}; /////////////////////////////////////
+			} /////////////////////////////////////
 			// Metaballs
 			/////////////////////////////////////
 			// Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after
@@ -729,9 +674,9 @@
 
 			};
 
-			this.render = function ( renderCallback ) {
+			this.onBeforeRender = function () {
 
-				this.begin(); // Triangulate. Yeah, this is slow.
+				this.count = 0; // Triangulate. Yeah, this is slow.
 
 				const smin2 = this.size - 2;
 
@@ -750,59 +695,28 @@
 							const fx = ( x - this.halfsize ) / this.halfsize; //+ 1
 
 							const q = y_offset + x;
-							polygonize( fx, fy, fz, q, this.isolation, renderCallback );
+							polygonize( fx, fy, fz, q, this.isolation );
 
 						}
 
 					}
 
-				}
-
-				this.end( renderCallback );
-
-			};
-
-			this.generateGeometry = function () {
-
-				console.warn( 'THREE.MarchingCubes: generateGeometry() now returns THREE.BufferGeometry' );
-				return this.generateBufferGeometry();
+				} // reset unneeded data
 
-			};
-
-			function concatenate( a, b, length ) {
 
-				const result = new Float32Array( a.length + length );
-				result.set( a, 0 );
-				result.set( b.slice( 0, length ), a.length );
-				return result;
-
-			}
-
-			this.generateBufferGeometry = function () {
+				for ( let i = this.count * 3; i < this.positionArray.length; i ++ ) {
 
-				const geo = new THREE.BufferGeometry();
-				let posArray = new Float32Array();
-				let normArray = new Float32Array();
-				let colorArray = new Float32Array();
-				let uvArray = new Float32Array();
-				const scope = this;
+					this.positionArray[ i ] = 0.0;
 
-				const geo_callback = function ( object ) {
+				} // update geometry data
 
-					if ( scope.hasPositions ) posArray = concatenate( posArray, object.positionArray, object.count * 3 );
-					if ( scope.hasNormals ) normArray = concatenate( normArray, object.normalArray, object.count * 3 );
-					if ( scope.hasColors ) colorArray = concatenate( colorArray, object.colorArray, object.count * 3 );
-					if ( scope.hasUvs ) uvArray = concatenate( uvArray, object.uvArray, object.count * 2 );
-					object.count = 0;
 
-				};
+				geometry.getAttribute( 'position' ).needsUpdate = true;
+				geometry.getAttribute( 'normal' ).needsUpdate = true;
+				if ( this.enableUvs ) geometry.getAttribute( 'uv' ).needsUpdate = true;
+				if ( this.enableColors ) geometry.getAttribute( 'color' ).needsUpdate = true; // safety check
 
-				this.render( geo_callback );
-				if ( this.hasPositions ) geo.setAttribute( 'position', new THREE.BufferAttribute( posArray, 3 ) );
-				if ( this.hasNormals ) geo.setAttribute( 'normal', new THREE.BufferAttribute( normArray, 3 ) );
-				if ( this.hasColors ) geo.setAttribute( 'color', new THREE.BufferAttribute( colorArray, 3 ) );
-				if ( this.hasUvs ) geo.setAttribute( 'uv', new THREE.BufferAttribute( uvArray, 2 ) );
-				return geo;
+				if ( this.count / 3 > maxPolyCount ) console.warn( 'THREE.MarchingCubes: Geometry buffers too small for rendering. Please create an instance with a higher poly count.' );
 
 			};
 
@@ -816,7 +730,7 @@
 	// Marching cubes lookup tables
 	/////////////////////////////////////
 	// These tables are straight from Paul Bourke's page:
-	// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
+	// http://paulbourke.net/geometry/polygonise/
 	// who in turn got them from Cory Gene Bloyd.
 
 	const edgeTable = new Int32Array( [ 0x0, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33, 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa, 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x66, 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff, 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55, 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc, 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66, 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa, 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33, 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99, 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 ] );

+ 4 - 4
examples/js/renderers/CSS2DRenderer.js

@@ -2,10 +2,10 @@
 
 	class CSS2DObject extends THREE.Object3D {
 
-		constructor( element ) {
+		constructor( element = document.createElement( 'div' ) ) {
 
 			super();
-			this.element = element || document.createElement( 'div' );
+			this.element = element;
 			this.element.style.position = 'absolute';
 			this.element.style.userSelect = 'none';
 			this.element.setAttribute( 'draggable', false );
@@ -49,7 +49,7 @@
 
 	class CSS2DRenderer {
 
-		constructor() {
+		constructor( parameters = {} ) {
 
 			const _this = this;
 
@@ -60,7 +60,7 @@
 			const cache = {
 				objects: new WeakMap()
 			};
-			const domElement = document.createElement( 'div' );
+			const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
 			domElement.style.overflow = 'hidden';
 			this.domElement = domElement;
 

+ 4 - 4
examples/js/renderers/CSS3DRenderer.js

@@ -12,10 +12,10 @@
 
 	class CSS3DObject extends THREE.Object3D {
 
-		constructor( element ) {
+		constructor( element = document.createElement( 'div' ) ) {
 
 			super();
-			this.element = element || document.createElement( 'div' );
+			this.element = element;
 			this.element.style.position = 'absolute';
 			this.element.style.pointerEvents = 'auto';
 			this.element.style.userSelect = 'none';
@@ -75,7 +75,7 @@
 
 	class CSS3DRenderer {
 
-		constructor() {
+		constructor( parameters = {} ) {
 
 			const _this = this;
 
@@ -90,7 +90,7 @@
 				},
 				objects: new WeakMap()
 			};
-			const domElement = document.createElement( 'div' );
+			const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
 			domElement.style.overflow = 'hidden';
 			this.domElement = domElement;
 			const cameraElement = document.createElement( 'div' );

+ 1 - 0
examples/js/utils/RoughnessMipmapper.js

@@ -77,6 +77,7 @@
 				material.roughnessMap.repeat.copy( roughnessMap.repeat );
 				material.roughnessMap.center.copy( roughnessMap.center );
 				material.roughnessMap.rotation = roughnessMap.rotation;
+				material.roughnessMap.image = roughnessMap.image;
 				material.roughnessMap.matrixAutoUpdate = roughnessMap.matrixAutoUpdate;
 				material.roughnessMap.matrix.copy( roughnessMap.matrix );
 

+ 50 - 17
examples/jsm/controls/ArcballControls.js

@@ -6,14 +6,14 @@ import {
 	LineBasicMaterial,
 	Raycaster,
 	Group,
-	Object3D,
 	Box3,
 	Sphere,
 	Quaternion,
 	Vector2,
 	Vector3,
 	Matrix4,
-	MathUtils
+	MathUtils,
+	EventDispatcher
 } from '../../../build/three.module.js';
 
 //trackball state
@@ -64,6 +64,8 @@ const _changeEvent = { type: 'change' };
 const _startEvent = { type: 'start' };
 const _endEvent = { type: 'end' };
 
+const _raycaster = new Raycaster();
+
 
 /**
  *
@@ -71,7 +73,7 @@ const _endEvent = { type: 'end' };
  * @param {HTMLElement} domElement Renderer's dom element
  * @param {Scene} scene The scene to be rendered
  */
-class ArcballControls extends Object3D {
+class ArcballControls extends EventDispatcher {
 
 	constructor( camera, domElement, scene = null ) {
 
@@ -79,6 +81,8 @@ class ArcballControls extends Object3D {
 		this.camera = null;
 		this.domElement = domElement;
 		this.scene = scene;
+		this.target = new Vector3( 0, 0, 0 );
+		this.radiusFactor = 0.67;
 
 		this.mouseActions = [];
 		this._mouseOp = null;
@@ -204,7 +208,6 @@ class ArcballControls extends Object3D {
 		this.maxZoom = Infinity;
 
 		//trackball parameters
-		this._tbCenter = new Vector3( 0, 0, 0 );
 		this._tbRadius = 1;
 
 		//FSA
@@ -1974,18 +1977,17 @@ class ArcballControls extends Object3D {
 	 */
 	calculateTbRadius = ( camera ) => {
 
-		const factor = 0.67;
 		const distance = camera.position.distanceTo( this._gizmos.position );
 
 		if ( camera.type == 'PerspectiveCamera' ) {
 
 			const halfFovV = MathUtils.DEG2RAD * camera.fov * 0.5; //vertical fov/2 in radians
 			const halfFovH = Math.atan( ( camera.aspect ) * Math.tan( halfFovV ) ); //horizontal fov/2 in radians
-			return Math.tan( Math.min( halfFovV, halfFovH ) ) * distance * factor;
+			return Math.tan( Math.min( halfFovV, halfFovH ) ) * distance * this.radiusFactor;
 
 		} else if ( camera.type == 'OrthographicCamera' ) {
 
-			return Math.min( camera.top, camera.right ) * factor;
+			return Math.min( camera.top, camera.right ) * this.radiusFactor;
 
 		}
 
@@ -2097,9 +2099,9 @@ class ArcballControls extends Object3D {
 		window.removeEventListener( 'pointerup', this.onPointerUp );
 
 		window.removeEventListener( 'resize', this.onWindowResize );
-		window.addEventListener( 'keydown', this.onKeyDown );
+		window.removeEventListener( 'keydown', this.onKeyDown );
 
-		this.scene.remove( this._gizmos );
+		if ( this.scene !== null ) this.scene.remove( this._gizmos );
 		this.disposeGrid();
 
 	};
@@ -2193,7 +2195,7 @@ class ArcballControls extends Object3D {
 	 */
 	setCamera = ( camera ) => {
 
-		camera.lookAt( this._tbCenter );
+		camera.lookAt( this.target );
 		camera.updateMatrix();
 
 		//setting state
@@ -2211,11 +2213,11 @@ class ArcballControls extends Object3D {
 		this._zoomState = this._zoom0;
 
 		this._initialNear = camera.near;
-		this._nearPos0 = camera.position.distanceTo( this._tbCenter ) - camera.near;
+		this._nearPos0 = camera.position.distanceTo( this.target ) - camera.near;
 		this._nearPos = this._initialNear;
 
 		this._initialFar = camera.far;
-		this._farPos0 = camera.position.distanceTo( this._tbCenter ) - camera.far;
+		this._farPos0 = camera.position.distanceTo( this.target ) - camera.far;
 		this._farPos = this._initialFar;
 
 		this._up0.copy( camera.up );
@@ -2226,7 +2228,7 @@ class ArcballControls extends Object3D {
 
 		//making gizmos
 		this._tbRadius = this.calculateTbRadius( camera );
-		this.makeGizmos( this._tbCenter, this._tbRadius );
+		this.makeGizmos( this.target, this._tbRadius );
 
 	};
 
@@ -2241,6 +2243,30 @@ class ArcballControls extends Object3D {
 
 	}
 
+	/**
+	 * Set gizmos radius factor and redraws gizmos
+	 * @param {Float} value Value of radius factor
+	 */
+	setTbRadius( value ) {
+
+		this.radiusFactor = value;
+		this._tbRadius = this.calculateTbRadius( this.camera );
+
+		const curve = new EllipseCurve( 0, 0, this._tbRadius, this._tbRadius );
+		const points = curve.getPoints( this._curvePts );
+		const curveGeometry = new BufferGeometry().setFromPoints( points );
+
+
+		for ( const gizmo in this._gizmos.children ) {
+
+			this._gizmos.children[ gizmo ].geometry = curveGeometry;
+
+		}
+
+		this.dispatchEvent( _changeEvent );
+
+	}
+
 	/**
 	 * Creates the rotation gizmos matching trackball center and radius
 	 * @param {Vector3} tbCenter The trackball center
@@ -2728,12 +2754,12 @@ class ArcballControls extends Object3D {
 	 */
 	setTarget = ( x, y, z ) => {
 
-		this._tbCenter.set( x, y, z );
+		this.target.set( x, y, z );
 		this._gizmos.position.set( x, y, z );	//for correct radius calculation
 		this._tbRadius = this.calculateTbRadius( this.camera );
 
-		this.makeGizmos( this._tbCenter, this._tbRadius );
-		this.camera.lookAt( this._tbCenter );
+		this.makeGizmos( this.target, this._tbRadius );
+		this.camera.lookAt( this.target );
 
 	};
 
@@ -2809,6 +2835,13 @@ class ArcballControls extends Object3D {
 	};
 
 
+	getRaycaster() {
+
+		return _raycaster;
+
+	}
+
+
 	/**
 	 * Unproject the cursor on the 3D object surface
 	 * @param {Vector2} cursor Cursor coordinates in NDC
@@ -2817,7 +2850,7 @@ class ArcballControls extends Object3D {
 	 */
 	unprojectOnObj = ( cursor, camera ) => {
 
-		const raycaster = new Raycaster();
+		const raycaster = this.getRaycaster();
 		raycaster.near = camera.near;
 		raycaster.far = camera.far;
 		raycaster.setFromCamera( cursor, camera );

+ 0 - 153
examples/jsm/controls/DeviceOrientationControls.js

@@ -1,153 +0,0 @@
-import {
-	Euler,
-	EventDispatcher,
-	MathUtils,
-	Quaternion,
-	Vector3
-} from '../../../build/three.module.js';
-
-const _zee = new Vector3( 0, 0, 1 );
-const _euler = new Euler();
-const _q0 = new Quaternion();
-const _q1 = new Quaternion( - Math.sqrt( 0.5 ), 0, 0, Math.sqrt( 0.5 ) ); // - PI/2 around the x-axis
-
-const _changeEvent = { type: 'change' };
-
-class DeviceOrientationControls extends EventDispatcher {
-
-	constructor( object ) {
-
-		super();
-
-		if ( window.isSecureContext === false ) {
-
-			console.error( 'THREE.DeviceOrientationControls: DeviceOrientationEvent is only available in secure contexts (https)' );
-
-		}
-
-		const scope = this;
-
-		const EPS = 0.000001;
-		const lastQuaternion = new Quaternion();
-
-		this.object = object;
-		this.object.rotation.reorder( 'YXZ' );
-
-		this.enabled = true;
-
-		this.deviceOrientation = {};
-		this.screenOrientation = 0;
-
-		this.alphaOffset = 0; // radians
-
-		const onDeviceOrientationChangeEvent = function ( event ) {
-
-			scope.deviceOrientation = event;
-
-		};
-
-		const onScreenOrientationChangeEvent = function () {
-
-			scope.screenOrientation = window.orientation || 0;
-
-		};
-
-		// The angles alpha, beta and gamma form a set of intrinsic Tait-Bryan angles of type Z-X'-Y''
-
-		const setObjectQuaternion = function ( quaternion, alpha, beta, gamma, orient ) {
-
-			_euler.set( beta, alpha, - gamma, 'YXZ' ); // 'ZXY' for the device, but 'YXZ' for us
-
-			quaternion.setFromEuler( _euler ); // orient the device
-
-			quaternion.multiply( _q1 ); // camera looks out the back of the device, not the top
-
-			quaternion.multiply( _q0.setFromAxisAngle( _zee, - orient ) ); // adjust for screen orientation
-
-		};
-
-		this.connect = function () {
-
-			onScreenOrientationChangeEvent(); // run once on load
-
-			// iOS 13+
-
-			if ( window.DeviceOrientationEvent !== undefined && typeof window.DeviceOrientationEvent.requestPermission === 'function' ) {
-
-				window.DeviceOrientationEvent.requestPermission().then( function ( response ) {
-
-					if ( response == 'granted' ) {
-
-						window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-						window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-
-					}
-
-				} ).catch( function ( error ) {
-
-					console.error( 'THREE.DeviceOrientationControls: Unable to use DeviceOrientation API:', error );
-
-				} );
-
-			} else {
-
-				window.addEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-				window.addEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-
-			}
-
-			scope.enabled = true;
-
-		};
-
-		this.disconnect = function () {
-
-			window.removeEventListener( 'orientationchange', onScreenOrientationChangeEvent );
-			window.removeEventListener( 'deviceorientation', onDeviceOrientationChangeEvent );
-
-			scope.enabled = false;
-
-		};
-
-		this.update = function () {
-
-			if ( scope.enabled === false ) return;
-
-			const device = scope.deviceOrientation;
-
-			if ( device ) {
-
-				const alpha = device.alpha ? MathUtils.degToRad( device.alpha ) + scope.alphaOffset : 0; // Z
-
-				const beta = device.beta ? MathUtils.degToRad( device.beta ) : 0; // X'
-
-				const gamma = device.gamma ? MathUtils.degToRad( device.gamma ) : 0; // Y''
-
-				const orient = scope.screenOrientation ? MathUtils.degToRad( scope.screenOrientation ) : 0; // O
-
-				setObjectQuaternion( scope.object.quaternion, alpha, beta, gamma, orient );
-
-				if ( 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
-
-					lastQuaternion.copy( scope.object.quaternion );
-					scope.dispatchEvent( _changeEvent );
-
-				}
-
-			}
-
-		};
-
-		this.dispose = function () {
-
-			scope.disconnect();
-
-		};
-
-		this.connect();
-
-	}
-
-}
-
-export { DeviceOrientationControls };

+ 1 - 1
examples/jsm/controls/OrbitControls.js

@@ -1026,7 +1026,7 @@ class OrbitControls extends EventDispatcher {
 
 		function onMouseWheel( event ) {
 
-			if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
+			if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
 
 			event.preventDefault();
 

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

@@ -208,7 +208,7 @@ class TrackballControls extends EventDispatcher {
 
 				} else if ( scope.object.isOrthographicCamera ) {
 
-					scope.object.zoom *= factor;
+					scope.object.zoom /= factor;
 					scope.object.updateProjectionMatrix();
 
 				} else {

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

@@ -2303,7 +2303,7 @@ class GLTFMaterialsVolumeExtension {
 		}
 
 		extensionDef.attenuationDistance = material.attenuationDistance;
-		extensionDef.attenuationColor = material.attenuationTint.toArray();
+		extensionDef.attenuationColor = material.attenuationColor.toArray();
 
 		materialDef.extensions = materialDef.extensions || {};
 		materialDef.extensions[ this.name ] = extensionDef;

+ 27 - 14
examples/jsm/exporters/USDZExporter.js

@@ -247,7 +247,7 @@ function buildMesh( geometry ) {
 
 function buildMeshVertexCount( geometry ) {
 
-	const count = geometry.index !== null ? geometry.index.array.length : geometry.attributes.position.count;
+	const count = geometry.index !== null ? geometry.index.count : geometry.attributes.position.count;
 
 	return Array( count / 3 ).fill( 3 ).join( ', ' );
 
@@ -255,18 +255,26 @@ function buildMeshVertexCount( geometry ) {
 
 function buildMeshVertexIndices( geometry ) {
 
-	if ( geometry.index !== null ) {
+	const index = geometry.index;
+	const array = [];
 
-		return geometry.index.array.join( ', ' );
+	if ( index !== null ) {
 
-	}
+		for ( let i = 0; i < index.count; i ++ ) {
 
-	const array = [];
-	const length = geometry.attributes.position.count;
+			array.push( index.getX( i ) );
+
+		}
+
+	} else {
+
+		const length = geometry.attributes.position.count;
+
+		for ( let i = 0; i < length; i ++ ) {
 
-	for ( let i = 0; i < length; i ++ ) {
+			array.push( i );
 
-		array.push( i );
+		}
 
 	}
 
@@ -284,11 +292,14 @@ function buildVector3Array( attribute, count ) {
 	}
 
 	const array = [];
-	const data = attribute.array;
 
-	for ( let i = 0; i < data.length; i += 3 ) {
+	for ( let i = 0; i < attribute.count; i ++ ) {
+
+		const x = attribute.getX( i );
+		const y = attribute.getY( i );
+		const z = attribute.getZ( i );
 
-		array.push( `(${ data[ i + 0 ].toPrecision( PRECISION ) }, ${ data[ i + 1 ].toPrecision( PRECISION ) }, ${ data[ i + 2 ].toPrecision( PRECISION ) })` );
+		array.push( `(${ x.toPrecision( PRECISION ) }, ${ y.toPrecision( PRECISION ) }, ${ z.toPrecision( PRECISION ) })` );
 
 	}
 
@@ -306,11 +317,13 @@ function buildVector2Array( attribute, count ) {
 	}
 
 	const array = [];
-	const data = attribute.array;
 
-	for ( let i = 0; i < data.length; i += 2 ) {
+	for ( let i = 0; i < attribute.count; i ++ ) {
+
+		const x = attribute.getX( i );
+		const y = attribute.getY( i );
 
-		array.push( `(${ data[ i + 0 ].toPrecision( PRECISION ) }, ${ 1 - data[ i + 1 ].toPrecision( PRECISION ) })` );
+		array.push( `(${ x.toPrecision( PRECISION ) }, ${ 1 - y.toPrecision( PRECISION ) })` );
 
 	}
 

+ 0 - 78
examples/jsm/libs/glslang.js

@@ -1,78 +0,0 @@
-// 0.0.15
-var Module = (function() {
-  var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
-
-  return (
-function(Module) {
-  Module = Module || {};
-
-var c;c||(c=typeof Module !== 'undefined' ? Module : {});
-c.compileGLSLZeroCopy=function(a,b,d,e){d=!!d;switch(b){case "vertex":var g=0;break;case "fragment":g=4;break;case "compute":g=5;break;default:throw Error("shader_stage must be 'vertex', 'fragment', or 'compute'.");}switch(e||"1.0"){case "1.0":var f=65536;break;case "1.1":f=65792;break;case "1.2":f=66048;break;case "1.3":f=66304;break;case "1.4":f=66560;break;case "1.5":f=66816;break;default:throw Error("spirv_version must be '1.0' ~ '1.5'.");}e=c._malloc(4);b=c._malloc(4);var h=aa([a,g,d,f,e,b]);
-d=k(e);a=k(b);c._free(e);c._free(b);if(0===h)throw Error("GLSL compilation failed");e={};d/=4;e.data=c.HEAPU32.subarray(d,d+a);e.free=function(){c._destroy_output_buffer(h)};return e};c.compileGLSL=function(a,b,d,e){a=c.compileGLSLZeroCopy(a,b,d,e);b=a.data.slice();a.free();return b};var p={},q;for(q in c)c.hasOwnProperty(q)&&(p[q]=c[q]);var r="./this.program",t=!1,u=!1;t="object"===typeof window;u="function"===typeof importScripts;var v="",w;
-if(t||u)u?v=self.location.href:document.currentScript&&(v=document.currentScript.src),_scriptDir&&(v=_scriptDir),0!==v.indexOf("blob:")?v=v.substr(0,v.lastIndexOf("/")+1):v="",u&&(w=function(a){var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)});var x=c.print||console.log.bind(console),y=c.printErr||console.warn.bind(console);for(q in p)p.hasOwnProperty(q)&&(c[q]=p[q]);p=null;c.thisProgram&&(r=c.thisProgram);var A;
-c.wasmBinary&&(A=c.wasmBinary);"object"!==typeof WebAssembly&&y("no native wasm support detected");function k(a){var b="i32";"*"===b.charAt(b.length-1)&&(b="i32");switch(b){case "i1":return B[a>>0];case "i8":return B[a>>0];case "i16":return ba[a>>1];case "i32":return C[a>>2];case "i64":return C[a>>2];case "float":return ca[a>>2];case "double":return da[a>>3];default:D("invalid type for getValue: "+b)}return null}var E,ea=new WebAssembly.Table({initial:859,maximum:859,element:"anyfunc"}),fa=!1;
-function ha(){var a=c._convert_glsl_to_spirv;a||D("Assertion failed: Cannot call unknown function convert_glsl_to_spirv, make sure it is exported");return a}
-function aa(a){var b="string number boolean number number number".split(" "),d={string:function(a){var b=0;if(null!==a&&void 0!==a&&0!==a){var d=(a.length<<2)+1;b=G(d);ia(a,H,b,d)}return b},array:function(a){var b=G(a.length);B.set(a,b);return b}},e=ha(),g=[],f=0;if(a)for(var h=0;h<a.length;h++){var n=d[b[h]];n?(0===f&&(f=ja()),g[h]=n(a[h])):g[h]=a[h]}a=e.apply(null,g);0!==f&&ka(f);return a}var la="undefined"!==typeof TextDecoder?new TextDecoder("utf8"):void 0;
-function I(a,b,d){var e=b+d;for(d=b;a[d]&&!(d>=e);)++d;if(16<d-b&&a.subarray&&la)return la.decode(a.subarray(b,d));for(e="";b<d;){var g=a[b++];if(g&128){var f=a[b++]&63;if(192==(g&224))e+=String.fromCharCode((g&31)<<6|f);else{var h=a[b++]&63;g=224==(g&240)?(g&15)<<12|f<<6|h:(g&7)<<18|f<<12|h<<6|a[b++]&63;65536>g?e+=String.fromCharCode(g):(g-=65536,e+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else e+=String.fromCharCode(g)}return e}
-function ia(a,b,d,e){if(0<e){e=d+e-1;for(var g=0;g<a.length;++g){var f=a.charCodeAt(g);if(55296<=f&&57343>=f){var h=a.charCodeAt(++g);f=65536+((f&1023)<<10)|h&1023}if(127>=f){if(d>=e)break;b[d++]=f}else{if(2047>=f){if(d+1>=e)break;b[d++]=192|f>>6}else{if(65535>=f){if(d+2>=e)break;b[d++]=224|f>>12}else{if(d+3>=e)break;b[d++]=240|f>>18;b[d++]=128|f>>12&63}b[d++]=128|f>>6&63}b[d++]=128|f&63}}b[d]=0}}"undefined"!==typeof TextDecoder&&new TextDecoder("utf-16le");var J,B,H,ba,C,ca,da;
-function ma(a){J=a;c.HEAP8=B=new Int8Array(a);c.HEAP16=ba=new Int16Array(a);c.HEAP32=C=new Int32Array(a);c.HEAPU8=H=new Uint8Array(a);c.HEAPU16=new Uint16Array(a);c.HEAPU32=new Uint32Array(a);c.HEAPF32=ca=new Float32Array(a);c.HEAPF64=da=new Float64Array(a)}var na=c.TOTAL_MEMORY||16777216;c.wasmMemory?E=c.wasmMemory:E=new WebAssembly.Memory({initial:na/65536});E&&(J=E.buffer);na=J.byteLength;ma(J);C[84916]=5582704;
-function K(a){for(;0<a.length;){var b=a.shift();if("function"==typeof b)b();else{var d=b.J;"number"===typeof d?void 0===b.H?c.dynCall_v(d):c.dynCall_vi(d,b.H):d(void 0===b.H?null:b.H)}}}var oa=[],pa=[],qa=[],ra=[];function sa(){var a=c.preRun.shift();oa.unshift(a)}var L=0,M=null,N=null;c.preloadedImages={};c.preloadedAudios={};function D(a){if(c.onAbort)c.onAbort(a);x(a);y(a);fa=!0;throw new WebAssembly.RuntimeError("abort("+a+"). Build with -s ASSERTIONS=1 for more info.");}
-function ta(){var a=O;return String.prototype.startsWith?a.startsWith("data:application/octet-stream;base64,"):0===a.indexOf("data:application/octet-stream;base64,")}var O="glslang.wasm";if(!ta()){var ua=O;O=c.locateFile?c.locateFile(ua,v):v+ua}function wa(){try{if(A)return new Uint8Array(A);if(w)return w(O);throw"both async and sync fetching of the wasm failed";}catch(a){D(a)}}
-function xa(){return A||!t&&!u||"function"!==typeof fetch?new Promise(function(a){a(wa())}):fetch(O,{credentials:"same-origin"}).then(function(a){if(!a.ok)throw"failed to load wasm binary file at '"+O+"'";return a.arrayBuffer()}).catch(function(){return wa()})}pa.push({J:function(){ya()}});var za=[null,[],[]],P=0;function Aa(){P+=4;return C[P-4>>2]}var Q={},Ba={};
-function Ca(){if(!R){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"===typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:r},b;for(b in Ba)a[b]=Ba[b];var d=[];for(b in a)d.push(b+"="+a[b]);R=d}return R}var R;function S(a){return 0===a%4&&(0!==a%100||0===a%400)}function T(a,b){for(var d=0,e=0;e<=b;d+=a[e++]);return d}var U=[31,29,31,30,31,30,31,31,30,31,30,31],W=[31,28,31,30,31,30,31,31,30,31,30,31];
-function X(a,b){for(a=new Date(a.getTime());0<b;){var d=a.getMonth(),e=(S(a.getFullYear())?U:W)[d];if(b>e-a.getDate())b-=e-a.getDate()+1,a.setDate(1),11>d?a.setMonth(d+1):(a.setMonth(0),a.setFullYear(a.getFullYear()+1));else{a.setDate(a.getDate()+b);break}}return a}
-function Da(a,b,d,e){function g(a,b,d){for(a="number"===typeof a?a.toString():a||"";a.length<b;)a=d[0]+a;return a}function f(a,b){return g(a,b,"0")}function h(a,b){function V(a){return 0>a?-1:0<a?1:0}var d;0===(d=V(a.getFullYear()-b.getFullYear()))&&0===(d=V(a.getMonth()-b.getMonth()))&&(d=V(a.getDate()-b.getDate()));return d}function n(a){switch(a.getDay()){case 0:return new Date(a.getFullYear()-1,11,29);case 1:return a;case 2:return new Date(a.getFullYear(),0,3);case 3:return new Date(a.getFullYear(),
-0,2);case 4:return new Date(a.getFullYear(),0,1);case 5:return new Date(a.getFullYear()-1,11,31);case 6:return new Date(a.getFullYear()-1,11,30)}}function z(a){a=X(new Date(a.A+1900,0,1),a.G);var b=n(new Date(a.getFullYear()+1,0,4));return 0>=h(n(new Date(a.getFullYear(),0,4)),a)?0>=h(b,a)?a.getFullYear()+1:a.getFullYear():a.getFullYear()-1}var m=C[e+40>>2];e={N:C[e>>2],M:C[e+4>>2],D:C[e+8>>2],C:C[e+12>>2],B:C[e+16>>2],A:C[e+20>>2],F:C[e+24>>2],G:C[e+28>>2],X:C[e+32>>2],L:C[e+36>>2],O:m?m?I(H,m,void 0):
-"":""};d=d?I(H,d,void 0):"";m={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var l in m)d=d.replace(new RegExp(l,"g"),m[l]);var F="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),
-va="January February March April May June July August September October November December".split(" ");m={"%a":function(a){return F[a.F].substring(0,3)},"%A":function(a){return F[a.F]},"%b":function(a){return va[a.B].substring(0,3)},"%B":function(a){return va[a.B]},"%C":function(a){return f((a.A+1900)/100|0,2)},"%d":function(a){return f(a.C,2)},"%e":function(a){return g(a.C,2," ")},"%g":function(a){return z(a).toString().substring(2)},"%G":function(a){return z(a)},"%H":function(a){return f(a.D,2)},
-"%I":function(a){a=a.D;0==a?a=12:12<a&&(a-=12);return f(a,2)},"%j":function(a){return f(a.C+T(S(a.A+1900)?U:W,a.B-1),3)},"%m":function(a){return f(a.B+1,2)},"%M":function(a){return f(a.M,2)},"%n":function(){return"\n"},"%p":function(a){return 0<=a.D&&12>a.D?"AM":"PM"},"%S":function(a){return f(a.N,2)},"%t":function(){return"\t"},"%u":function(a){return a.F||7},"%U":function(a){var b=new Date(a.A+1900,0,1),d=0===b.getDay()?b:X(b,7-b.getDay());a=new Date(a.A+1900,a.B,a.C);return 0>h(d,a)?f(Math.ceil((31-
-d.getDate()+(T(S(a.getFullYear())?U:W,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(d,b)?"01":"00"},"%V":function(a){var b=n(new Date(a.A+1900,0,4)),d=n(new Date(a.A+1901,0,4)),e=X(new Date(a.A+1900,0,1),a.G);return 0>h(e,b)?"53":0>=h(d,e)?"01":f(Math.ceil((b.getFullYear()<a.A+1900?a.G+32-b.getDate():a.G+1-b.getDate())/7),2)},"%w":function(a){return a.F},"%W":function(a){var b=new Date(a.A,0,1),d=1===b.getDay()?b:X(b,0===b.getDay()?1:7-b.getDay()+1);a=new Date(a.A+1900,a.B,a.C);return 0>h(d,a)?f(Math.ceil((31-
-d.getDate()+(T(S(a.getFullYear())?U:W,a.getMonth()-1)-31)+a.getDate())/7),2):0===h(d,b)?"01":"00"},"%y":function(a){return(a.A+1900).toString().substring(2)},"%Y":function(a){return a.A+1900},"%z":function(a){a=a.L;var b=0<=a;a=Math.abs(a)/60;return(b?"+":"-")+String("0000"+(a/60*100+a%60)).slice(-4)},"%Z":function(a){return a.O},"%%":function(){return"%"}};for(l in m)0<=d.indexOf(l)&&(d=d.replace(new RegExp(l,"g"),m[l](e)));l=Ea(d);if(l.length>b)return 0;B.set(l,a);return l.length-1}
-function Ea(a){for(var b=0,d=0;d<a.length;++d){var e=a.charCodeAt(d);55296<=e&&57343>=e&&(e=65536+((e&1023)<<10)|a.charCodeAt(++d)&1023);127>=e?++b:b=2047>=e?b+2:65535>=e?b+3:b+4}b=Array(b+1);ia(a,b,0,b.length);return b}
-var Ga={f:function(){},c:function(){c.___errno_location&&(C[c.___errno_location()>>2]=63);return-1},n:function(a,b){P=b;try{var d=Aa();var e=Aa();if(-1===d||0===e)var g=-28;else{var f=Q.K[d];if(f&&e===f.U){var h=(void 0).T(f.S);Q.R(d,h,e,f.flags,f.offset);(void 0).W(h);Q.K[d]=null;f.P&&Fa(f.V)}g=0}return g}catch(n){return D(n),-n.I}},a:function(){},b:function(){D()},k:function(a,b,d){H.set(H.subarray(b,b+d),a)},l:function(a){var b=B.length;if(2147418112<a)return!1;for(var d=1;4>=d;d*=2){var e=b*(1+
-.2/d);e=Math.min(e,a+100663296);e=Math.max(16777216,a,e);0<e%65536&&(e+=65536-e%65536);a:{try{E.grow(Math.min(2147418112,e)-J.byteLength+65535>>16);ma(E.buffer);var g=1;break a}catch(f){}g=void 0}if(g)return!0}return!1},d:function(a,b){var d=0;Ca().forEach(function(e,g){var f=b+d;g=C[a+4*g>>2]=f;for(f=0;f<e.length;++f)B[g++>>0]=e.charCodeAt(f);B[g>>0]=0;d+=e.length+1});return 0},e:function(a,b){var d=Ca();C[a>>2]=d.length;var e=0;d.forEach(function(a){e+=a.length+1});C[b>>2]=e;return 0},h:function(){return 0},
-j:function(){return 0},g:function(a,b,d,e){try{for(var g=0,f=0;f<d;f++){for(var h=C[b+8*f>>2],n=C[b+(8*f+4)>>2],z=0;z<n;z++){var m=H[h+z],l=za[a];0===m||10===m?((1===a?x:y)(I(l,0)),l.length=0):l.push(m)}g+=n}C[e>>2]=g;return 0}catch(F){return D(F),F.I}},memory:E,o:function(){},i:function(){},m:function(a,b,d,e){return Da(a,b,d,e)},table:ea},Ha=function(){function a(a){c.asm=a.exports;L--;c.monitorRunDependencies&&c.monitorRunDependencies(L);0==L&&(null!==M&&(clearInterval(M),M=null),N&&(a=N,N=null,
-a()))}function b(b){a(b.instance)}function d(a){return xa().then(function(a){return WebAssembly.instantiate(a,e)}).then(a,function(a){y("failed to asynchronously prepare wasm: "+a);D(a)})}var e={env:Ga,wasi_snapshot_preview1:Ga};L++;c.monitorRunDependencies&&c.monitorRunDependencies(L);if(c.instantiateWasm)try{return c.instantiateWasm(e,a)}catch(g){return y("Module.instantiateWasm callback failed with error: "+g),!1}(function(){if(A||"function"!==typeof WebAssembly.instantiateStreaming||ta()||"function"!==
-typeof fetch)return d(b);fetch(O,{credentials:"same-origin"}).then(function(a){return WebAssembly.instantiateStreaming(a,e).then(b,function(a){y("wasm streaming compile failed: "+a);y("falling back to ArrayBuffer instantiation");d(b)})})})();return{}}();c.asm=Ha;var ya=c.___wasm_call_ctors=function(){return(ya=c.___wasm_call_ctors=c.asm.p).apply(null,arguments)};c._convert_glsl_to_spirv=function(){return(c._convert_glsl_to_spirv=c.asm.q).apply(null,arguments)};
-c._destroy_output_buffer=function(){return(c._destroy_output_buffer=c.asm.r).apply(null,arguments)};c._malloc=function(){return(c._malloc=c.asm.s).apply(null,arguments)};var Fa=c._free=function(){return(Fa=c._free=c.asm.t).apply(null,arguments)},ja=c.stackSave=function(){return(ja=c.stackSave=c.asm.u).apply(null,arguments)},G=c.stackAlloc=function(){return(G=c.stackAlloc=c.asm.v).apply(null,arguments)},ka=c.stackRestore=function(){return(ka=c.stackRestore=c.asm.w).apply(null,arguments)};
-c.dynCall_vi=function(){return(c.dynCall_vi=c.asm.x).apply(null,arguments)};c.dynCall_v=function(){return(c.dynCall_v=c.asm.y).apply(null,arguments)};c.asm=Ha;var Y;c.then=function(a){if(Y)a(c);else{var b=c.onRuntimeInitialized;c.onRuntimeInitialized=function(){b&&b();a(c)}}return c};N=function Ia(){Y||Z();Y||(N=Ia)};
-function Z(){function a(){if(!Y&&(Y=!0,!fa)){K(pa);K(qa);if(c.onRuntimeInitialized)c.onRuntimeInitialized();if(c.postRun)for("function"==typeof c.postRun&&(c.postRun=[c.postRun]);c.postRun.length;){var a=c.postRun.shift();ra.unshift(a)}K(ra)}}if(!(0<L)){if(c.preRun)for("function"==typeof c.preRun&&(c.preRun=[c.preRun]);c.preRun.length;)sa();K(oa);0<L||(c.setStatus?(c.setStatus("Running..."),setTimeout(function(){setTimeout(function(){c.setStatus("")},1);a()},1)):a())}}c.run=Z;
-if(c.preInit)for("function"==typeof c.preInit&&(c.preInit=[c.preInit]);0<c.preInit.length;)c.preInit.pop()();Z();
-
-
-  return Module
-}
-);
-})();
-if (typeof exports === 'object' && typeof module === 'object')
-      module.exports = Module;
-    else if (typeof define === 'function' && define['amd'])
-      define([], function() { return Module; });
-    else if (typeof exports === 'object')
-      exports["Module"] = Module;
-    export default (() => {
-    const initialize = () => {
-        return new Promise(resolve => {
-            Module({
-                locateFile() {
-                    const i = import.meta.url.lastIndexOf('/')
-                    return import.meta.url.substring(0, i) + '/glslang.wasm';
-                },
-                onRuntimeInitialized() {
-                    resolve({
-                        compileGLSLZeroCopy: this.compileGLSLZeroCopy,
-                        compileGLSL: this.compileGLSL,
-                    });
-                },
-            });
-        });
-    };
-
-    let instance;
-    return () => {
-        if (!instance) {
-            instance = initialize();
-        }
-        return instance;
-    };
-})();

BIN
examples/jsm/libs/glslang.wasm


+ 52 - 19
examples/jsm/lines/LineMaterial.js

@@ -55,10 +55,23 @@ ShaderLib[ 'line' ] = {
 		attribute vec3 instanceColorStart;
 		attribute vec3 instanceColorEnd;
 
-		varying vec2 vUv;
-		varying vec4 worldPos;
-		varying vec3 worldStart;
-		varying vec3 worldEnd;
+		#ifdef WORLD_UNITS
+
+			varying vec4 worldPos;
+			varying vec3 worldStart;
+			varying vec3 worldEnd;
+
+			#ifdef USE_DASH
+
+				varying vec2 vUv;
+
+			#endif
+
+		#else
+
+			varying vec2 vUv;
+
+		#endif
 
 		#ifdef USE_DASH
 
@@ -95,19 +108,26 @@ ShaderLib[ 'line' ] = {
 			#ifdef USE_DASH
 
 				vLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;
+				vUv = uv;
 
 			#endif
 
 			float aspect = resolution.x / resolution.y;
 
-			vUv = uv;
-
 			// camera space
 			vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );
 			vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );
 
-			worldStart = start.xyz;
-			worldEnd = end.xyz;
+			#ifdef WORLD_UNITS
+
+				worldStart = start.xyz;
+				worldEnd = end.xyz;
+
+			#else
+
+				vUv = uv;
+
+			#endif
 
 			// special case for perspective projection, and segments that terminate either in, or behind, the camera plane
 			// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space
@@ -263,9 +283,24 @@ ShaderLib[ 'line' ] = {
 		#endif
 
 		varying float vLineDistance;
-		varying vec4 worldPos;
-		varying vec3 worldStart;
-		varying vec3 worldEnd;
+
+		#ifdef WORLD_UNITS
+
+			varying vec4 worldPos;
+			varying vec3 worldStart;
+			varying vec3 worldEnd;
+
+			#ifdef USE_DASH
+
+				varying vec2 vUv;
+
+			#endif
+
+		#else
+
+			varying vec2 vUv;
+
+		#endif
 
 		#include <common>
 		#include <color_pars_fragment>
@@ -273,8 +308,6 @@ ShaderLib[ 'line' ] = {
 		#include <logdepthbuf_pars_fragment>
 		#include <clipping_planes_pars_fragment>
 
-		varying vec2 vUv;
-
 		vec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {
 
 			float mua;
@@ -333,7 +366,7 @@ ShaderLib[ 'line' ] = {
 
 				#ifndef USE_DASH
 
-					#ifdef ALPHA_TO_COVERAGE
+					#ifdef USE_ALPHA_TO_COVERAGE
 
 						float dnorm = fwidth( norm );
 						alpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );
@@ -352,7 +385,7 @@ ShaderLib[ 'line' ] = {
 
 			#else
 
-				#ifdef ALPHA_TO_COVERAGE
+				#ifdef USE_ALPHA_TO_COVERAGE
 
 					// artifacts appear on some hardware if a derivative is taken within a conditional
 					float a = vUv.x;
@@ -625,13 +658,13 @@ class LineMaterial extends ShaderMaterial {
 
 				get: function () {
 
-					return Boolean( 'ALPHA_TO_COVERAGE' in this.defines );
+					return Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines );
 
 				},
 
 				set: function ( value ) {
 
-					if ( Boolean( value ) !== Boolean( 'ALPHA_TO_COVERAGE' in this.defines ) ) {
+					if ( Boolean( value ) !== Boolean( 'USE_ALPHA_TO_COVERAGE' in this.defines ) ) {
 
 						this.needsUpdate = true;
 
@@ -639,12 +672,12 @@ class LineMaterial extends ShaderMaterial {
 
 					if ( value === true ) {
 
-						this.defines.ALPHA_TO_COVERAGE = '';
+						this.defines.USE_ALPHA_TO_COVERAGE = '';
 						this.extensions.derivatives = true;
 
 					} else {
 
-						delete this.defines.ALPHA_TO_COVERAGE;
+						delete this.defines.USE_ALPHA_TO_COVERAGE;
 						this.extensions.derivatives = false;
 
 					}

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

@@ -1431,7 +1431,7 @@ class ThreeMFLoader extends Loader {
 			for ( let i = 0; i < buildData.length; i ++ ) {
 
 				const buildItem = buildData[ i ];
-				const object3D = objects[ buildItem[ 'objectId' ] ];
+				const object3D = objects[ buildItem[ 'objectId' ] ].clone();
 
 				// apply transform
 

+ 61 - 3
examples/jsm/loaders/ColladaLoader.js

@@ -10,6 +10,7 @@ import {
 	Euler,
 	FileLoader,
 	Float32BufferAttribute,
+	FrontSide,
 	Group,
 	Line,
 	LineBasicMaterial,
@@ -33,6 +34,7 @@ import {
 	SkinnedMesh,
 	SpotLight,
 	TextureLoader,
+	Vector2,
 	Vector3,
 	VectorKeyframeTrack
 } from '../../../build/three.module.js';
@@ -1239,6 +1241,10 @@ class ColladaLoader extends Loader {
 						data.parameters = parseEffectParameters( child );
 						break;
 
+					case 'extra':
+						data.extra = parseEffectExtra( child );
+						break;
+
 				}
 
 			}
@@ -1399,6 +1405,10 @@ class ColladaLoader extends Loader {
 
 						break;
 
+					case 'bump':
+						data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
+						break;
+
 				}
 
 			}
@@ -1445,6 +1455,34 @@ class ColladaLoader extends Loader {
 						data[ child.nodeName ] = parseInt( child.textContent );
 						break;
 
+					case 'bump':
+						data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
+						break;
+
+				}
+
+			}
+
+			return data;
+
+		}
+
+		function parseEffectExtraTechniqueBump( xml ) {
+
+			var data = {};
+
+			for ( var i = 0, l = xml.childNodes.length; i < l; i ++ ) {
+
+				var child = xml.childNodes[ i ];
+
+				if ( child.nodeType !== 1 ) continue;
+
+				switch ( child.nodeName ) {
+
+					case 'texture':
+						data[ child.nodeName ] = { id: child.getAttribute( 'texture' ), texcoord: child.getAttribute( 'texcoord' ), extra: parseEffectParameterTexture( child ) };
+						break;
+
 				}
 
 			}
@@ -1519,7 +1557,6 @@ class ColladaLoader extends Loader {
 
 			const effect = getEffect( data.url );
 			const technique = effect.profile.technique;
-			const extra = effect.profile.extra;
 
 			let material;
 
@@ -1700,6 +1737,7 @@ class ColladaLoader extends Loader {
 							material.opacity = color[ 0 ] * transparency.float;
 							break;
 						default:
+							material.opacity = 1 - transparency.float;
 							console.warn( 'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque );
 
 					}
@@ -1712,9 +1750,29 @@ class ColladaLoader extends Loader {
 
 			//
 
-			if ( extra !== undefined && extra.technique !== undefined && extra.technique.double_sided === 1 ) {
 
-				material.side = DoubleSide;
+			if ( technique.extra !== undefined && technique.extra.technique !== undefined ) {
+
+				const techniques = technique.extra.technique;
+
+				for ( const k in techniques ) {
+
+					const v = techniques[ k ];
+
+					switch ( k ) {
+
+						case 'double_sided':
+							material.side = ( v === 1 ? DoubleSide : FrontSide );
+							break;
+
+						case 'bump':
+							material.normalMap = getTexture( v.texture );
+							material.normalScale = new Vector2( 1, 1 );
+							break;
+
+					}
+
+				}
 
 			}
 

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

@@ -1421,7 +1421,7 @@ class FBXTreeParser {
 
 			for ( const nodeID in BindPoseNode ) {
 
-				if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) {
+				if ( BindPoseNode[ nodeID ].attrType === 'BindPose' && BindPoseNode[ nodeID ].NbPoseNodes > 0 ) {
 
 					const poseNodes = BindPoseNode[ nodeID ].PoseNode;
 

+ 86 - 33
examples/jsm/loaders/GLTFLoader.js

@@ -94,6 +94,12 @@ class GLTFLoader extends Loader {
 
 		} );
 
+		this.register( function ( parser ) {
+
+			return new GLTFMaterialsSheenExtension( parser );
+
+		} );
+
 		this.register( function ( parser ) {
 
 			return new GLTFMaterialsTransmissionExtension( parser );
@@ -428,6 +434,7 @@ const EXTENSIONS = {
 	KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 	KHR_MATERIALS_IOR: 'KHR_materials_ior',
 	KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
+	KHR_MATERIALS_SHEEN: 'KHR_materials_sheen',
 	KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 	KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 	KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
@@ -704,6 +711,80 @@ class GLTFMaterialsClearcoatExtension {
 
 }
 
+/**
+ * Sheen Materials Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_sheen
+ */
+class GLTFMaterialsSheenExtension {
+
+	constructor( parser ) {
+
+		this.parser = parser;
+		this.name = EXTENSIONS.KHR_MATERIALS_SHEEN;
+
+	}
+
+	getMaterialType( materialIndex ) {
+
+		const parser = this.parser;
+		const materialDef = parser.json.materials[ materialIndex ];
+
+		if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) return null;
+
+		return MeshPhysicalMaterial;
+
+	}
+
+	extendMaterialParams( materialIndex, materialParams ) {
+
+		const parser = this.parser;
+		const materialDef = parser.json.materials[ materialIndex ];
+
+		if ( ! materialDef.extensions || ! materialDef.extensions[ this.name ] ) {
+
+			return Promise.resolve();
+
+		}
+
+		const pending = [];
+
+		materialParams.sheenColor = new Color( 0, 0, 0 );
+		materialParams.sheenRoughness = 0;
+		materialParams.sheen = 1;
+
+		const extension = materialDef.extensions[ this.name ];
+
+		if ( extension.sheenColorFactor !== undefined ) {
+
+			materialParams.sheenColor.fromArray( extension.sheenColorFactor );
+
+		}
+
+		if ( extension.sheenRoughnessFactor !== undefined ) {
+
+			materialParams.sheenRoughness = extension.sheenRoughnessFactor;
+
+		}
+
+		if ( extension.sheenColorTexture !== undefined ) {
+
+			pending.push( parser.assignTexture( materialParams, 'sheenColorMap', extension.sheenColorTexture ) );
+
+		}
+
+		if ( extension.sheenRoughnessTexture !== undefined ) {
+
+			pending.push( parser.assignTexture( materialParams, 'sheenRoughnessMap', extension.sheenRoughnessTexture ) );
+
+		}
+
+		return Promise.all( pending );
+
+	}
+
+}
+
 /**
  * Transmission Materials Extension
  *
@@ -814,7 +895,7 @@ class GLTFMaterialsVolumeExtension {
 		materialParams.attenuationDistance = extension.attenuationDistance || 0;
 
 		const colorArray = extension.attenuationColor || [ 1, 1, 1 ];
-		materialParams.attenuationTint = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
+		materialParams.attenuationColor = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
 
 		return Promise.all( pending );
 
@@ -917,11 +998,11 @@ class GLTFMaterialsSpecularExtension {
 		}
 
 		const colorArray = extension.specularColorFactor || [ 1, 1, 1 ];
-		materialParams.specularTint = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
+		materialParams.specularColor = new Color( colorArray[ 0 ], colorArray[ 1 ], colorArray[ 2 ] );
 
 		if ( extension.specularColorTexture !== undefined ) {
 
-			pending.push( parser.assignTexture( materialParams, 'specularTintMap', extension.specularColorTexture ).then( function ( texture ) {
+			pending.push( parser.assignTexture( materialParams, 'specularColorMap', extension.specularColorTexture ).then( function ( texture ) {
 
 				texture.encoding = sRGBEncoding;
 
@@ -1887,34 +1968,6 @@ const ALPHA_MODES = {
 	BLEND: 'BLEND'
 };
 
-/* UTILITY FUNCTIONS */
-
-function resolveURL( url, path ) {
-
-	// Invalid URL
-	if ( typeof url !== 'string' || url === '' ) return '';
-
-	// Host Relative URL
-	if ( /^https?:\/\//i.test( path ) && /^\//.test( url ) ) {
-
-		path = path.replace( /(^https?:\/\/[^\/]+).*/i, '$1' );
-
-	}
-
-	// Absolute URL http://,https://,//
-	if ( /^(https?:)?\/\//i.test( url ) ) return url;
-
-	// Data URI
-	if ( /^data:.*,.*$/i.test( url ) ) return url;
-
-	// Blob URL
-	if ( /^blob:.*$/i.test( url ) ) return url;
-
-	// Relative URL
-	return path + url;
-
-}
-
 /**
  * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
  */
@@ -2574,7 +2627,7 @@ class GLTFParser {
 
 		return new Promise( function ( resolve, reject ) {
 
-			loader.load( resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () {
+			loader.load( LoaderUtils.resolveURL( bufferDef.uri, options.path ), resolve, undefined, function () {
 
 				reject( new Error( 'THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".' ) );
 
@@ -2820,7 +2873,7 @@ class GLTFParser {
 
 				}
 
-				loader.load( resolveURL( sourceURI, options.path ), onLoad, undefined, reject );
+				loader.load( LoaderUtils.resolveURL( sourceURI, options.path ), onLoad, undefined, reject );
 
 			} );
 

+ 19 - 0
examples/jsm/loaders/KTX2Loader.js

@@ -37,6 +37,8 @@ const KTX2TransferSRGB = 2;
 const KTX2_ALPHA_PREMULTIPLIED = 1;
 const _taskCache = new WeakMap();
 
+let _activeLoaders = 0;
+
 class KTX2Loader extends Loader {
 
 	constructor( manager ) {
@@ -154,6 +156,21 @@ class KTX2Loader extends Loader {
 
 				} );
 
+			if ( _activeLoaders > 0 ) {
+
+				// Each instance loads a transcoder and allocates workers, increasing network and memory cost.
+
+				console.warn(
+
+					'THREE.KTX2Loader: Multiple active KTX2 loaders may cause performance issues.'
+					+ ' Use a single KTX2Loader instance, or call .dispose() on old instances.'
+
+				);
+
+			}
+
+			_activeLoaders++;
+
 		}
 
 		return this.transcoderPending;
@@ -248,6 +265,8 @@ class KTX2Loader extends Loader {
 		URL.revokeObjectURL( this.workerSourceURL );
 		this.workerPool.dispose();
 
+		_activeLoaders--;
+
 		return this;
 
 	}

+ 225 - 285
examples/jsm/loaders/TDSLoader.js

@@ -32,7 +32,6 @@ class TDSLoader extends Loader {
 		this.debug = false;
 
 		this.group = null;
-		this.position = 0;
 
 		this.materials = [];
 		this.meshes = [];
@@ -97,7 +96,6 @@ class TDSLoader extends Loader {
 	parse( arraybuffer, path ) {
 
 		this.group = new Group();
-		this.position = 0;
 		this.materials = [];
 		this.meshes = [];
 
@@ -123,31 +121,30 @@ class TDSLoader extends Loader {
 	readFile( arraybuffer, path ) {
 
 		const data = new DataView( arraybuffer );
-		const chunk = this.readChunk( data );
+		const chunk = new Chunk( data, 0, this.debugMessage );
 
 		if ( chunk.id === MLIBMAGIC || chunk.id === CMAGIC || chunk.id === M3DMAGIC ) {
 
-			let next = this.nextChunk( data, chunk );
+			let next = chunk.readChunk();
 
-			while ( next !== 0 ) {
+			while ( next ) {
 
-				if ( next === M3D_VERSION ) {
+				if ( next.id === M3D_VERSION ) {
 
-					const version = this.readDWord( data );
+					const version = next.readDWord();
 					this.debugMessage( '3DS file version: ' + version );
 
-				} else if ( next === MDATA ) {
+				} else if ( next.id === MDATA ) {
 
-					this.resetPosition( data );
-					this.readMeshData( data, path );
+					this.readMeshData( next, path );
 
 				} else {
 
-					this.debugMessage( 'Unknown main chunk: ' + next.toString( 16 ) );
+					this.debugMessage( 'Unknown main chunk: ' + next.hexId);
 
 				}
 
-				next = this.nextChunk( data, chunk );
+				next = chunk.readChunk();
 
 			}
 
@@ -161,46 +158,43 @@ class TDSLoader extends Loader {
 	 * Read mesh data chunk.
 	 *
 	 * @method readMeshData
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk to read mesh from
 	 * @param {String} path Path for external resources.
 	 */
-	readMeshData( data, path ) {
+	readMeshData( chunk, path ) {
 
-		const chunk = this.readChunk( data );
-		let next = this.nextChunk( data, chunk );
+		let next = chunk.readChunk();
 
-		while ( next !== 0 ) {
+		while ( next ) {
 
-			if ( next === MESH_VERSION ) {
+			if ( next.id === MESH_VERSION ) {
 
-				const version = + this.readDWord( data );
+				const version = + next.readDWord();
 				this.debugMessage( 'Mesh Version: ' + version );
 
-			} else if ( next === MASTER_SCALE ) {
+			} else if ( next.id === MASTER_SCALE ) {
 
-				const scale = this.readFloat( data );
+				const scale = next.readFloat();
 				this.debugMessage( 'Master scale: ' + scale );
 				this.group.scale.set( scale, scale, scale );
 
-			} else if ( next === NAMED_OBJECT ) {
+			} else if ( next.id === NAMED_OBJECT ) {
 
 				this.debugMessage( 'Named Object' );
-				this.resetPosition( data );
-				this.readNamedObject( data );
+				this.readNamedObject( next );
 
-			} else if ( next === MAT_ENTRY ) {
+			} else if ( next.id === MAT_ENTRY ) {
 
 				this.debugMessage( 'Material' );
-				this.resetPosition( data );
-				this.readMaterialEntry( data, path );
+				this.readMaterialEntry( next, path );
 
 			} else {
 
-				this.debugMessage( 'Unknown MDATA chunk: ' + next.toString( 16 ) );
+				this.debugMessage( 'Unknown MDATA chunk: ' + next.hexId );
 
 			}
 
-			next = this.nextChunk( data, chunk );
+			next = chunk.readChunk();
 
 		}
 
@@ -210,143 +204,129 @@ class TDSLoader extends Loader {
 	 * Read named object chunk.
 	 *
 	 * @method readNamedObject
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 */
-	readNamedObject( data ) {
+	readNamedObject( chunk ) {
 
-		const chunk = this.readChunk( data );
-		const name = this.readString( data, 64 );
-		chunk.cur = this.position;
+		const name = chunk.readString();
 
-		let next = this.nextChunk( data, chunk );
-		while ( next !== 0 ) {
+		let next = chunk.readChunk();
+		while ( next ) {
 
-			if ( next === N_TRI_OBJECT ) {
+			if ( next.id === N_TRI_OBJECT ) {
 
-				this.resetPosition( data );
-				const mesh = this.readMesh( data );
+				const mesh = this.readMesh( next );
 				mesh.name = name;
 				this.meshes.push( mesh );
-
 			} else {
-
-				this.debugMessage( 'Unknown named object chunk: ' + next.toString( 16 ) );
+				this.debugMessage( 'Unknown named object chunk: ' + next.hexId );
 
 			}
 
-			next = this.nextChunk( data, chunk );
+			next = chunk.readChunk( );
 
 		}
 
-		this.endChunk( chunk );
-
 	}
 
 	/**
 	 * Read material data chunk and add it to the material list.
 	 *
 	 * @method readMaterialEntry
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @param {String} path Path for external resources.
 	 */
-	readMaterialEntry( data, path ) {
+	readMaterialEntry( chunk, path ) {
 
-		const chunk = this.readChunk( data );
-		let next = this.nextChunk( data, chunk );
-		const material = new MeshPhongMaterial();
+		let next = chunk.readChunk();
+		let material = new MeshPhongMaterial();
 
-		while ( next !== 0 ) {
+		while ( next ) {
 
-			if ( next === MAT_NAME ) {
+			if ( next.id === MAT_NAME ) {
 
-				material.name = this.readString( data, 64 );
+				material.name = next.readString();
 				this.debugMessage( '   Name: ' + material.name );
 
-			} else if ( next === MAT_WIRE ) {
+			} else if ( next.id === MAT_WIRE ) {
 
 				this.debugMessage( '   Wireframe' );
 				material.wireframe = true;
 
-			} else if ( next === MAT_WIRE_SIZE ) {
+			} else if ( next.id === MAT_WIRE_SIZE ) {
 
-				const value = this.readByte( data );
+				const value = next.readByte();
 				material.wireframeLinewidth = value;
 				this.debugMessage( '   Wireframe Thickness: ' + value );
 
-			} else if ( next === MAT_TWO_SIDE ) {
+			} else if ( next.id === MAT_TWO_SIDE ) {
 
 				material.side = DoubleSide;
 				this.debugMessage( '   DoubleSided' );
 
-			} else if ( next === MAT_ADDITIVE ) {
+			} else if ( next.id === MAT_ADDITIVE ) {
 
 				this.debugMessage( '   Additive Blending' );
 				material.blending = AdditiveBlending;
 
-			} else if ( next === MAT_DIFFUSE ) {
+			} else if ( next.id === MAT_DIFFUSE ) {
 
 				this.debugMessage( '   Diffuse Color' );
-				material.color = this.readColor( data );
+				material.color = this.readColor( next );
 
-			} else if ( next === MAT_SPECULAR ) {
+			} else if ( next.id === MAT_SPECULAR ) {
 
 				this.debugMessage( '   Specular Color' );
-				material.specular = this.readColor( data );
+				material.specular = this.readColor( next );
 
-			} else if ( next === MAT_AMBIENT ) {
+			} else if ( next.id === MAT_AMBIENT ) {
 
 				this.debugMessage( '   Ambient color' );
-				material.color = this.readColor( data );
+				material.color = this.readColor( next );
 
-			} else if ( next === MAT_SHININESS ) {
+			} else if ( next.id === MAT_SHININESS ) {
 
-				const shininess = this.readPercentage( data );
+				const shininess = this.readPercentage( next );
 				material.shininess = shininess * 100;
 				this.debugMessage( '   Shininess : ' + shininess );
 
-			} else if ( next === MAT_TRANSPARENCY ) {
+			} else if ( next.id === MAT_TRANSPARENCY ) {
 
-				const transparency = this.readPercentage( data );
+				const transparency = this.readPercentage( next );
 				material.opacity = 1 - transparency;
 				this.debugMessage( '  Transparency : ' + transparency );
 				material.transparent = material.opacity < 1 ? true : false;
 
-			} else if ( next === MAT_TEXMAP ) {
+			} else if ( next.id === MAT_TEXMAP ) {
 
 				this.debugMessage( '   ColorMap' );
-				this.resetPosition( data );
-				material.map = this.readMap( data, path );
+				material.map = this.readMap( next, path );
 
-			} else if ( next === MAT_BUMPMAP ) {
+			} else if ( next.id === MAT_BUMPMAP ) {
 
 				this.debugMessage( '   BumpMap' );
-				this.resetPosition( data );
-				material.bumpMap = this.readMap( data, path );
+				material.bumpMap = this.readMap( next, path );
 
-			} else if ( next === MAT_OPACMAP ) {
+			} else if ( next.id === MAT_OPACMAP ) {
 
 				this.debugMessage( '   OpacityMap' );
-				this.resetPosition( data );
-				material.alphaMap = this.readMap( data, path );
+				material.alphaMap = this.readMap( next, path );
 
-			} else if ( next === MAT_SPECMAP ) {
+			} else if ( next.id === MAT_SPECMAP ) {
 
 				this.debugMessage( '   SpecularMap' );
-				this.resetPosition( data );
-				material.specularMap = this.readMap( data, path );
+				material.specularMap = this.readMap( next, path );
 
 			} else {
 
-				this.debugMessage( '   Unknown material chunk: ' + next.toString( 16 ) );
+				this.debugMessage( '   Unknown material chunk: ' + next.hexId );
 
 			}
 
-			next = this.nextChunk( data, chunk );
+			next = chunk.readChunk();
 
 		}
 
-		this.endChunk( chunk );
-
 		this.materials[ material.name ] = material;
 
 	}
@@ -355,13 +335,12 @@ class TDSLoader extends Loader {
 	 * Read mesh data chunk.
 	 *
 	 * @method readMesh
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @return {Mesh} The parsed mesh.
 	 */
-	readMesh( data ) {
+	readMesh( chunk ) {
 
-		const chunk = this.readChunk( data );
-		let next = this.nextChunk( data, chunk );
+		let next = chunk.readChunk( );
 
 		const geometry = new BufferGeometry();
 
@@ -369,11 +348,11 @@ class TDSLoader extends Loader {
 		const mesh = new Mesh( geometry, material );
 		mesh.name = 'mesh';
 
-		while ( next !== 0 ) {
+		while ( next ) {
 
-			if ( next === POINT_ARRAY ) {
+			if ( next.id === POINT_ARRAY ) {
 
-				const points = this.readWord( data );
+				const points = next.readWord( );
 
 				this.debugMessage( '   Vertex: ' + points );
 
@@ -383,22 +362,21 @@ class TDSLoader extends Loader {
 
 				for ( let i = 0; i < points; i ++ )		{
 
-					vertices.push( this.readFloat( data ) );
-					vertices.push( this.readFloat( data ) );
-					vertices.push( this.readFloat( data ) );
+					vertices.push( next.readFloat( ) );
+					vertices.push( next.readFloat( ) );
+					vertices.push( next.readFloat( ) );
 
 				}
 
 				geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
 
-			} else if ( next === FACE_ARRAY ) {
+			} else if ( next.id === FACE_ARRAY ) {
 
-				this.resetPosition( data );
-				this.readFaceArray( data, mesh );
+				this.readFaceArray( next, mesh );
 
-			} else if ( next === TEX_VERTS ) {
+			} else if ( next.id === TEX_VERTS ) {
 
-				const texels = this.readWord( data );
+				const texels = next.readWord( );
 
 				this.debugMessage( '   UV: ' + texels );
 
@@ -406,24 +384,24 @@ class TDSLoader extends Loader {
 
 				const uvs = [];
 
-				for ( let i = 0; i < texels; i ++ )		{
+				for ( let i = 0; i < texels; i ++ ) {
 
-					uvs.push( this.readFloat( data ) );
-					uvs.push( this.readFloat( data ) );
+					uvs.push( next.readFloat( ) );
+					uvs.push( next.readFloat( ) );
 
 				}
 
 				geometry.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
 
 
-			} else if ( next === MESH_MATRIX ) {
+			} else if ( next.id === MESH_MATRIX ) {
 
 				this.debugMessage( '   Tranformation Matrix (TODO)' );
 
 				const values = [];
 				for ( let i = 0; i < 12; i ++ ) {
 
-					values[ i ] = this.readFloat( data );
+					values[ i ] = next.readFloat( );
 
 				}
 
@@ -463,16 +441,14 @@ class TDSLoader extends Loader {
 
 			} else {
 
-				this.debugMessage( '   Unknown mesh chunk: ' + next.toString( 16 ) );
+				this.debugMessage( '   Unknown mesh chunk: ' + next.hexId );
 
 			}
 
-			next = this.nextChunk( data, chunk );
+			next = chunk.readChunk( );
 
 		}
 
-		this.endChunk( chunk );
-
 		geometry.computeVertexNormals();
 
 		return mesh;
@@ -483,13 +459,12 @@ class TDSLoader extends Loader {
 	 * Read face array data chunk.
 	 *
 	 * @method readFaceArray
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @param {Mesh} mesh Mesh to be filled with the data read.
 	 */
-	readFaceArray( data, mesh ) {
+	readFaceArray( chunk, mesh ) {
 
-		const chunk = this.readChunk( data );
-		const faces = this.readWord( data );
+		const faces = chunk.readWord( );
 
 		this.debugMessage( '   Faces: ' + faces );
 
@@ -497,9 +472,9 @@ class TDSLoader extends Loader {
 
 		for ( let i = 0; i < faces; ++ i ) {
 
-			index.push( this.readWord( data ), this.readWord( data ), this.readWord( data ) );
+			index.push( chunk.readWord( ), chunk.readWord( ), chunk.readWord( ) );
 
-			this.readWord( data ); // visibility
+			chunk.readWord( ); // visibility
 
 		}
 
@@ -510,17 +485,15 @@ class TDSLoader extends Loader {
 		let materialIndex = 0;
 		let start = 0;
 
-		while ( this.position < chunk.end ) {
+		while ( !chunk.endOfChunk ) {
 
-			const subchunk = this.readChunk( data );
+			const subchunk = chunk.readChunk( );
 
 			if ( subchunk.id === MSH_MAT_GROUP ) {
 
 				this.debugMessage( '      Material Group' );
 
-				this.resetPosition( data );
-
-				const group = this.readMaterialGroup( data );
+				const group = this.readMaterialGroup( subchunk );
 				const count = group.index.length * 3; // assuming successive indices
 
 				mesh.geometry.addGroup( start, count, materialIndex );
@@ -540,78 +513,71 @@ class TDSLoader extends Loader {
 
 			} else {
 
-				this.debugMessage( '      Unknown face array chunk: ' + subchunk.toString( 16 ) );
+				this.debugMessage( '      Unknown face array chunk: ' + subchunk.hexId );
 
 			}
 
-			this.endChunk( subchunk );
-
 		}
 
 		if ( mesh.material.length === 1 ) mesh.material = mesh.material[ 0 ]; // for backwards compatibility
 
-		this.endChunk( chunk );
-
 	}
 
 	/**
 	 * Read texture map data chunk.
 	 *
 	 * @method readMap
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @param {String} path Path for external resources.
 	 * @return {Texture} Texture read from this data chunk.
 	 */
-	readMap( data, path ) {
+	readMap( chunk, path ) {
 
-		const chunk = this.readChunk( data );
-		let next = this.nextChunk( data, chunk );
+		let next = chunk.readChunk( );
 		let texture = {};
 
 		const loader = new TextureLoader( this.manager );
 		loader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
 
-		while ( next !== 0 ) {
+		while ( next ) {
 
-			if ( next === MAT_MAPNAME ) {
+			if ( next.id === MAT_MAPNAME ) {
 
-				const name = this.readString( data, 128 );
+				const name = next.readString();
 				texture = loader.load( name );
 
 				this.debugMessage( '      File: ' + path + name );
 
-			} else if ( next === MAT_MAP_UOFFSET ) {
+			} else if ( next.id === MAT_MAP_UOFFSET ) {
 
-				texture.offset.x = this.readFloat( data );
+				texture.offset.x = next.readFloat( );
 				this.debugMessage( '      OffsetX: ' + texture.offset.x );
 
-			} else if ( next === MAT_MAP_VOFFSET ) {
+			} else if ( next.id === MAT_MAP_VOFFSET ) {
 
-				texture.offset.y = this.readFloat( data );
+				texture.offset.y = next.readFloat( );
 				this.debugMessage( '      OffsetY: ' + texture.offset.y );
 
-			} else if ( next === MAT_MAP_USCALE ) {
+			} else if ( next.id === MAT_MAP_USCALE ) {
 
-				texture.repeat.x = this.readFloat( data );
+				texture.repeat.x = next.readFloat( );
 				this.debugMessage( '      RepeatX: ' + texture.repeat.x );
 
-			} else if ( next === MAT_MAP_VSCALE ) {
+			} else if ( next.id === MAT_MAP_VSCALE ) {
 
-				texture.repeat.y = this.readFloat( data );
+				texture.repeat.y = next.readFloat( );
 				this.debugMessage( '      RepeatY: ' + texture.repeat.y );
 
 			} else {
 
-				this.debugMessage( '      Unknown map chunk: ' + next.toString( 16 ) );
+				this.debugMessage( '      Unknown map chunk: ' + next.hexId );
 
 			}
 
-			next = this.nextChunk( data, chunk );
+			next = chunk.readChunk( );
 
 		}
 
-		this.endChunk( chunk );
-
 		return texture;
 
 	}
@@ -620,14 +586,13 @@ class TDSLoader extends Loader {
 	 * Read material group data chunk.
 	 *
 	 * @method readMaterialGroup
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @return {Object} Object with name and index of the object.
 	 */
-	readMaterialGroup( data ) {
+	readMaterialGroup( chunk ) {
 
-		this.readChunk( data );
-		const name = this.readString( data, 64 );
-		const numFaces = this.readWord( data );
+		const name = chunk.readString();
+		const numFaces = chunk.readWord();
 
 		this.debugMessage( '         Name: ' + name );
 		this.debugMessage( '         Faces: ' + numFaces );
@@ -635,7 +600,7 @@ class TDSLoader extends Loader {
 		const index = [];
 		for ( let i = 0; i < numFaces; ++ i ) {
 
-			index.push( this.readWord( data ) );
+			index.push( chunk.readWord( ) );
 
 		}
 
@@ -647,29 +612,29 @@ class TDSLoader extends Loader {
 	 * Read a color value.
 	 *
 	 * @method readColor
-	 * @param {DataView} data Dataview.
+	 * @param {Chunk} chunk Chunk.
 	 * @return {Color} Color value read..
 	 */
-	readColor( data ) {
+	readColor( chunk ) {
 
-		const chunk = this.readChunk( data );
+		const subChunk = chunk.readChunk( );
 		const color = new Color();
 
-		if ( chunk.id === COLOR_24 || chunk.id === LIN_COLOR_24 ) {
+		if ( subChunk.id === COLOR_24 || subChunk.id === LIN_COLOR_24 ) {
 
-			const r = this.readByte( data );
-			const g = this.readByte( data );
-			const b = this.readByte( data );
+			const r = subChunk.readByte( );
+			const g = subChunk.readByte( );
+			const b = subChunk.readByte( );
 
 			color.setRGB( r / 255, g / 255, b / 255 );
 
 			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 
-		}	else if ( chunk.id === COLOR_F || chunk.id === LIN_COLOR_F ) {
+		}	else if ( subChunk.id === COLOR_F || subChunk.id === LIN_COLOR_F ) {
 
-			const r = this.readFloat( data );
-			const g = this.readFloat( data );
-			const b = this.readFloat( data );
+			const r = subChunk.readFloat( );
+			const g = subChunk.readFloat( );
+			const b = subChunk.readFloat( );
 
 			color.setRGB( r, g, b );
 
@@ -677,101 +642,142 @@ class TDSLoader extends Loader {
 
 		}	else {
 
-			this.debugMessage( '      Unknown color chunk: ' + chunk.toString( 16 ) );
+			this.debugMessage( '      Unknown color chunk: ' + subChunk.hexId );
 
 		}
 
-		this.endChunk( chunk );
 		return color;
 
 	}
 
 	/**
-	 * Read next chunk of data.
+	 * Read percentage value.
 	 *
-	 * @method readChunk
-	 * @param {DataView} data Dataview.
-	 * @return {Object} Chunk of data read.
+	 * @method readPercentage
+	 * @param {Chunk} chunk Chunk to read data from.
+	 * @return {Number} Data read from the dataview.
 	 */
-	readChunk( data ) {
+	readPercentage( chunk ) {
 
-		const chunk = {};
+		const subChunk = chunk.readChunk( );
 
-		chunk.cur = this.position;
-		chunk.id = this.readWord( data );
-		chunk.size = this.readDWord( data );
-		chunk.end = chunk.cur + chunk.size;
-		chunk.cur += 6;
+		switch ( subChunk.id ) {
 
-		return chunk;
+			case INT_PERCENTAGE:
+				return ( subChunk.readShort( ) / 100 );
+				break;
+
+			case FLOAT_PERCENTAGE:
+				return subChunk.readFloat( );
+				break;
+
+			default:
+				this.debugMessage( '      Unknown percentage chunk: ' + subChunk.hexId );
+				return 0;
+
+		}
 
 	}
 
 	/**
-	 * Set position to the end of the current chunk of data.
+	 * Print debug message to the console.
+	 *
+	 * Is controlled by a flag to show or hide debug messages.
 	 *
-	 * @method endChunk
-	 * @param {Object} chunk Data chunk.
+	 * @method debugMessage
+	 * @param {Object} message Debug message to print to the console.
 	 */
-	endChunk( chunk ) {
+	debugMessage( message ) {
 
-		this.position = chunk.end;
+		if ( this.debug ) {
+
+			console.log( message );
+
+		}
 
 	}
+}
+
 
+/** Read data/sub-chunks from chunk */
+class Chunk {
 	/**
-	 * Move to the next data chunk.
+	 * Create a new chunk
 	 *
-	 * @method nextChunk
-	 * @param {DataView} data Dataview.
-	 * @param {Object} chunk Data chunk.
+	 * @class Chunk
+	 * @param {DataView} data DataView to read from.
+	 * @param {Number} position in data.
+	 * @param {Function} debugMessage logging callback.
 	 */
-	nextChunk( data, chunk ) {
-
-		if ( chunk.cur >= chunk.end ) {
+	constructor(data, position, debugMessage) {
+		this.data = data;
+		// the offset to the begin of this chunk
+		this.offset = position;
+		// the current reading position
+		this.position = position;
+		this.debugMessage = debugMessage;
+
+		if (this.debugMessage instanceof Function) {
+			this.debugMessage = function() {};
+		}
 
-			return 0;
+		this.id = this.readWord();
+		this.size = this.readDWord();
+		this.end = this.offset + this.size;
 
+		if (this.end > data.byteLength) {
+			this.debugMessage( 'Bad chunk size for chunk at ' + position );
 		}
+	}
 
-		this.position = chunk.cur;
+	/**
+	 * read a sub cchunk.
+	 *
+	 * @method readChunk
+	 * @return {Chunk | null} next sub chunk
+	 */
+	readChunk () {
 
-		try {
+		if ( this.endOfChunk ) {
+			return null;
+		}
 
-			const next = this.readChunk( data );
-			chunk.cur += next.size;
-			return next.id;
+		try {
+			let next = new Chunk( this.data, this.position, this.debugMessage );
+			this.position += next.size;
+			return next;
 
 		}	catch ( e ) {
-
 			this.debugMessage( 'Unable to read chunk at ' + this.position );
-			return 0;
+			return null;
 
 		}
 
 	}
 
 	/**
-	 * Reset dataview position.
+	 * return the ID of this chunk as Hex
 	 *
-	 * @method resetPosition
+	 * @method idToString
+	 * @return {String} hex-string of id
 	 */
-	resetPosition() {
-
-		this.position -= 6;
+	get hexId() {
+		return this.id.toString( 16 );
+	}
 
+	get endOfChunk() {
+		return this.position >= this.end;
 	}
 
 	/**
 	 * Read byte value.
 	 *
 	 * @method readByte
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 */
-	readByte( data ) {
+	readByte() {
 
-		const v = data.getUint8( this.position, true );
+		const v = this.data.getUint8( this.position, true );
 		this.position += 1;
 		return v;
 
@@ -781,20 +787,20 @@ class TDSLoader extends Loader {
 	 * Read 32 bit float value.
 	 *
 	 * @method readFloat
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 */
-	readFloat( data ) {
+	readFloat() {
 
 		try {
 
-			const v = data.getFloat32( this.position, true );
+			const v = this.data.getFloat32( this.position, true );
 			this.position += 4;
 			return v;
 
 		}	catch ( e ) {
 
-			this.debugMessage( e + ' ' + this.position + ' ' + data.byteLength );
+			this.debugMessage( e + ' ' + this.position + ' ' + this.data.byteLength );
+			return 0;
 
 		}
 
@@ -804,12 +810,11 @@ class TDSLoader extends Loader {
 	 * Read 32 bit signed integer value.
 	 *
 	 * @method readInt
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 */
-	readInt( data ) {
+	readInt() {
 
-		const v = data.getInt32( this.position, true );
+		const v = this.data.getInt32( this.position, true );
 		this.position += 4;
 		return v;
 
@@ -819,12 +824,11 @@ class TDSLoader extends Loader {
 	 * Read 16 bit signed integer value.
 	 *
 	 * @method readShort
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 */
-	readShort( data ) {
+	readShort() {
 
-		const v = data.getInt16( this.position, true );
+		const v = this.data.getInt16( this.position, true );
 		this.position += 2;
 		return v;
 
@@ -834,12 +838,11 @@ class TDSLoader extends Loader {
 	 * Read 64 bit unsigned integer value.
 	 *
 	 * @method readDWord
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 */
-	readDWord( data ) {
+	readDWord() {
 
-		const v = data.getUint32( this.position, true );
+		const v = this.data.getUint32( this.position, true );
 		this.position += 4;
 		return v;
 
@@ -849,95 +852,32 @@ class TDSLoader extends Loader {
 	 * Read 32 bit unsigned integer value.
 	 *
 	 * @method readWord
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 */
-	readWord( data ) {
+	readWord() {
 
-		const v = data.getUint16( this.position, true );
+		const v = this.data.getUint16( this.position, true );
 		this.position += 2;
 		return v;
 
 	}
 
 	/**
-	 * Read string value.
+	 * Read NULL terminated ASCII string value from chunk-pos.
 	 *
 	 * @method readString
-	 * @param {DataView} data Dataview to read data from.
-	 * @param {Number} maxLength Max size of the string to be read.
 	 * @return {String} Data read from the dataview.
 	 */
-	readString( data, maxLength ) {
+	readString() {
 
 		let s = '';
-
-		for ( let i = 0; i < maxLength; i ++ ) {
-
-			const c = this.readByte( data );
-			if ( ! c ) {
-
-				break;
-
-			}
-
+		let c = this.readByte();
+		while ( c ) {
 			s += String.fromCharCode( c );
-
+			c = this.readByte();
 		}
 
 		return s;
-
-	}
-
-	/**
-	 * Read percentage value.
-	 *
-	 * @method readPercentage
-	 * @param {DataView} data Dataview to read data from.
-	 * @return {Number} Data read from the dataview.
-	 */
-	readPercentage( data ) {
-
-		const chunk = this.readChunk( data );
-		let 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.
-	 *
-	 * Is controlled by a flag to show or hide debug messages.
-	 *
-	 * @method debugMessage
-	 * @param {Object} message Debug message to print to the console.
-	 */
-	debugMessage( message ) {
-
-		if ( this.debug ) {
-
-			console.log( message );
-
-		}
-
 	}
 
 }

+ 1 - 1
examples/jsm/nodes/materials/StandardNodeMaterial.js

@@ -34,7 +34,7 @@ NodeUtils.addShortcuts( StandardNodeMaterial.prototype, 'fragment', [
 	'environment',
 	'mask',
 	'position',
-	'sheenTint'
+	'sheenColor'
 ] );
 
 export { StandardNodeMaterial };

+ 6 - 6
examples/jsm/nodes/materials/nodes/StandardNode.js

@@ -185,7 +185,7 @@ class StandardNode extends Node {
 
 			}
 
-			if ( this.sheenTint ) this.sheenTint.analyze( builder );
+			if ( this.sheenColor ) this.sheenColor.analyze( builder );
 
 			// build code
 
@@ -230,7 +230,7 @@ class StandardNode extends Node {
 
 			const clearcoatEnv = useClearcoat && environment ? this.environment.flow( builder, 'c', { cache: 'clearcoat', context: contextClearcoatEnvironment, slot: 'environment' } ) : undefined;
 
-			const sheenTint = this.sheenTint ? this.sheenTint.flow( builder, 'c' ) : undefined;
+			const sheenColor = this.sheenColor ? this.sheenColor.flow( builder, 'c' ) : undefined;
 
 			builder.requires.transparent = alpha !== undefined;
 
@@ -368,9 +368,9 @@ class StandardNode extends Node {
 
 			}
 
-			if ( sheenTint ) {
+			if ( sheenColor ) {
 
-				output.push( 'material.sheenTint = ' + sheenTint.result + ';' );
+				output.push( 'material.sheenColor = ' + sheenColor.result + ';' );
 
 			}
 
@@ -547,7 +547,7 @@ class StandardNode extends Node {
 
 		if ( source.environment ) this.environment = source.environment;
 
-		if ( source.sheenTint ) this.sheenTint = source.sheenTint;
+		if ( source.sheenColor ) this.sheenColor = source.sheenColor;
 
 		return this;
 
@@ -593,7 +593,7 @@ class StandardNode extends Node {
 
 			if ( this.environment ) data.environment = this.environment.toJSON( meta ).uuid;
 
-			if ( this.sheenTint ) data.sheenTint = this.sheenTint.toJSON( meta ).uuid;
+			if ( this.sheenColor ) data.sheenColor = this.sheenColor.toJSON( meta ).uuid;
 
 		}
 

+ 46 - 152
examples/jsm/objects/MarchingCubes.js

@@ -2,19 +2,21 @@ import {
 	BufferAttribute,
 	BufferGeometry,
 	Color,
-	ImmediateRenderObject,
-	NoColors
+	DynamicDrawUsage,
+	Mesh
 } from '../../../build/three.module.js';
 
 /**
  * Port of http://webglsamples.org/blob/blob.html
  */
 
-class MarchingCubes extends ImmediateRenderObject {
+class MarchingCubes extends Mesh {
 
-	constructor( resolution, material, enableUvs, enableColors ) {
+	constructor( resolution, material, enableUvs = false, enableColors = false, maxPolyCount = 10000 ) {
 
-		super( material );
+		const geometry = new BufferGeometry();
+
+		super( geometry, material );
 
 		const scope = this;
 
@@ -24,8 +26,8 @@ class MarchingCubes extends ImmediateRenderObject {
 		const nlist = new Float32Array( 12 * 3 );
 		const clist = new Float32Array( 12 * 3 );
 
-		this.enableUvs = enableUvs !== undefined ? enableUvs : false;
-		this.enableColors = enableColors !== undefined ? enableColors : false;
+		this.enableUvs = enableUvs;
+		this.enableColors = enableColors;
 
 		// functions have to be object properties
 		// prototype functions kill performance
@@ -56,28 +58,37 @@ class MarchingCubes extends ImmediateRenderObject {
 			this.normal_cache = new Float32Array( this.size3 * 3 );
 			this.palette = new Float32Array( this.size3 * 3 );
 
-			// immediate render mode simulator
+			//
 
-			this.maxCount = 4096; // TODO: find the fastest size for this buffer
 			this.count = 0;
 
-			this.hasPositions = false;
-			this.hasNormals = false;
-			this.hasColors = false;
-			this.hasUvs = false;
+			const maxVertexCount = maxPolyCount * 3;
+
+			this.positionArray = new Float32Array( maxVertexCount * 3 );
+			const positionAttribute = new BufferAttribute( this.positionArray, 3 );
+			positionAttribute.setUsage( DynamicDrawUsage );
+			geometry.setAttribute( 'position', positionAttribute );
 
-			this.positionArray = new Float32Array( this.maxCount * 3 );
-			this.normalArray = new Float32Array( this.maxCount * 3 );
+			this.normalArray = new Float32Array( maxVertexCount * 3 );
+			const normalAttribute = new BufferAttribute( this.normalArray, 3 );
+			normalAttribute.setUsage( DynamicDrawUsage );
+			geometry.setAttribute( 'normal', normalAttribute );
 
 			if ( this.enableUvs ) {
 
-				this.uvArray = new Float32Array( this.maxCount * 2 );
+				this.uvArray = new Float32Array( maxVertexCount * 2 );
+				const uvAttribute = new BufferAttribute( this.uvArray, 2 );
+				uvAttribute.setUsage( DynamicDrawUsage );
+				geometry.setAttribute( 'uv', uvAttribute );
 
 			}
 
 			if ( this.enableColors ) {
 
-				this.colorArray = new Float32Array( this.maxCount * 3 );
+				this.colorArray = new Float32Array( maxVertexCount * 3 );
+				const colorAttribute = new BufferAttribute( this.colorArray, 3 );
+				colorAttribute.setUsage( DynamicDrawUsage );
+				geometry.setAttribute( 'color', colorAttribute );
 
 			}
 
@@ -173,7 +184,7 @@ class MarchingCubes extends ImmediateRenderObject {
 		// Returns total number of triangles. Fills triangles.
 		// (this is where most of time is spent - it's inner work of O(n3) loop )
 
-		function polygonize( fx, fy, fz, q, isol, renderCallback ) {
+		function polygonize( fx, fy, fz, q, isol ) {
 
 			// cache indices
 			const q1 = q + 1,
@@ -369,8 +380,7 @@ class MarchingCubes extends ImmediateRenderObject {
 					clist,
 					3 * triTable[ o1 ],
 					3 * triTable[ o2 ],
-					3 * triTable[ o3 ],
-					renderCallback
+					3 * triTable[ o3 ]
 				);
 
 				i += 3;
@@ -382,11 +392,7 @@ class MarchingCubes extends ImmediateRenderObject {
 
 		}
 
-		/////////////////////////////////////
-		// Immediate render mode simulator
-		/////////////////////////////////////
-
-		function posnormtriv( pos, norm, colors, o1, o2, o3, renderCallback ) {
+		function posnormtriv( pos, norm, colors, o1, o2, o3 ) {
 
 			const c = scope.count * 3;
 
@@ -477,69 +483,8 @@ class MarchingCubes extends ImmediateRenderObject {
 
 			scope.count += 3;
 
-			if ( scope.count >= scope.maxCount - 3 ) {
-
-				scope.hasPositions = true;
-				scope.hasNormals = true;
-
-				if ( scope.enableUvs ) {
-
-					scope.hasUvs = true;
-
-				}
-
-				if ( scope.enableColors ) {
-
-					scope.hasColors = true;
-
-				}
-
-				renderCallback( scope );
-
-			}
-
 		}
 
-		this.begin = function () {
-
-			this.count = 0;
-
-			this.hasPositions = false;
-			this.hasNormals = false;
-			this.hasUvs = false;
-			this.hasColors = false;
-
-		};
-
-		this.end = function ( renderCallback ) {
-
-			if ( this.count === 0 ) return;
-
-			for ( let i = this.count * 3; i < this.positionArray.length; i ++ ) {
-
-				this.positionArray[ i ] = 0.0;
-
-			}
-
-			this.hasPositions = true;
-			this.hasNormals = true;
-
-			if ( this.enableUvs && this.material.map ) {
-
-				this.hasUvs = true;
-
-			}
-
-			if ( this.enableColors && this.material.vertexColors !== NoColors ) {
-
-				this.hasColors = true;
-
-			}
-
-			renderCallback( this );
-
-		};
-
 		/////////////////////////////////////
 		// Metaballs
 		/////////////////////////////////////
@@ -867,9 +812,9 @@ class MarchingCubes extends ImmediateRenderObject {
 
 		};
 
-		this.render = function ( renderCallback ) {
+		this.onBeforeRender = function () {
 
-			this.begin();
+			this.count = 0;
 
 			// Triangulate. Yeah, this is slow.
 
@@ -890,7 +835,7 @@ class MarchingCubes extends ImmediateRenderObject {
 						const fx = ( x - this.halfsize ) / this.halfsize; //+ 1
 						const q = y_offset + x;
 
-						polygonize( fx, fy, fz, q, this.isolation, renderCallback );
+						 polygonize( fx, fy, fz, q, this.isolation );
 
 					}
 
@@ -898,76 +843,25 @@ class MarchingCubes extends ImmediateRenderObject {
 
 			}
 
-			this.end( renderCallback );
+			// reset unneeded data
 
-		};
+			for ( let i = this.count * 3; i < this.positionArray.length; i ++ ) {
 
-		this.generateGeometry = function () {
+				this.positionArray[ i ] = 0.0;
 
-			console.warn(
-				'THREE.MarchingCubes: generateGeometry() now returns BufferGeometry'
-			);
-			return this.generateBufferGeometry();
+			}
 
-		};
+			// update geometry data
 
-		function concatenate( a, b, length ) {
+			geometry.getAttribute( 'position' ).needsUpdate = true;
+			geometry.getAttribute( 'normal' ).needsUpdate = true;
 
-			const result = new Float32Array( a.length + length );
-			result.set( a, 0 );
-			result.set( b.slice( 0, length ), a.length );
-			return result;
+			if ( this.enableUvs ) geometry.getAttribute( 'uv' ).needsUpdate = true;
+			if ( this.enableColors ) geometry.getAttribute( 'color' ).needsUpdate = true;
 
-		}
+			// safety check
 
-		this.generateBufferGeometry = function () {
-
-			const geo = new BufferGeometry();
-			let posArray = new Float32Array();
-			let normArray = new Float32Array();
-			let colorArray = new Float32Array();
-			let uvArray = new Float32Array();
-			const scope = this;
-
-			const geo_callback = function ( object ) {
-
-				if ( scope.hasPositions )
-					posArray = concatenate(
-						posArray,
-						object.positionArray,
-						object.count * 3
-					);
-				if ( scope.hasNormals )
-					normArray = concatenate(
-						normArray,
-						object.normalArray,
-						object.count * 3
-					);
-				if ( scope.hasColors )
-					colorArray = concatenate(
-						colorArray,
-						object.colorArray,
-						object.count * 3
-					);
-				if ( scope.hasUvs )
-					uvArray = concatenate( uvArray, object.uvArray, object.count * 2 );
-
-				object.count = 0;
-
-			};
-
-			this.render( geo_callback );
-
-			if ( this.hasPositions )
-				geo.setAttribute( 'position', new BufferAttribute( posArray, 3 ) );
-			if ( this.hasNormals )
-				geo.setAttribute( 'normal', new BufferAttribute( normArray, 3 ) );
-			if ( this.hasColors )
-				geo.setAttribute( 'color', new BufferAttribute( colorArray, 3 ) );
-			if ( this.hasUvs )
-				geo.setAttribute( 'uv', new BufferAttribute( uvArray, 2 ) );
-
-			return geo;
+			if ( this.count / 3 > maxPolyCount ) console.warn( 'THREE.MarchingCubes: Geometry buffers too small for rendering. Please create an instance with a higher poly count.' );
 
 		};
 
@@ -984,7 +878,7 @@ MarchingCubes.prototype.isMarchingCubes = true;
 /////////////////////////////////////
 
 // These tables are straight from Paul Bourke's page:
-// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/
+// http://paulbourke.net/geometry/polygonise/
 // who in turn got them from Cory Gene Bloyd.
 
 const edgeTable = new Int32Array( [

+ 5 - 4
examples/jsm/renderers/CSS2DRenderer.js

@@ -6,11 +6,11 @@ import {
 
 class CSS2DObject extends Object3D {
 
- 	constructor( element ) {
+	constructor( element = document.createElement( 'div' ) ) {
 
 		super();
 
-		this.element = element || document.createElement( 'div' );
+		this.element = element;
 
 		this.element.style.position = 'absolute';
 		this.element.style.userSelect = 'none';
@@ -57,7 +57,7 @@ const _b = new Vector3();
 
 class CSS2DRenderer {
 
-	constructor() {
+	constructor( parameters = {} ) {
 
 		const _this = this;
 
@@ -68,7 +68,8 @@ class CSS2DRenderer {
 			objects: new WeakMap()
 		};
 
-		const domElement = document.createElement( 'div' );
+		const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
+
 		domElement.style.overflow = 'hidden';
 
 		this.domElement = domElement;

+ 5 - 4
examples/jsm/renderers/CSS3DRenderer.js

@@ -15,11 +15,11 @@ const _scale = new Vector3();
 
 class CSS3DObject extends Object3D {
 
-	constructor( element ) {
+	constructor( element = document.createElement( 'div' ) ) {
 
 		super();
 
-		this.element = element || document.createElement( 'div' );
+		this.element = element;
 		this.element.style.position = 'absolute';
 		this.element.style.pointerEvents = 'auto';
 		this.element.style.userSelect = 'none';
@@ -87,7 +87,7 @@ const _matrix2 = new Matrix4();
 
 class CSS3DRenderer {
 
-	constructor() {
+	constructor( parameters = {} ) {
 
 		const _this = this;
 
@@ -99,7 +99,8 @@ class CSS3DRenderer {
 			objects: new WeakMap()
 		};
 
-		const domElement = document.createElement( 'div' );
+		const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
+
 		domElement.style.overflow = 'hidden';
 
 		this.domElement = domElement;

+ 11 - 13
examples/jsm/renderers/nodes/Nodes.js

@@ -1,8 +1,8 @@
 // core
 import ArrayInputNode from './core/ArrayInputNode.js';
 import AttributeNode from './core/AttributeNode.js';
+import BypassNode from './core/BypassNode.js';
 import CodeNode from './core/CodeNode.js';
-import ConstNode from './core/ConstNode.js';
 import ContextNode from './core/ContextNode.js';
 import ExpressionNode from './core/ExpressionNode.js';
 import FunctionCallNode from './core/FunctionCallNode.js';
@@ -15,13 +15,10 @@ import NodeCode from './core/NodeCode.js';
 import NodeFrame from './core/NodeFrame.js';
 import NodeFunctionInput from './core/NodeFunctionInput.js';
 import NodeKeywords from './core/NodeKeywords.js';
-import NodeSlot from './core/NodeSlot.js';
 import NodeUniform from './core/NodeUniform.js';
 import NodeVar from './core/NodeVar.js';
 import NodeVary from './core/NodeVary.js';
 import PropertyNode from './core/PropertyNode.js';
-import StructNode from './core/StructNode.js';
-import StructVarNode from './core/StructVarNode.js';
 import TempNode from './core/TempNode.js';
 import VarNode from './core/VarNode.js';
 import VaryNode from './core/VaryNode.js';
@@ -37,11 +34,13 @@ import Object3DNode from './accessors/Object3DNode.js';
 import PointUVNode from './accessors/PointUVNode.js';
 import PositionNode from './accessors/PositionNode.js';
 import ReferenceNode from './accessors/ReferenceNode.js';
+import SkinningNode from './accessors/SkinningNode.js';
 import UVNode from './accessors/UVNode.js';
 
 // inputs
 import ColorNode from './inputs/ColorNode.js';
 import FloatNode from './inputs/FloatNode.js';
+import IntNode from './inputs/IntNode.js';
 import Matrix3Node from './inputs/Matrix3Node.js';
 import Matrix4Node from './inputs/Matrix3Node.js';
 import TextureNode from './inputs/TextureNode.js';
@@ -63,6 +62,7 @@ import LightNode from './lights/LightNode.js';
 import LightsNode from './lights/LightsNode.js';
 
 // utils
+import ArrayElementNode from './utils/ArrayElementNode.js';
 import JoinNode from './utils/JoinNode.js';
 import SplitNode from './utils/SplitNode.js';
 import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
@@ -76,21 +76,19 @@ export * from './core/constants.js';
 
 // functions
 export * from './functions/BSDFs.js';
-export * from './functions/EncodingFunctions.js';
-export * from './functions/MathFunctions.js';
-
-// consts
-export * from './consts/MathConsts.js';
 
 // materials
 export * from './materials/Materials.js';
 
+// shader node
+export * from './ShaderNode.js';
+
 export {
 	// core
 	ArrayInputNode,
 	AttributeNode,
+	BypassNode,
 	CodeNode,
-	ConstNode,
 	ContextNode,
 	ExpressionNode,
 	FunctionCallNode,
@@ -103,13 +101,10 @@ export {
 	NodeFrame,
 	NodeFunctionInput,
 	NodeKeywords,
-	NodeSlot,
 	NodeUniform,
 	NodeVar,
 	NodeVary,
 	PropertyNode,
-	StructNode,
-	StructVarNode,
 	TempNode,
 	VarNode,
 	VaryNode,
@@ -125,11 +120,13 @@ export {
 	PointUVNode,
 	PositionNode,
 	ReferenceNode,
+	SkinningNode,
 	UVNode,
 
 	// inputs
 	ColorNode,
 	FloatNode,
+	IntNode,
 	Matrix3Node,
 	Matrix4Node,
 	TextureNode,
@@ -151,6 +148,7 @@ export {
 	LightsNode,
 
 	// utils
+	ArrayElementNode,
 	JoinNode,
 	SplitNode,
 	SpriteSheetUVNode,

+ 162 - 45
examples/jsm/renderers/nodes/ShaderNode.js

@@ -1,3 +1,7 @@
+// core
+import PropertyNode from './core/PropertyNode.js';
+import VarNode from './core/VarNode.js';
+
 // inputs
 import ColorNode from './inputs/ColorNode.js';
 import FloatNode from './inputs/FloatNode.js';
@@ -5,11 +9,17 @@ import Vector2Node from './inputs/Vector2Node.js';
 import Vector3Node from './inputs/Vector3Node.js';
 import Vector4Node from './inputs/Vector4Node.js';
 
+// accessors
+import PositionNode from './accessors/PositionNode.js';
+import NormalNode from './accessors/NormalNode.js';
+
 // math
-import MathNode from './math/MathNode.js';
 import OperatorNode from './math/OperatorNode.js';
+import CondNode from './math/CondNode.js';
+import MathNode from './math/MathNode.js';
 
 // utils
+import ArrayElementNode from './utils/ArrayElementNode.js';
 import JoinNode from './utils/JoinNode.js';
 import SplitNode from './utils/SplitNode.js';
 
@@ -18,17 +28,35 @@ import { Vector2, Vector3, Vector4, Color } from 'three';
 
 const NodeHandler = {
 
-	get: function ( node, prop ) {
+	construct( NodeClosure, params ) {
+
+		const inputs = params.shift();
 
-		// Split Properties Pass
+		return NodeClosure( ShaderNodeObjects( inputs ), ...params );
+
+	},
+
+	get: function ( node, prop ) {
 
 		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
 
-			const splitProps = prop.match( /^[xyzw]{1,4}$/ );
+			if ( /^[xyzwrgbastpq]{1,4}$/.test( prop ) === true ) {
+
+				// accessing properties ( swizzle )
+
+				prop = prop
+					.replace( /r|s/g, 'x' )
+					.replace( /g|t/g, 'y' )
+					.replace( /b|p/g, 'z' )
+					.replace( /a|q/g, 'w' );
+
+				return ShaderNodeObject( new SplitNode( node, prop ) );
 
-			if ( splitProps !== null ) {
+			} else if ( /^\d+$/.test( prop ) === true ) {
 
-				return ShaderNodeObject( new SplitNode( node, splitProps[ 0 ] ) );
+				// accessing array
+
+				return ShaderNodeObject( new ArrayElementNode( node, new FloatNode( Number( prop ) ).setConst( true ) ) );
 
 			}
 
@@ -40,7 +68,7 @@ const NodeHandler = {
 
 };
 
-export const ShaderNodeObject = ( obj ) => {
+const ShaderNodeObject = ( obj ) => {
 
 	const type = typeof obj;
 
@@ -70,7 +98,19 @@ export const ShaderNodeObject = ( obj ) => {
 
 };
 
-export const ShaderNodeArray = ( array ) => {
+const ShaderNodeObjects = ( objects ) => {
+
+	for ( const name in objects ) {
+
+		objects[ name ] = ShaderNodeObject( objects[ name ] );
+
+	}
+
+	return objects;
+
+};
+
+const ShaderNodeArray = ( array ) => {
 
 	const len = array.length;
 
@@ -84,29 +124,57 @@ export const ShaderNodeArray = ( array ) => {
 
 };
 
-export const ShaderNodeScript = ( jsFunc ) => {
+const ShaderNodeProxy = ( NodeClass, scope = null, factor = null ) => {
 
-	return ( ...params ) => {
+	if ( scope === null ) {
 
-		ShaderNodeArray( params );
+		return ( ...params ) => {
 
-		return ShaderNodeObject( jsFunc( ...params ) );
+			return ShaderNodeObject( new NodeClass( ...ShaderNodeArray( params ) ) );
 
-	};
+		};
+
+	} else if ( factor === null ) {
+
+		return ( ...params ) => {
+
+			return ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ) ) );
+
+		};
+
+	} else {
+
+		factor = ShaderNodeObject( factor );
+
+		return ( ...params ) => {
+
+			return ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ), factor ) );
+
+		};
+
+	}
 
 };
 
-export const ShaderNode = ( obj ) => {
+const ShaderNodeScript = function ( jsFunc ) {
+
+	return ( inputs, builder ) => {
+
+		ShaderNodeObjects( inputs );
 
-	return ShaderNodeScript( obj );
+		return ShaderNodeObject( jsFunc( inputs, builder ) );
+
+	};
 
 };
 
+export const ShaderNode = new Proxy( ShaderNodeScript, NodeHandler );
+
 //
 // Node Material Shader Syntax
 //
 
-export const uniform = ShaderNodeScript( ( inputNode ) => {
+export const uniform = new ShaderNode( ( inputNode ) => {
 
 	inputNode.setConst( false );
 
@@ -132,62 +200,111 @@ export const join = ( ...params ) => {
 
 };
 
-export const vec2 = ( ...params ) => {
+export const cond = ( ...params ) => {
 
-	return ShaderNodeObject( new Vector2Node( new Vector2( ...params ) ).setConst( true ) );
+	return ShaderNodeObject( new CondNode( ...ShaderNodeArray( params ) ) );
 
 };
 
-export const vec3 = ( ...params ) => {
-
-	return ShaderNodeObject( new Vector3Node( new Vector3( ...params ) ).setConst( true ) );
-
-};
+export const vec2 = ( ...params ) => {
 
-export const vec4 = ( ...params ) => {
+	// Providing one scalar value: This value is used for all components
 
-	return ShaderNodeObject( new Vector4Node( new Vector4( ...params ) ).setConst( true ) );
+	if ( params.length === 1 ) {
 
-};
+		params[ 1 ] = params[ 0 ];
 
-export const add = ( ...params ) => {
+	}
 
-	return ShaderNodeObject( new OperatorNode( '+', ...ShaderNodeArray( params ) ) );
+	return ShaderNodeObject( new Vector2Node( new Vector2( ...params ) ).setConst( true ) );
 
 };
 
-export const sub = ( ...params ) => {
-
-	return new OperatorNode( '-', ...ShaderNodeArray( params ) );
-
-};
+export const vec3 = ( ...params ) => {
 
-export const mul = ( ...params ) => {
+	// Providing one scalar value: This value is used for all components
 
-	return ShaderNodeObject( new OperatorNode( '*', ...ShaderNodeArray( params ) ) );
+	if ( params.length === 1 ) {
 
-};
+		params[ 1 ] = params[ 2 ] = params[ 0 ];
 
-export const div = ( ...params ) => {
+	}
 
-	return ShaderNodeObject( new OperatorNode( '/', ...ShaderNodeArray( params ) ) );
+	return ShaderNodeObject( new Vector3Node( new Vector3( ...params ) ).setConst( true ) );
 
 };
 
-export const floor = ( ...params ) => {
+export const vec4 = ( ...params ) => {
+
+	// Providing one scalar value: This value is used for all components
 
-	return ShaderNodeObject( new MathNode( 'floor', ...ShaderNodeArray( params ) ) );
+	if ( params.length === 1 ) {
 
-};
+		params[ 1 ] = params[ 2 ] = params[ 3 ] = params[ 0 ];
 
-export const mod = ( ...params ) => {
+	}
 
-	return ShaderNodeObject( new MathNode( 'mod', ...ShaderNodeArray( params ) ) );
+	return ShaderNodeObject( new Vector4Node( new Vector4( ...params ) ).setConst( true ) );
 
 };
 
-export const sign = ( ...params ) => {
+export const addTo = ( varNode, ...params ) => {
 
-	return ShaderNodeObject( new MathNode( 'sign', ...ShaderNodeArray( params ) ) );
+	varNode.node = add( varNode.node, ...ShaderNodeArray( params ) );
+
+	return ShaderNodeObject( varNode );
 
 };
+
+export const add = ShaderNodeProxy( OperatorNode, '+' );
+export const sub = ShaderNodeProxy( OperatorNode, '-' );
+export const mul = ShaderNodeProxy( OperatorNode, '*' );
+export const div = ShaderNodeProxy( OperatorNode, '/' );
+export const equal = ShaderNodeProxy( OperatorNode, '==' );
+export const assign = ShaderNodeProxy( OperatorNode, '=' );
+export const greaterThan = ShaderNodeProxy( OperatorNode, '>' );
+export const and = ShaderNodeProxy( OperatorNode, '&&' );
+
+export const element = ShaderNodeProxy( ArrayElementNode );
+
+export const normalLocal = new NormalNode( NormalNode.LOCAL );
+export const normalWorld = new NormalNode( NormalNode.WORLD );
+export const normalView = new NormalNode( NormalNode.VIEW );
+export const transformedNormalView = new VarNode( new NormalNode( NormalNode.VIEW ), 'TransformedNormalView', 'vec3' );
+
+export const positionLocal = new PositionNode( PositionNode.LOCAL );
+export const positionWorld = new PositionNode( PositionNode.WORLD );
+export const positionView = new PositionNode( PositionNode.VIEW );
+export const positionViewDirection = new PositionNode( PositionNode.VIEW_DIRECTION );
+
+export const PI = float( 3.141592653589793 );
+export const RECIPROCAL_PI = float( 0.3183098861837907 );
+export const EPSILON = float( 1e-6 );
+
+export const diffuseColor = new PropertyNode( 'DiffuseColor', 'vec4' );
+export const roughness = new PropertyNode( 'Roughness', 'float' );
+export const metalness = new PropertyNode( 'Metalness', 'float' );
+export const alphaTest = new PropertyNode( 'AlphaTest', 'float' );
+export const specularColor = new PropertyNode( 'SpecularColor', 'color' );
+
+export const negate = ShaderNodeProxy( MathNode, 'negate' );
+export const floor = ShaderNodeProxy( MathNode, 'floor' );
+export const mod = ShaderNodeProxy( MathNode, 'mod' );
+export const cross = ShaderNodeProxy( MathNode, 'cross' );
+export const max = ShaderNodeProxy( MathNode, 'max' );
+export const min = ShaderNodeProxy( MathNode, 'min' );
+export const dot = ShaderNodeProxy( MathNode, 'dot' );
+export const normalize = ShaderNodeProxy( MathNode, 'normalize' );
+export const sqrt = ShaderNodeProxy( MathNode, 'sqrt' );
+export const inversesqrt = ShaderNodeProxy( MathNode, 'inversesqrt' );
+export const sign = ShaderNodeProxy( MathNode, 'sign' );
+export const dFdx = ShaderNodeProxy( MathNode, 'dFdx' );
+export const dFdy = ShaderNodeProxy( MathNode, 'dFdy' );
+export const pow = ShaderNodeProxy( MathNode, 'pow' );
+export const pow2 = ShaderNodeProxy( MathNode, 'pow', 2 );
+export const pow3 = ShaderNodeProxy( MathNode, 'pow', 3 );
+export const pow4 = ShaderNodeProxy( MathNode, 'pow', 4 );
+export const exp = ShaderNodeProxy( MathNode, 'exp' );
+export const exp2 = ShaderNodeProxy( MathNode, 'exp2' );
+export const saturate = ShaderNodeProxy( MathNode, 'saturate' );
+export const transformDirection = ShaderNodeProxy( MathNode, 'transformDirection' );

+ 6 - 6
examples/jsm/renderers/nodes/accessors/MaterialNode.js

@@ -22,7 +22,7 @@ class MaterialNode extends Node {
 	getNodeType( builder ) {
 
 		const scope = this.scope;
-		const material = builder.getContextValue( 'material' );
+		const material = builder.context.material;
 
 		if ( scope === MaterialNode.COLOR ) {
 
@@ -46,7 +46,7 @@ class MaterialNode extends Node {
 
 	generate( builder, output ) {
 
-		const material = builder.getContextValue( 'material' );
+		const material = builder.context.material;
 		const scope = this.scope;
 
 		let node = null;
@@ -85,15 +85,15 @@ class MaterialNode extends Node {
 
 		} else if ( scope === MaterialNode.SPECULAR ) {
 
-			const specularTintNode = new MaterialReferenceNode( 'specularTint', 'color' );
+			const specularColorNode = new MaterialReferenceNode( 'specularColor', 'color' );
 
-			if ( material.specularTintMap !== null && material.specularTintMap !== undefined && material.specularTintMap.isTexture === true ) {
+			if ( material.specularColorMap !== null && material.specularColorMap !== undefined && material.specularColorMap.isTexture === true ) {
 
-				node = new OperatorNode( '*', specularTintNode, new MaterialReferenceNode( 'specularTintMap', 'texture' ) );
+				node = new OperatorNode( '*', specularColorNode, new MaterialReferenceNode( 'specularColorMap', 'texture' ) );
 
 			} else {
 
-				node = specularTintNode;
+				node = specularColorNode;
 
 			}
 

+ 5 - 5
examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js

@@ -12,16 +12,16 @@ class ModelViewProjectionNode extends Node {
 
 		this.position = position;
 
-		this._mvpMatrix = new OperatorNode( '*', new CameraNode( CameraNode.PROJECTION_MATRIX ), new ModelNode( ModelNode.VIEW_MATRIX ) );
-
 	}
 
 	generate( builder ) {
 
-		const mvpSnipped = this._mvpMatrix.build( builder );
-		const positionSnipped = this.position.build( builder, 'vec3' );
+		const position = this.position;
+
+		const mvpMatrix = new OperatorNode( '*', new CameraNode( CameraNode.PROJECTION_MATRIX ), new ModelNode( ModelNode.VIEW_MATRIX ) );
+		const mvpNode = new OperatorNode( '*', mvpMatrix, position );
 
-		return `( ${mvpSnipped} * vec4( ${positionSnipped}, 1.0 ) )`;
+		return mvpNode.build( builder );
 
 	}
 

+ 15 - 3
examples/jsm/renderers/nodes/accessors/NormalNode.js

@@ -1,14 +1,15 @@
 import Node from '../core/Node.js';
 import AttributeNode from '../core/AttributeNode.js';
+import NodeKeywords from '../core/NodeKeywords.js';
 import VaryNode from '../core/VaryNode.js';
 import ModelNode from '../accessors/ModelNode.js';
 import CameraNode from '../accessors/CameraNode.js';
 import OperatorNode from '../math/OperatorNode.js';
 import MathNode from '../math/MathNode.js';
-import { inverseTransformDirection } from '../functions/MathFunctions.js';
 
 class NormalNode extends Node {
 
+	static GEOMETRY = 'geometry';
 	static LOCAL = 'local';
 	static WORLD = 'world';
 	static VIEW = 'view';
@@ -21,16 +22,26 @@ class NormalNode extends Node {
 
 	}
 
+	getHash( /*builder*/ ) {
+
+		return `normal-${this.scope}`;
+
+	}
+
 	generate( builder ) {
 
 		const scope = this.scope;
 
 		let outputNode = null;
 
-		if ( scope === NormalNode.LOCAL ) {
+		if ( scope === NormalNode.GEOMETRY ) {
 
 			outputNode = new AttributeNode( 'normal', 'vec3' );
 
+		} else if ( scope === NormalNode.LOCAL ) {
+
+			outputNode = new VaryNode( new NormalNode( NormalNode.GEOMETRY ) );
+
 		} else if ( scope === NormalNode.VIEW ) {
 
 			const vertexNormalNode = new OperatorNode( '*', new ModelNode( ModelNode.NORMAL_MATRIX ), new NormalNode( NormalNode.LOCAL ) );
@@ -38,7 +49,8 @@ class NormalNode extends Node {
 
 		} else if ( scope === NormalNode.WORLD ) {
 
-			const vertexNormalNode = inverseTransformDirection.call( { dir: new NormalNode( NormalNode.VIEW ), matrix: new CameraNode( CameraNode.VIEW_MATRIX ) } );
+			// To use INVERSE_TRANSFORM_DIRECTION only inverse the param order like this: MathNode( ..., Vector, Matrix );
+			const vertexNormalNode = new MathNode( MathNode.TRANSFORM_DIRECTION, new NormalNode( NormalNode.VIEW ), new CameraNode( CameraNode.VIEW_MATRIX ) );
 			outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
 
 		}

+ 3 - 3
examples/jsm/renderers/nodes/accessors/PointUVNode.js

@@ -6,11 +6,9 @@ class PointUVNode extends Node {
 
 		super( 'vec2' );
 
-		Object.defineProperty( this, 'isPointUVNode', { value: true } );
-
 	}
 
-	generate( builder ) {
+	generate( /*builder*/ ) {
 
 		return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
 
@@ -18,4 +16,6 @@ class PointUVNode extends Node {
 
 }
 
+PointUVNode.prototype.isPointUVNode = true;
+
 export default PointUVNode;

+ 16 - 5
examples/jsm/renderers/nodes/accessors/PositionNode.js

@@ -1,13 +1,14 @@
 import Node from '../core/Node.js';
 import AttributeNode from '../core/AttributeNode.js';
+import NodeKeywords from '../core/NodeKeywords.js';
 import VaryNode from '../core/VaryNode.js';
 import ModelNode from '../accessors/ModelNode.js';
 import MathNode from '../math/MathNode.js';
 import OperatorNode from '../math/OperatorNode.js';
-import { transformDirection } from '../functions/MathFunctions.js';
 
 class PositionNode extends Node {
 
+	static GEOMETRY = 'geometry';
 	static LOCAL = 'local';
 	static WORLD = 'world';
 	static VIEW = 'view';
@@ -21,19 +22,29 @@ class PositionNode extends Node {
 
 	}
 
+	getHash( /*builder*/ ) {
+
+		return `position-${this.scope}`;
+
+	}
+
 	generate( builder ) {
 
 		const scope = this.scope;
 
 		let outputNode = null;
 
-		if ( scope === PositionNode.LOCAL ) {
-			
+		if ( scope === PositionNode.GEOMETRY ) {
+
 			outputNode = new AttributeNode( 'position', 'vec3' );
-			
+
+		} else if ( scope === PositionNode.LOCAL ) {
+
+			outputNode = new VaryNode( new PositionNode( PositionNode.GEOMETRY ) );
+
 		} else if ( scope === PositionNode.WORLD ) {
 
-			const vertexPositionNode = transformDirection.call( { dir: new PositionNode( PositionNode.LOCAL ), matrix: new ModelNode( ModelNode.WORLD_MATRIX ) } );
+			const vertexPositionNode = new MathNode( MathNode.TRANSFORM_DIRECTION, new ModelNode( ModelNode.WORLD_MATRIX ), new PositionNode( PositionNode.LOCAL ) );
 			outputNode = new VaryNode( vertexPositionNode );
 
 		} else if ( scope === PositionNode.VIEW ) {

+ 107 - 0
examples/jsm/renderers/nodes/accessors/SkinningNode.js

@@ -0,0 +1,107 @@
+import Node from '../core/Node.js';
+import AttributeNode from '../core/AttributeNode.js';
+import PositionNode from '../accessors/PositionNode.js';
+import NormalNode from '../accessors/NormalNode.js';
+import Matrix4Node from '../inputs/Matrix4Node.js';
+import BufferNode from '../inputs/BufferNode.js';
+
+import { ShaderNode, assign, element, add, mul, transformDirection } from '../ShaderNode.js';
+
+import { NodeUpdateType } from '../core/constants.js';
+
+const Skinning = new ShaderNode( ( inputs, builder ) => {
+
+	const { position, normal, index, weight, bindMatrix, bindMatrixInverse, boneMatrices } = inputs;
+
+	const boneMatX = element( boneMatrices, index.x );
+	const boneMatY = element( boneMatrices, index.y );
+	const boneMatZ = element( boneMatrices, index.z );
+	const boneMatW = element( boneMatrices, index.w );
+
+	// POSITION
+
+	const skinVertex = mul( bindMatrix, position );
+
+	const skinned = add(
+		mul( mul( boneMatX, skinVertex ), weight.x ),
+		mul( mul( boneMatY, skinVertex ), weight.y ),
+		mul( mul( boneMatZ, skinVertex ), weight.z ),
+		mul( mul( boneMatW, skinVertex ), weight.w )
+	);
+
+	const skinPosition = mul( bindMatrixInverse, skinned ).xyz;
+
+	// NORMAL
+
+	let skinMatrix = add(
+		mul( weight.x, boneMatX ),
+		mul( weight.y, boneMatY ),
+		mul( weight.z, boneMatZ ),
+		mul( weight.w, boneMatW )
+	);
+
+	skinMatrix = mul( mul( bindMatrixInverse, skinMatrix ), bindMatrix );
+
+	const skinNormal = transformDirection( skinMatrix, normal ).xyz;
+
+	// ASSIGNS
+
+	assign( position, skinPosition ).build( builder );
+	assign( normal, skinNormal ).build( builder );
+
+} );
+
+class SkinningNode extends Node {
+
+	constructor( skinnedMesh ) {
+
+		super( 'void' );
+
+		this.skinnedMesh = skinnedMesh;
+
+		this.updateType = NodeUpdateType.Object;
+
+		//
+
+		this.skinIndexNode = new AttributeNode( 'skinIndex', 'uvec4' );
+		this.skinWeightNode = new AttributeNode( 'skinWeight', 'vec4' );
+
+		this.bindMatrixNode = new Matrix4Node( skinnedMesh.bindMatrix );
+		this.bindMatrixInverseNode = new Matrix4Node( skinnedMesh.bindMatrixInverse );
+		this.boneMatricesNode = new BufferNode( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );
+
+	}
+
+	generate( builder ) {
+
+		// inout nodes
+		const position = new PositionNode( PositionNode.LOCAL );
+		const normal = new NormalNode( NormalNode.LOCAL );
+
+		const index = this.skinIndexNode;
+		const weight = this.skinWeightNode;
+		const bindMatrix = this.bindMatrixNode;
+		const bindMatrixInverse = this.bindMatrixInverseNode;
+		const boneMatrices = this.boneMatricesNode;
+
+		Skinning( {
+			position,
+			normal,
+			index,
+			weight,
+			bindMatrix,
+			bindMatrixInverse,
+			boneMatrices
+		}, builder );
+
+	}
+
+	update() {
+
+		this.skinnedMesh.skeleton.update();
+
+	}
+
+}
+
+export default SkinningNode;

+ 7 - 5
examples/jsm/renderers/nodes/accessors/UVNode.js

@@ -2,22 +2,24 @@ import AttributeNode from '../core/AttributeNode.js';
 
 class UVNode extends AttributeNode {
 
-	constructor( index = 0 ) {
+	constructor( value = 0 ) {
 
 		super( null, 'vec2' );
 
-		this.index = index;
-
-		Object.defineProperty( this, 'isUVNode', { value: true } );
+		this.value = value;
 
 	}
 
 	getAttributeName( /*builder*/ ) {
 
-		return 'uv' + ( this.index > 0 ? this.index + 1 : '' );
+		const value = this.value;
+
+		return 'uv' + ( value > 0 ? value + 1 : '' );
 
 	}
 
 }
 
+UVNode.prototype.isUVNode = true;
+
 export default UVNode;

+ 0 - 7
examples/jsm/renderers/nodes/consts/MathConsts.js

@@ -1,7 +0,0 @@
-import ConstNode from '../core/ConstNode.js';
-
-export const PI = new ConstNode( '3.141592653589793', 'float', 'PI' );
-export const RECIPROCAL_PI = new ConstNode( '0.3183098861837907', 'float', 'RECIPROCAL_PI' );
-export const EPSILON = new ConstNode( '1e-6', 'float', 'EPSILON' );
-
-export const DEFAULT_SPECULAR_COEFFICIENT = new ConstNode( '0.04', 'float', 'DEFAULT_SPECULAR_COEFFICIENT' );

+ 3 - 3
examples/jsm/renderers/nodes/core/ArrayInputNode.js

@@ -2,17 +2,17 @@ import InputNode from './InputNode.js';
 
 class ArrayInputNode extends InputNode {
 
-	constructor( value = [] ) {
+	constructor( nodes = [] ) {
 
 		super();
 
-		this.value = value;
+		this.nodes = nodes;
 
 	}
 
 	getNodeType( builder ) {
 
-		return this.value[ 0 ].getNodeType( builder );
+		return this.nodes[ 0 ].getNodeType( builder );
 
 	}
 

+ 6 - 0
examples/jsm/renderers/nodes/core/AttributeNode.js

@@ -11,6 +11,12 @@ class AttributeNode extends Node {
 
 	}
 
+	getHash( builder ) {
+
+		return this.getAttributeName( builder );
+
+	}
+
 	setAttributeName( attributeName ) {
 
 		this._attributeName = attributeName;

+ 38 - 0
examples/jsm/renderers/nodes/core/BypassNode.js

@@ -0,0 +1,38 @@
+import Node from './Node.js';
+
+class BypassNode extends Node {
+
+	constructor( returnNode, callNode ) {
+
+		super();
+
+		this.outputNode = returnNode;
+		this.callNode = callNode;
+
+	}
+
+	getNodeType( builder ) {
+
+		return this.outputNode.getNodeType( builder );
+
+	}
+
+	generate( builder, output ) {
+
+		const snippet = this.callNode.build( builder, 'void' );
+
+		if ( snippet !== '' ) {
+
+			builder.addFlowCode( snippet );
+
+		}
+
+		return this.outputNode.build( builder, output );
+
+	}
+
+}
+
+BypassNode.prototype.isBypassNode = true;
+
+export default BypassNode;

+ 3 - 3
examples/jsm/renderers/nodes/core/CodeNode.js

@@ -12,8 +12,6 @@ class CodeNode extends Node {
 
 		this._includes = [];
 
-		Object.defineProperty( this, 'isCodeNode', { value: true } );
-
 	}
 
 	setIncludes( includes ) {
@@ -34,7 +32,7 @@ class CodeNode extends Node {
 
 		if ( this.useKeywords === true ) {
 
-			const contextKeywords = builder.getContextValue( 'keywords' );
+			const contextKeywords = builder.context.keywords;
 
 			if ( contextKeywords !== undefined ) {
 
@@ -75,4 +73,6 @@ class CodeNode extends Node {
 
 }
 
+CodeNode.prototype.isCodeNode = true;
+
 export default CodeNode;

+ 0 - 39
examples/jsm/renderers/nodes/core/ConstNode.js

@@ -1,39 +0,0 @@
-import CodeNode from './CodeNode.js';
-
-class ConstNode extends CodeNode {
-
-	constructor( code = '', type = '', name = '' ) {
-
-		super( code, type );
-
-		this.includes = [];
-
-		this.name = name;
-
-	}
-
-	generate( builder ) {
-
-		const code = super.generate( builder );
-
-		const nodeCode = builder.getCodeFromNode( this, this.getNodeType( builder ) );
-
-		if ( this.name !== '' ) {
-
-			// use a custom property name
-
-			nodeCode.name = this.name;
-
-		}
-
-		const propertyName = builder.getPropertyName( nodeCode );
-
-		nodeCode.code = `#define ${propertyName} ${code}`;
-
-		return propertyName;
-
-	}
-
-}
-
-export default ConstNode;

+ 4 - 19
examples/jsm/renderers/nodes/core/ContextNode.js

@@ -2,30 +2,13 @@ import Node from './Node.js';
 
 class ContextNode extends Node {
 
-	constructor( node, nodeType, context = {} ) {
+	constructor( node, context = {} ) {
 
-		super( nodeType );
+		super();
 
 		this.node = node;
-
 		this.context = context;
 
-		Object.defineProperty( this, 'isContextNode', { value: true } );
-
-	}
-
-	setContextValue( name, value ) {
-
-		this.context[ name ] = value;
-
-		return this;
-
-	}
-
-	getContextValue( name ) {
-
-		return this.context[ name ];
-
 	}
 
 	getNodeType( builder ) {
@@ -50,4 +33,6 @@ class ContextNode extends Node {
 
 }
 
+ContextNode.prototype.isContextNode = true;
+
 export default ContextNode;

+ 13 - 2
examples/jsm/renderers/nodes/core/ExpressionNode.js

@@ -2,7 +2,7 @@ import TempNode from './TempNode.js';
 
 class ExpressionNode extends TempNode {
 
-	constructor( snipped = '', nodeType = null ) {
+	constructor( snipped = '', nodeType = 'void' ) {
 
 		super( nodeType );
 
@@ -12,7 +12,18 @@ class ExpressionNode extends TempNode {
 
 	generate( builder ) {
 
-		return `( ${ this.snipped } )`;
+		const type = this.getNodeType( builder );
+		const snipped = this.snipped;
+
+		if ( type === 'void' ) {
+
+			builder.addFlowCode( snipped );
+
+		} else {
+
+			return `( ${ snipped } )`;
+
+		}
 
 	}
 

+ 19 - 132
examples/jsm/renderers/nodes/core/FunctionNode.js

@@ -1,143 +1,43 @@
 import CodeNode from './CodeNode.js';
-import NodeFunctionInput from './NodeFunctionInput.js';
 import FunctionCallNode from './FunctionCallNode.js';
 
-const declarationRegexp = /^\s*(highp|mediump|lowp)?\s*([a-z_0-9]+)\s*([a-z_0-9]+)?\s*\((.*?)\)/i;
-const propertiesRegexp = /[a-z_0-9]+/ig;
-
-const pragmaMain = '#pragma main';
-
 class FunctionNode extends CodeNode {
 
 	constructor( code = '' ) {
 
 		super( code );
 
-		this.inputs = [];
-
-		this.name = '';
-		this.needsUpdate = true;
-
 		this.useKeywords = true;
 
-		this.presicion = '';
-
-		this._includeCode = '';
-		this._internalCode = '';
-
 	}
 
 	getNodeType( builder ) {
 
-		if ( this.needsUpdate === true ) {
-
-			this.parse();
-
-		}
-
-		return super.getNodeType( builder );
+		return this.getNodeFunction( builder ).type;
 
 	}
 
-	getInputs( /*builder*/ ) {
-
-		if ( this.needsUpdate === true ) {
-
-			this.parse();
+	getInputs( builder ) {
 
-		}
-
-		return this.inputs;
+		return this.getNodeFunction( builder ).inputs;
 
 	}
 
-	parse() {
-
-		const code = this.code;
-
-		const pragmaMainIndex = code.indexOf( pragmaMain );
-
-		const mainCode = pragmaMainIndex !== - 1 ? code.substr( pragmaMainIndex + pragmaMain.length ) : code;
-
-		const declaration = mainCode.match( declarationRegexp );
-
-		if ( declaration !== null && declaration.length === 5 ) {
-
-			// tokenizer
-
-			const paramsCode = declaration[ 4 ];
-			const propsMatches = [];
-
-			let nameMatch = null;
-
-			while ( ( nameMatch = propertiesRegexp.exec( paramsCode ) ) !== null ) {
-
-				propsMatches.push( nameMatch );
-
-			}
-
-			// parser
-
-			const inputs = [];
-
-			let i = 0;
-
-			while ( i < propsMatches.length ) {
-
-				const isConst = propsMatches[ i ][ 0 ] === 'const';
-
-				if ( isConst === true ) {
+	getNodeFunction( builder ) {
 
-					i ++;
+		const nodeData = builder.getDataFromNode( this );
 
-				}
+		let nodeFunction = nodeData.nodeFunction;
 
-				let qualifier = propsMatches[ i ][ 0 ];
+		if ( nodeFunction === undefined ) {
 
-				if ( qualifier === 'in' || qualifier === 'out' || qualifier === 'inout' ) {
+			nodeFunction = builder.parser.parseFunction( this.code );
 
-					i ++;
-
-				} else {
-
-					qualifier = '';
-
-				}
-
-				const type = propsMatches[ i ++ ][ 0 ];
-
-				let count = Number.parseInt( propsMatches[ i ][ 0 ] );
-
-				if ( Number.isNaN( count ) === false ) i ++;
-				else count = 0;
-
-				const name = propsMatches[ i ++ ][ 0 ];
-
-				inputs.push( new NodeFunctionInput( type, name, qualifier, isConst, count ) );
-
-			}
-
-			const blockCode = mainCode.substring( declaration[ 0 ].length );
-
-			this.name = declaration[ 3 ] !== undefined ? declaration[ 3 ] : '';
-			this.nodeType = declaration[ 2 ];
-
-			this.presicion = declaration[ 1 ] !== undefined ? declaration[ 1 ] : '';
-
-			this.inputs = inputs;
-
-			this._includeCode = pragmaMainIndex !== - 1 ? code.substr( 0, pragmaMainIndex ) : '';
-			this._internalCode = `( ${paramsCode} ) ${blockCode}`;
-
-		} else {
-
-			throw new Error( 'FunctionNode: Function is not a GLSL code.' );
+			nodeData.nodeFunction = nodeFunction;
 
 		}
 
-		this.code = code;
-
-		this.needsUpdate = false;
+		return nodeFunction;
 
 	}
 
@@ -151,37 +51,24 @@ class FunctionNode extends CodeNode {
 
 		super.generate( builder );
 
-		const type = this.getNodeType( builder );
+		const nodeFunction = this.getNodeFunction( builder );
+
+		const name = nodeFunction.name;
+		const type = nodeFunction.type;
+
 		const nodeCode = builder.getCodeFromNode( this, type );
 
-		if ( this.name !== '' ) {
+		if ( name !== '' ) {
 
 			// use a custom property name
 
-			nodeCode.name = this.name;
+			nodeCode.name = name;
 
 		}
 
 		const propertyName = builder.getPropertyName( nodeCode );
 
-		const presicion = this.presicion;
-		const includeCode = this._includeCode;
-
-		let code = `${type} ${propertyName} ${this._internalCode}`;
-
-		if ( presicion !== '' ) {
-
-			code = `${presicion} ${code}`;
-
-		}
-
-		if ( includeCode !== '' ) {
-
-			code = `${includeCode} ${code}`;
-
-		}
-
-		nodeCode.code = code;
+		nodeCode.code = this.getNodeFunction( builder ).getCode( propertyName );
 
 		if ( output === 'property' ) {
 
@@ -189,7 +76,7 @@ class FunctionNode extends CodeNode {
 
 		} else {
 
-			return builder.format( `${propertyName}()`, type, output );
+			return builder.format( `${ propertyName }()`, type, output );
 
 		}
 

+ 13 - 3
examples/jsm/renderers/nodes/core/InputNode.js

@@ -2,9 +2,11 @@ import Node from './Node.js';
 
 class InputNode extends Node {
 
-	constructor( nodeType ) {
+	constructor( inputType ) {
 
-		super( nodeType );
+		super( inputType );
+
+		this.inputType = inputType;
 
 		this.constant = false;
 
@@ -24,6 +26,12 @@ class InputNode extends Node {
 
 	}
 
+	getInputType( builder ) {
+
+		return this.inputType;
+
+	}
+
 	generateConst( builder ) {
 
 		return builder.getConst( this.getNodeType( builder ), this.value );
@@ -40,7 +48,9 @@ class InputNode extends Node {
 
 		} else {
 
-			const nodeUniform = builder.getUniformFromNode( this, builder.shaderStage, type );
+			const inputType = this.getInputType( builder );
+
+			const nodeUniform = builder.getUniformFromNode( this, builder.shaderStage, inputType );
 			const propertyName = builder.getPropertyName( nodeUniform );
 
 			return builder.format( propertyName, type, output );

+ 32 - 10
examples/jsm/renderers/nodes/core/Node.js

@@ -1,5 +1,7 @@
 import { NodeUpdateType } from './constants.js';
 
+import { MathUtils } from 'three';
+
 class Node {
 
 	constructor( nodeType = null ) {
@@ -8,6 +10,8 @@ class Node {
 
 		this.updateType = NodeUpdateType.None;
 
+		this.uuid = MathUtils.generateUUID();
+
 	}
 
 	get type() {
@@ -16,21 +20,21 @@ class Node {
 
 	}
 
-	getUpdateType( /*builder*/ ) {
+	getHash( /*builder*/ ) {
 
-		return this.updateType;
+		return this.uuid;
 
 	}
 
-	getNodeType( /*builder*/ ) {
+	getUpdateType( /*builder*/ ) {
 
-		return this.nodeType;
+		return this.updateType;
 
 	}
 
-	getTypeLength( builder ) {
+	getNodeType( /*builder*/ ) {
 
-		return builder.getTypeLength( this.getNodeType( builder ) );
+		return this.nodeType;
 
 	}
 
@@ -48,30 +52,48 @@ class Node {
 
 	build( builder, output = null ) {
 
+		const hash = this.getHash( builder );
+		const sharedNode = builder.getNodeFromHash( hash );
+
+		if ( sharedNode !== undefined && this !== sharedNode ) {
+
+			return sharedNode.build( builder, output );
+
+		}
+
 		builder.addNode( this );
+		builder.addStack( this );
 
 		const isGenerateOnce = this.generate.length === 1;
 
+		let snippet = null;
+
 		if ( isGenerateOnce ) {
 
 			const type = this.getNodeType( builder );
 			const nodeData = builder.getDataFromNode( this );
 
-			let snippet = nodeData.snippet;
+			snippet = nodeData.snippet;
 
 			if ( snippet === undefined ) {
 
-				snippet = this.generate( builder );
+				snippet = this.generate( builder ) || '';
 
 				nodeData.snippet = snippet;
 
 			}
 
-			return builder.format( snippet, type, output );
+			snippet = builder.format( snippet, type, output );
+
+		} else {
+
+			snippet = this.generate( builder, output ) || '';
 
 		}
 
-		return this.generate( builder, output );
+		builder.removeStack( this );
+
+		return snippet;
 
 	}
 

+ 143 - 124
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -6,39 +6,71 @@ import NodeCode from './NodeCode.js';
 import NodeKeywords from './NodeKeywords.js';
 import { NodeUpdateType } from './constants.js';
 
-import { LinearEncoding } from 'three';
+import { REVISION, LinearEncoding } from 'three';
+
+const shaderStages = [ 'fragment', 'vertex' ];
 
 class NodeBuilder {
 
-	constructor( material, renderer ) {
+	constructor( object, renderer, parser ) {
 
-		this.material = material;
+		this.object = object;
+		this.material = object.material;
 		this.renderer = renderer;
+		this.parser = parser;
 
 		this.nodes = [];
 		this.updateNodes = [];
+		this.hashNodes = {};
 
 		this.vertexShader = null;
 		this.fragmentShader = null;
 
-		this.slots = { vertex: [], fragment: [] };
-		this.defines = { vertex: {}, fragment: {} };
+		this.flowNodes = { vertex: [], fragment: [] };
+		this.flowCode = { vertex: '', fragment: '' };
 		this.uniforms = { vertex: [], fragment: [], index: 0 };
 		this.codes = { vertex: [], fragment: [] };
 		this.attributes = [];
 		this.varys = [];
 		this.vars = { vertex: [], fragment: [] };
 		this.flow = { code: '' };
+		this.stack = [];
 
 		this.context = {
 			keywords: new NodeKeywords(),
-			material: material
+			material: object.material
 		};
 
 		this.nodesData = new WeakMap();
+		this.flowsData = new WeakMap();
 
 		this.shaderStage = null;
-		this.slot = null;
+		this.node = null;
+
+	}
+
+	addStack( node ) {
+
+		/*
+		if ( this.stack.indexOf( node ) !== - 1 ) {
+
+			console.warn( 'Recursive node: ', node );
+
+		}
+*/
+		this.stack.push( node );
+
+	}
+
+	removeStack( node ) {
+
+		const lastStack = this.stack.pop();
+
+		if ( lastStack !== node ) {
+
+			throw new Error( 'NodeBuilder: Invalid node stack!' );
+
+		}
 
 	}
 
@@ -56,37 +88,41 @@ class NodeBuilder {
 
 			this.nodes.push( node );
 
+			this.hashNodes[ node.getHash( this ) ] = node;
+
 		}
 
 	}
 
-	addSlot( shaderStage, slot ) {
+	getMethod( method ) {
 
-		this.slots[ shaderStage ].push( slot );
+		return method;
 
 	}
 
-	define( shaderStage, name, value = '' ) {
+	getNodeFromHash( hash ) {
 
-		this.defines[ shaderStage ][ name ] = value;
+		return this.hashNodes[ hash ];
 
 	}
 
-	setContext( context ) {
+	addFlow( shaderStage, node ) {
 
-		this.context = context;
+		this.flowNodes[ shaderStage ].push( node );
+
+		return node;
 
 	}
 
-	getContext() {
+	setContext( context ) {
 
-		return this.context;
+		this.context = context;
 
 	}
 
-	getContextValue( name ) {
+	getContext() {
 
-		return this.context[ name ];
+		return this.context;
 
 	}
 
@@ -102,18 +138,31 @@ class NodeBuilder {
 
 	}
 
+	// rename to generate
 	getConst( type, value ) {
 
 		if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
-		if ( type === 'vec2' ) return `vec2( ${value.x}, ${value.y} )`;
-		if ( type === 'vec3' ) return `vec3( ${value.x}, ${value.y}, ${value.z} )`;
-		if ( type === 'vec4' ) return `vec4( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
-		if ( type === 'color' ) return `vec3( ${value.r}, ${value.g}, ${value.b} )`;
+		if ( type === 'vec2' ) return `${ this.getType( 'vec2' ) }( ${value.x}, ${value.y} )`;
+		if ( type === 'vec3' ) return `${ this.getType( 'vec3' ) }( ${value.x}, ${value.y}, ${value.z} )`;
+		if ( type === 'vec4' ) return `${ this.getType( 'vec4' ) }( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
+		if ( type === 'color' ) return `${ this.getType( 'vec3' ) }( ${value.r}, ${value.g}, ${value.b} )`;
 
 		throw new Error( `NodeBuilder: Type '${type}' not found in generate constant attempt.` );
 
 	}
 
+	getType( type ) {
+
+		return type;
+
+	}
+
+	generateMethod( method ) {
+
+		return method;
+
+	}
+
 	getAttribute( name, type ) {
 
 		const attributes = this.attributes;
@@ -140,7 +189,7 @@ class NodeBuilder {
 
 	}
 
-	getPropertyName( node ) {
+	getPropertyName( node/*, shaderStage*/ ) {
 
 		return node.name;
 
@@ -334,30 +383,33 @@ class NodeBuilder {
 
 	addFlowCode( code ) {
 
-		if ( ! /;\s*$/.test( code ) ) {
+		this.flow.code += code;
 
-			code += ';';
+	}
 
-		}
+	getFlowData( shaderStage, node ) {
 
-		this.flow.code += code + ' ';
+		return this.flowsData.get( node );
 
 	}
 
-	flowSlot( slot, shaderStage = this.shaderStage ) {
+	flowNode( node ) {
+
+		this.node = node;
 
-		this.slot = slot;
+		const output = node.getNodeType( this );
 
-		const flowData = this.flowNode( slot.node, slot.output );
+		const flowData = this.flowChildNode( node, output );
 
-		this.define( shaderStage, `NODE_CODE_${slot.name}`, flowData.code );
-		this.define( shaderStage, `NODE_${slot.name}`, flowData.result );
+		this.flowsData.set( node, flowData );
 
-		this.slot = null;
+		this.node = null;
+
+		return flowData;
 
 	}
 
-	flowNode( node, output = null ) {
+	flowChildNode( node, output = null ) {
 
 		const previousFlow = this.flow;
 
@@ -381,17 +433,15 @@ class NodeBuilder {
 
 		this.setShaderStage( shaderStage );
 
-		const flowData = this.flowNode( node, output );
+		const flowData = this.flowChildNode( node, output );
 
 		if ( propertyName !== null ) {
 
-			flowData.code += `${propertyName} = ${flowData.result}; `;
+			flowData.code += `${propertyName} = ${flowData.result};\n\t`;
 
 		}
 
-		const shaderStageCode = this.defines[ shaderStage ][ 'NODE_CODE' ] + flowData.code;
-
-		this.define( shaderStage, 'NODE_CODE', shaderStageCode );
+		this.flowCode[ shaderStage ] = this.flowCode[ shaderStage ] + flowData.code;
 
 		this.setShaderStage( previousShaderStage );
 
@@ -399,22 +449,6 @@ class NodeBuilder {
 
 	}
 
-	getDefines( shaderStage ) {
-
-		const defines = this.defines[ shaderStage ];
-
-		let code = '';
-
-		for ( const name in defines ) {
-
-			code += `#define ${name} ${defines[ name ]}\n`;
-
-		}
-
-		return code;
-
-	}
-
 	getAttributes( shaderStage ) {
 
 		let snippet = '';
@@ -501,68 +535,37 @@ class NodeBuilder {
 
 	}
 
-	build() {
-
-		const shaderStages = [ 'vertex', 'fragment' ];
-		const shaderData = {};
-
-		for ( const shaderStage of shaderStages ) {
+	buildCode() {
 
-			this.setShaderStage( shaderStage );
+		console.warn( 'Abstract function.' );
 
-			this.define( shaderStage, 'NODE_CODE', '' );
+	}
 
-			const slots = this.slots[ shaderStage ];
+	build() {
 
-			for ( const slot of slots ) {
+		if ( this.context.vertex && this.context.vertex.isNode ) {
 
-				this.flowSlot( slot, shaderStage );
-
-			}
+			this.flowNodeFromShaderStage( 'vertex', this.context.vertex );
 
 		}
 
-		this.setShaderStage( null );
-
 		for ( const shaderStage of shaderStages ) {
 
-			const defines = this.getDefines( shaderStage );
-			const uniforms = this.getUniforms( shaderStage );
-			const attributes = this.getAttributes( shaderStage );
-			const varys = this.getVarys( shaderStage );
-			const vars = this.getVars( shaderStage );
-			const codes = this.getCodes( shaderStage );
-
-			shaderData[ shaderStage ] = `
-				// <node_builder>
-
-				#define NODE_MATERIAL
-
-				// defines
-				${defines}
-
-				// uniforms
-				${uniforms}
-
-				// attributes
-				${attributes}
+			this.setShaderStage( shaderStage );
 
-				// varys
-				${varys}
+			const flowNodes = this.flowNodes[ shaderStage ];
 
-				// vars
-				${vars}
+			for ( const node of flowNodes ) {
 
-				// codes
-				${codes}
+				this.flowNode( node, shaderStage );
 
-				// </node_builder>
-				`;
+			}
 
 		}
 
-		this.vertexShader = shaderData.vertex;
-		this.fragmentShader = shaderData.fragment;
+		this.setShaderStage( null );
+
+		this.buildCode();
 
 		return this;
 
@@ -577,31 +580,42 @@ class NodeBuilder {
 
 		switch ( typeToType ) {
 
-			case 'float to vec2' : return `vec2( ${snippet} )`;
-			case 'float to vec3' : return `vec3( ${snippet} )`;
-			case 'float to vec4' : return `vec4( vec3( ${snippet} ), 1.0 )`;
-
-			case 'vec2 to float' : return `${snippet}.x`;
-			case 'vec2 to vec3'  : return `vec3( ${snippet}, 0.0 )`;
-			case 'vec2 to vec4'  : return `vec4( ${snippet}.xy, 0.0, 1.0 )`;
-
-			case 'vec3 to float' : return `${snippet}.x`;
-			case 'vec3 to vec2'  : return `${snippet}.xy`;
-			case 'vec3 to vec4'  : return `vec4( ${snippet}, 1.0 )`;
-
-			case 'vec4 to float' : return `${snippet}.x`;
-			case 'vec4 to vec2'  : return `${snippet}.xy`;
-			case 'vec4 to vec3'  : return `${snippet}.xyz`;
-
-			case 'mat3 to float' : return `( ${snippet} * vec3( 1.0 ) ).x`;
-			case 'mat3 to vec2'  : return `( ${snippet} * vec3( 1.0 ) ).xy`;
-			case 'mat3 to vec3'  : return `( ${snippet} * vec3( 1.0 ) ).xyz`;
-			case 'mat3 to vec4'  : return `vec4( ${snippet} * vec3( 1.0 ), 1.0 )`;
-
-			case 'mat4 to float' : return `( ${snippet} * vec4( 1.0 ) ).x`;
-			case 'mat4 to vec2'  : return `( ${snippet} * vec4( 1.0 ) ).xy`;
-			case 'mat4 to vec3'  : return `( ${snippet} * vec4( 1.0 ) ).xyz`;
-			case 'mat4 to vec4'  : return `( ${snippet} * vec4( 1.0 ) )`;
+			case 'int to float' : return `${ this.getType( 'float' ) }( ${ snippet } )`;
+			case 'int to vec2' : return `${ this.getType( 'vec2' ) }( ${ this.getType( 'float' ) }( ${ snippet } ) )`;
+			case 'int to vec3' : return `${ this.getType( 'vec3' ) }( ${ this.getType( 'float' ) }( ${ snippet } ) )`;
+			case 'int to vec4' : return `${ this.getType( 'vec4' ) }( ${ this.getType( 'vec3' ) }( ${ this.getType( 'float' ) }( ${ snippet } ) ), 1.0 )`;
+
+			case 'float to int' : return `${ this.getType( 'int' ) }( ${ snippet } )`;
+			case 'float to vec2' : return `${ this.getType( 'vec2' ) }( ${ snippet } )`;
+			case 'float to vec3' : return `${ this.getType( 'vec3' ) }( ${ snippet } )`;
+			case 'float to vec4' : return `${ this.getType( 'vec4' ) }( ${ this.getType( 'vec3' ) }( ${ snippet } ), 1.0 )`;
+
+			case 'vec2 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
+			case 'vec2 to float' : return `${ snippet }.x`;
+			case 'vec2 to vec3' : return `${ this.getType( 'vec3' ) }( ${ snippet }, 0.0 )`;
+			case 'vec2 to vec4' : return `${ this.getType( 'vec4' ) }( ${ snippet }.xy, 0.0, 1.0 )`;
+
+			case 'vec3 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
+			case 'vec3 to float' : return `${ snippet }.x`;
+			case 'vec3 to vec2' : return `${ snippet }.xy`;
+			case 'vec3 to vec4' : return `${ this.getType( 'vec4' ) }( ${ snippet }, 1.0 )`;
+
+			case 'vec4 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
+			case 'vec4 to float' : return `${ snippet }.x`;
+			case 'vec4 to vec2' : return `${ snippet }.xy`;
+			case 'vec4 to vec3' : return `${ snippet }.xyz`;
+
+			case 'mat3 to int' : return `${ this.getType( 'int' ) }( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).x`;
+			case 'mat3 to float' : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).x`;
+			case 'mat3 to vec2' : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xy`;
+			case 'mat3 to vec3' : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xyz`;
+			case 'mat3 to vec4' : return `${ this.getType( 'vec4' ) }( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ), 1.0 )`;
+
+			case 'mat4 to int' : return `${ this.getType( 'int' ) }( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).x`;
+			case 'mat4 to float' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).x`;
+			case 'mat4 to vec2' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xy`;
+			case 'mat4 to vec3' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xyz`;
+			case 'mat4 to vec4' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) )`;
 
 		}
 
@@ -609,6 +623,11 @@ class NodeBuilder {
 
 	}
 
+	getSignature() {
+
+		return `// Three.js r${ REVISION } • NodeMaterial System\n`;
+
+	}
 
 }
 

+ 22 - 0
examples/jsm/renderers/nodes/core/NodeFunction.js

@@ -0,0 +1,22 @@
+class NodeFunction {
+
+	constructor( type, inputs, name = '', presicion = '' ) {
+
+		this.type = type;
+		this.inputs = inputs;
+		this.name = name;
+		this.presicion = presicion;
+
+	}
+
+	getCode( /*name = this.name*/ ) {
+
+		console.warn( 'Abstract function.' );
+
+	}
+
+}
+
+NodeFunction.isNodeFunction = true;
+
+export default NodeFunction;

+ 4 - 4
examples/jsm/renderers/nodes/core/NodeFunctionInput.js

@@ -1,17 +1,17 @@
 class NodeFunctionInput {
 
-	constructor( type, name, qualifier = '', isConst = false, count = 0 ) {
+	constructor( type, name, count = null, qualifier = '', isConst = false ) {
 
 		this.type = type;
 		this.name = name;
+		this.count = count;
 		this.qualifier = qualifier;
 		this.isConst = isConst;
-		this.count = count;
-
-		Object.defineProperty( this, 'isNodeFunction', { value: true } );
 
 	}
 
 }
 
+NodeFunctionInput.isNodeFunctionInput = true;
+
 export default NodeFunctionInput;

+ 18 - 179
examples/jsm/renderers/nodes/core/NodeKeywords.js

@@ -1,75 +1,10 @@
-import VarNode from './VarNode.js';
-import PropertyNode from './PropertyNode.js';
-import PositionNode from '../accessors/PositionNode.js';
-import NormalNode from '../accessors/NormalNode.js';
-
-import { PI, RECIPROCAL_PI, EPSILON } from '../consts/MathConsts.js';
-import { saturateMacro, whiteComplementMacro } from '../functions/MathFunctions.js';
-
 class NodeKeywords {
 
-	static PI = 'PI';
-	static RECIPROCAL_PI = 'RECIPROCAL_PI';
-	static EPSILON = 'EPSILON';
-
-	static Saturate = 'saturate';
-	static WhiteComplement = 'whiteComplement';
-
-	static PositionLocal = 'PositionLocal';
-	static PositionWorld = 'PositionWorld';
-	static PositionView = 'PositionView';
-	static PositionViewDirection = 'PositionViewDirection';
-
-	static NormalLocal = 'NormalLocal';
-	static NormalWorld = 'NormalWorld';
-	static NormalView = 'NormalView';
-
-	static Irradiance = 'Irradiance';
-	static ReflectedLightIndirectDiffuse = 'ReflectedLightIndirectDiffuse';
-	static ReflectedLightIndirectSpecular = 'ReflectedLightIndirectSpecular';
-	static ReflectedLightDirectDiffuse = 'ReflectedLightDirectDiffuse';
-	static ReflectedLightDirectSpecular = 'ReflectedLightDirectSpecular';
-
-	static MaterialDiffuseColor = 'MaterialDiffuseColor';
-
-	// STANDARD
-	static MaterialRoughness = 'MaterialRoughness';
-	static MaterialMetalness = 'MaterialMetalness';
-	static MaterialSpecularTint = 'MaterialSpecularTint';
-
 	constructor() {
 
-		this.keywords = [
-			// consts
-			NodeKeywords.PI,
-			NodeKeywords.RECIPROCAL_PI,
-			NodeKeywords.EPSILON,
-			// variadic macros
-			NodeKeywords.Saturate,
-			NodeKeywords.WhiteComplement,
-			// nodes
-			NodeKeywords.PositionLocal,
-			NodeKeywords.PositionWorld,
-			NodeKeywords.PositionView,
-			NodeKeywords.PositionViewDirection,
-			NodeKeywords.NormalLocal,
-			NodeKeywords.NormalWorld,
-			NodeKeywords.NormalView,
-			// vars -> float
-			NodeKeywords.MaterialRoughness,
-			NodeKeywords.MaterialMetalness,
-			// vars -> vec3
-			NodeKeywords.Irradiance,
-			NodeKeywords.ReflectedLightIndirectDiffuse,
-			NodeKeywords.ReflectedLightIndirectSpecular,
-			NodeKeywords.ReflectedLightDirectDiffuse,
-			NodeKeywords.ReflectedLightDirectSpecular,
-			NodeKeywords.MaterialSpecularTint,
-			// vars -> vec4
-			NodeKeywords.MaterialDiffuseColor
-		];
-
+		this.keywords = [];
 		this.nodes = [];
+		this.keywordsCallback = {};
 
 	}
 
@@ -77,120 +12,24 @@ class NodeKeywords {
 
 		let node = this.nodes[ name ];
 
-		if ( node === undefined ) {
-
-			switch ( name ) {
-
-				case NodeKeywords.PI:
-
-					node = PI;
-
-					break;
-
-				case NodeKeywords.RECIPROCAL_PI:
-
-					node = RECIPROCAL_PI;
-
-					break;
-
-				case NodeKeywords.EPSILON:
-
-					node = EPSILON;
-
-					break;
-
-				case NodeKeywords.Saturate:
-
-					node = saturateMacro;
-
-					break;
-
-				case NodeKeywords.WhiteComplement:
-
-					node = whiteComplementMacro;
-
-					break;
-
-				case NodeKeywords.PositionLocal:
-
-					node = new VarNode( new PositionNode( PositionNode.LOCAL ), name );
-
-					break;
-
-				case NodeKeywords.PositionWorld:
-
-					node = new VarNode( new PositionNode( PositionNode.WORLD ), name );
+		if ( node === undefined && this.keywordsCallback[ name ] !== undefined ) {
 
-					break;
+			node = this.keywordsCallback[ name ]( name );
 
-				case NodeKeywords.PositionView:
+			this.nodes[ name ] = node;
 
-					node = new VarNode( new PositionNode( PositionNode.VIEW ), name );
-
-					break;
-
-				case NodeKeywords.PositionViewDirection:
-
-					node = new VarNode( new PositionNode( PositionNode.VIEW_DIRECTION ), name );
-
-					break;
-
-				case NodeKeywords.NormalLocal:
-
-					node = new VarNode( new NormalNode( NormalNode.LOCAL ), name );
-
-					break;
-
-				case NodeKeywords.NormalWorld:
-
-					node = new VarNode( new NormalNode( NormalNode.WORLD ), name );
-
-					break;
-
-				case NodeKeywords.NormalView:
-
-					node = new VarNode( new NormalNode( NormalNode.VIEW ), name );
-
-					break;
-
-				// floats properties
-				case NodeKeywords.MaterialRoughness:
-				case NodeKeywords.MaterialMetalness:
-
-					node = new PropertyNode( name, 'float' );
-
-					break;
-
-				// vec3 properties
-				case NodeKeywords.Irradiance:
-				case NodeKeywords.ReflectedLightIndirectDiffuse:
-				case NodeKeywords.ReflectedLightIndirectSpecular:
-				case NodeKeywords.ReflectedLightDirectDiffuse:
-				case NodeKeywords.ReflectedLightDirectSpecular:
-				case NodeKeywords.MaterialSpecularTint:
-
-					node = new PropertyNode( name, 'vec3' );
-
-					break;
-
-				// vec4 properties
-				case NodeKeywords.MaterialDiffuseColor:
-
-					node = new PropertyNode( name, 'vec4' );
-
-					break;
-
-			}
+		}
 
-			if ( node !== undefined ) {
+		return node;
 
-				this.nodes[ name ] = node;
+	}
 
-			}
+	addKeyword( name, callback ) {
 
-		}
+		this.keywords.push( name );
+		this.keywordsCallback[ name ] = callback;
 
-		return node;
+		return this;
 
 	}
 
@@ -202,7 +41,7 @@ class NodeKeywords {
 
 		const codeKeywords = code.match( regExp );
 
-		const keywords = [];
+		const keywordNodes = [];
 
 		if ( codeKeywords !== null ) {
 
@@ -210,9 +49,9 @@ class NodeKeywords {
 
 				const node = this.getNode( keyword );
 
-				if ( keywords.indexOf( node ) === - 1 ) {
+				if ( node !== undefined && keywordNodes.indexOf( node ) === - 1 ) {
 
-					keywords.push( node );
+					keywordNodes.push( node );
 
 				}
 
@@ -220,15 +59,15 @@ class NodeKeywords {
 
 		}
 
-		return keywords;
+		return keywordNodes;
 
 	}
 
 	include( builder, code ) {
 
-		const keywords = this.parse( code );
+		const keywordNodes = this.parse( code );
 
-		for ( const keywordNode of keywords ) {
+		for ( const keywordNode of keywordNodes ) {
 
 			keywordNode.build( builder );
 

+ 11 - 0
examples/jsm/renderers/nodes/core/NodeParser.js

@@ -0,0 +1,11 @@
+class NodeParser {
+
+	parseFunction( /*source*/ ) {
+
+		console.warn( 'Abstract function.' );
+
+	}
+
+}
+
+export default NodeParser;

+ 0 - 13
examples/jsm/renderers/nodes/core/NodeSlot.js

@@ -1,13 +0,0 @@
-class NodeSlot {
-
-	constructor( node, name, output ) {
-
-		this.node = node;
-		this.name = name;
-		this.output = output;
-
-	}
-
-}
-
-export default NodeSlot;

+ 2 - 2
examples/jsm/renderers/nodes/core/NodeVar.js

@@ -5,10 +5,10 @@ class NodeVar {
 		this.name = name;
 		this.type = type;
 
-		Object.defineProperty( this, 'isNodeVar', { value: true } );
-
 	}
 
 }
 
+NodeVar.prototype.isNodeVar = true;
+
 export default NodeVar;

+ 14 - 2
examples/jsm/renderers/nodes/core/PropertyNode.js

@@ -2,7 +2,7 @@ import Node from './Node.js';
 
 class PropertyNode extends Node {
 
-	constructor( name, nodeType ) {
+	constructor( name = null, nodeType = 'vec4' ) {
 
 		super( nodeType );
 
@@ -10,10 +10,22 @@ class PropertyNode extends Node {
 
 	}
 
+	getHash( builder ) {
+
+		return this.name || super.getHash( builder );
+
+	}
+
 	generate( builder ) {
 
 		const nodeVary = builder.getVarFromNode( this, this.getNodeType( builder ) );
-		nodeVary.name = this.name;
+		const name = this.name;
+
+		if ( name !== null ) {
+
+			nodeVary.name = name;
+
+		}
 
 		return builder.getPropertyName( nodeVary );
 

+ 0 - 80
examples/jsm/renderers/nodes/core/StructNode.js

@@ -1,80 +0,0 @@
-import CodeNode from './CodeNode.js';
-import StructVarNode from './StructVarNode.js';
-
-class StructNode extends CodeNode {
-
-	constructor( inputs = {}, name = '' ) {
-
-		super();
-
-		this.inputs = inputs;
-		this.name = name;
-
-	}
-
-	getNodeType( builder ) {
-
-		if ( this.name !== '' ) {
-
-			return this.name;
-
-		} else {
-
-			const codeNode = builder.getCodeFromNode( this, 'code' );
-
-			return codeNode.name;
-
-		}
-
-	}
-
-	create( inputs = {} ) {
-
-		return new StructVarNode( this, inputs );
-
-	}
-
-	generate( builder, output ) {
-
-		const type = this.getNodeType( builder );
-		const inputs = this.inputs;
-
-		let code = `struct ${type} {\n`;
-
-		for ( const inputName in inputs ) {
-
-			const inputType = inputs[ inputName ];
-
-			code += `\t${inputType} ${inputName};\n`;
-
-		}
-
-		code += `};`;
-
-		this.code = code;
-
-		super.generate( builder, output );
-
-		if ( output === 'var' ) {
-
-			const nodeData = builder.getDataFromNode( this );
-
-			if ( nodeData.index === undefined ) {
-
-				nodeData.index = 0;
-
-			}
-
-			return `structVar${nodeData.index ++}`;
-
-		} else {
-
-			return code;
-
-		}
-
-	}
-
-}
-
-export default StructNode;

+ 0 - 75
examples/jsm/renderers/nodes/core/StructVarNode.js

@@ -1,75 +0,0 @@
-import Node from './Node.js';
-import FloatNode from '../inputs/FloatNode.js';
-
-const zeroValue = new FloatNode( 0 ).setConst( true );
-
-class StructVarNode extends Node {
-
-	constructor( struct, inputs = {} ) {
-
-		super();
-
-		this.struct = struct;
-		this.inputs = inputs;
-
-	}
-
-	getNodeType( builder ) {
-
-		return this.struct.getNodeType( builder );
-
-	}
-
-	generate( builder ) {
-
-		const type = this.getNodeType( builder );
-
-		const struct = this.struct;
-
-		const inputs = this.inputs;
-		const structInputs = this.struct.inputs;
-
-		const nodeData = builder.getDataFromNode( this );
-
-		let property = nodeData.property;
-
-		if ( property === undefined ) {
-
-			property = struct.build( builder, 'var' );
-
-			const inputsSnippets = [];
-
-			for ( const inputName in structInputs ) {
-
-				const inputType = structInputs[ inputName ];
-				const input = inputs[ inputName ];
-
-				let inputSnippet = null;
-
-				if ( input !== undefined ) {
-
-					inputSnippet = input.build( builder, inputType );
-
-				} else {
-
-					inputSnippet = zeroValue.build( builder, inputType );
-
-				}
-
-				inputsSnippets.push( inputSnippet );
-
-			}
-
-			builder.addFlowCode( `${type} ${property} = ${type}( ${inputsSnippets.join( ', ' )} )` );
-
-			nodeData.property = property;
-
-		}
-
-		return property;
-
-	}
-
-}
-
-export default StructVarNode;

+ 9 - 12
examples/jsm/renderers/nodes/core/TempNode.js

@@ -12,33 +12,30 @@ class TempNode extends Node {
 
 		const type = builder.getVectorType( this.getNodeType( builder ) );
 
-		if ( type !== 'void' ) {
-
-			const nodeVar = builder.getVarFromNode( this, type );
-			const propertyName = builder.getPropertyName( nodeVar );
+		if ( builder.context.temp !== false && type !== 'void ' && output !== 'void' ) {
 
 			const nodeData = builder.getDataFromNode( this );
 
-			let snippet = nodeData.snippet;
+			if ( nodeData.snippet === undefined ) {
 
-			if ( snippet === undefined ) {
+				const snippet = super.build( builder, type );
 
-				snippet = super.build( builder, type );
+				const nodeVar = builder.getVarFromNode( this, type );
+				const propertyName = builder.getPropertyName( nodeVar );
 
 				builder.addFlowCode( `${propertyName} = ${snippet}` );
 
 				nodeData.snippet = snippet;
+				nodeData.propertyName = propertyName;
 
 			}
 
-			return builder.format( propertyName, type, output );
-
-		} else {
-
-			return super.build( builder, output );
+			return builder.format( nodeData.propertyName, type, output );
 
 		}
 
+		return super.build( builder, output );
+
 	}
 
 }

+ 12 - 7
examples/jsm/renderers/nodes/core/VarNode.js

@@ -2,32 +2,37 @@ import Node from './Node.js';
 
 class VarNode extends Node {
 
-	constructor( value, name = '', nodeType = null ) {
+	constructor( node, name = null, nodeType = null ) {
 
 		super( nodeType );
 
-		this.value = value;
+		this.node = node;
 		this.name = name;
 
 	}
 
+	getHash( builder ) {
+
+		return this.name || super.getHash( builder );
+
+	}
+
 	getNodeType( builder ) {
 
-		return super.getNodeType( builder ) || this.value.getNodeType( builder );
+		return super.getNodeType( builder ) || this.node.getNodeType( builder );
 
 	}
 
 	generate( builder ) {
 
 		const type = builder.getVectorType( this.getNodeType( builder ) );
+		const node = this.node;
 		const name = this.name;
-		const value = this.value;
 
+		const snippet = node.build( builder, type );
 		const nodeVar = builder.getVarFromNode( this, type );
 
-		const snippet = value.build( builder, type );
-
-		if ( name !== '' ) {
+		if ( name !== null ) {
 
 			nodeVar.name = name;
 

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