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>
 		<p>Get list of cumulative segment lengths.</p>
 
 
 		<h3>[method:null updateArcLengths]()</h3>
 		<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>
 		<h3>[method:Float getUtoTmapping]( [param:Float u], [param:Float distance] )</h3>
 		<p>
 		<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>
 		</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>
 		<h2>Source</h2>
 
 
 		<p>
 		<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_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
-			[example:webgl_materials_physical_sheen materials / physical / sheen]<br />
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 		</p>
 		</p>
 
 
@@ -135,19 +134,31 @@
 
 
 		<h3>[property:Float sheen]</h3>
 		<h3>[property:Float sheen]</h3>
 		<p>
 		<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>
 		</p>
 
 
 		<h3>[property:Float sheenRoughness]</h3>
 		<h3>[property:Float sheenRoughness]</h3>
 		<p>
 		<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>
 		</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>
 		<p>
 			The sheen tint. Default is *0xffffff*, white.
 			The sheen tint. Default is *0xffffff*, white.
 		</p>
 		</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>
 		<h3>[property:Float transmission]</h3>
 		<p>
 		<p>
 		Degree of transmission (or optical transparency), from *0.0* to *1.0*. Default is *0.0*.<br />
 		Degree of transmission (or optical transparency), from *0.0* to *1.0*. Default is *0.0*.<br />

+ 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
 		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.
 		to reduce the number of draw calls and thus improve the overall rendering performance in your application.
 		</p>
 		</p>
-		<p>
-		The current implementation requires that materials are not shared between [name] and other 3D objects.
-		</p>
 
 
 		<h2>Examples</h2>
 		<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].
 			[page:WebGLRenderer.autoClearDepth autoClearDepth] properties to false. To forcibly clear one ore more buffers call [page:WebGLRenderer.clear .clear].
 		</p>
 		</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>
 		<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>
 		<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.
 		Set this to *true* to trigger an update next time the texture is used. Particularly important for setting the wrap mode.
 		</p>
 		</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>
 		<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 />
 		height — Y轴上面的高度,默认值为1。<br />
 		depth — Z轴上面的深度,默认值为1。<br />
 		depth — Z轴上面的深度,默认值为1。<br />
 		widthSegments — (可选)宽度的分段数,默认值是1。<br />
 		widthSegments — (可选)宽度的分段数,默认值是1。<br />
-		heightSegments — (可选)度的分段数,默认值是1。<br />
-		depthSegments — (可选)度的分段数,默认值是1。
+		heightSegments — (可选)度的分段数,默认值是1。<br />
+		depthSegments — (可选)度的分段数,默认值是1。
 		</p>
 		</p>
 
 
 		<h2>属性</h2>
 		<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_variations_physical materials / variations / physical]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_clearcoat materials / physical / clearcoat]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
 			[example:webgl_materials_physical_reflectivity materials / physical / reflectivity]<br />
-			[example:webgl_materials_physical_sheen materials / physical / sheen]<br />
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 			[example:webgl_materials_physical_transmission materials / physical / transmission]
 		</p>
 		</p>
 
 
@@ -130,10 +129,31 @@
 			这模拟了非金属材质的反射率。当[page:MeshStandardMaterial]为*1.0*时,此属性无效。
 			这模拟了非金属材质的反射率。当[page:MeshStandardMaterial]为*1.0*时,此属性无效。
 		</p>
 		</p>
 
 
-		<h3>[property:Color sheen]</h3>
+		<h3>[property:Float sheen]</h3>
 		<p>
 		<p>
-			If a color is assigned to this property, the material will use a special sheen BRDF intended for rendering cloth materials such as velvet.
-			The sheen color provides the ability to create two-tone specular materials. *null* by default.
+			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>
 		</p>
 
 
 		<h3>[property:Float transmission]</h3>
 		<h3>[property:Float transmission]</h3>

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

@@ -15,9 +15,6 @@
 		一种具有实例化渲染支持的特殊版本的[page:Mesh]。你可以使用 [name] 来渲染大量具有相同几何体与材质、但具有不同世界变换的物体。
 		一种具有实例化渲染支持的特殊版本的[page:Mesh]。你可以使用 [name] 来渲染大量具有相同几何体与材质、但具有不同世界变换的物体。
 		使用 [name] 将帮助你减少 draw call 的数量,从而提升你应用程序的整体渲染性能。
 		使用 [name] 将帮助你减少 draw call 的数量,从而提升你应用程序的整体渲染性能。
 		</p>
 		</p>
-		<p>
-		当前的实现需要[name]和其它3D物体间不共享材质。
-		</p>
 
 
 		<h2>示例</h2>
 		<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来阻止对应缓存被清除。
 			即便forceClear设为true, 也可以通过将[page:WebGLRenderer.autoClearColor autoClearColor]、[page:WebGLRenderer.autoClearStencil autoClearStencil]或[page:WebGLRenderer.autoClearDepth autoClearDepth]属性的值设为false来阻止对应缓存被清除。
 		</p>
 		</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>
 		<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>
 		<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>
 		</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>
 		<h2>方法</h2>
 
 

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

@@ -7,22 +7,24 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	</head>
 	<body>
 	<body>
+		[page:EventDispatcher] &rarr;
+
 		<h1>[name]</h1>
 		<h1>[name]</h1>
 
 
 		<p class="desc">
 		<p class="desc">
 		Arcball controls allow the camera to be controlled by a virtual trackball with full touch support and advanced navigation functionality. <br>
 		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
 		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
 		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>
 		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
 		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.
 		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>
 		(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
 		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>
 		are on.<br><br>
 
 
@@ -189,6 +191,11 @@
 			Maximum angular velocity allowed on rotation animation start.
 			Maximum angular velocity allowed on rotation animation start.
 		</p>
 		</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>
 		<h2>Methods</h2>
 
 
@@ -232,6 +239,11 @@
 			Set the visible property of gizmos.
 			Set the visible property of gizmos.
 		</p>
 		</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>
 		<h3>[method:Boolean setMouseAction] ( [param:String operation], mouse, key )</h3>
 		<p>
 		<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>
 			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>
 			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.
 			Keyboard modifiers can be specified as 'CTRL', 'SHIFT' or null if not needed.
 		</p>
 		</p>
-		
+
 		<h3>[method:null update] ()</h3>
 		<h3>[method:null update] ()</h3>
 		<p>
 		<p>
 			Update the controls. Must be called after any manual changes to the camera's transform.
 			Update the controls. Must be called after any manual changes to the camera's transform.
 		</p>
 		</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>
 		<h2>Source</h2>
 
 
 		<p>
 		<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>
 		<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>
 		<h2>Methods</h2>
 
 

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

@@ -33,7 +33,21 @@
 
 
 		<h2>Constructor</h2>
 		<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>
 		<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
 			// *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' );
 				window.location.hash = window.location.hash.replace( 'BufferGeometry', 'Geometry' );
 
 

+ 0 - 12
docs/list.json

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

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

@@ -142,8 +142,8 @@ function SidebarMaterial( editor ) {
 
 
 	// attenuation tint
 	// 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
 	// thickness
 
 

+ 3 - 3
editor/js/Strings.js

@@ -251,7 +251,7 @@ function Strings( config ) {
 			'sidebar/material/clearcoatroughness': 'Clearcoat Roughness',
 			'sidebar/material/clearcoatroughness': 'Clearcoat Roughness',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/attenuationDistance': 'Attenuation Distance',
 			'sidebar/material/attenuationDistance': 'Attenuation Distance',
-			'sidebar/material/attenuationTint': 'Attenuation Tint',
+			'sidebar/material/attenuationColor': 'Attenuation Color',
 			'sidebar/material/thickness': 'Thickness',
 			'sidebar/material/thickness': 'Thickness',
 			'sidebar/material/vertexcolors': 'Vertex Colors',
 			'sidebar/material/vertexcolors': 'Vertex Colors',
 			'sidebar/material/matcap': 'Matcap',
 			'sidebar/material/matcap': 'Matcap',
@@ -579,7 +579,7 @@ function Strings( config ) {
 			'sidebar/material/clearcoatroughness': 'Rugosité du vernis',
 			'sidebar/material/clearcoatroughness': 'Rugosité du vernis',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/attenuationDistance': 'Attenuation Distance',
 			'sidebar/material/attenuationDistance': 'Attenuation Distance',
-			'sidebar/material/attenuationTint': 'Attenuation Tint',
+			'sidebar/material/attenuationColor': 'Attenuation Color',
 			'sidebar/material/thickness': 'Thickness',
 			'sidebar/material/thickness': 'Thickness',
 			'sidebar/material/vertexcolors': 'Couleurs aux Sommets',
 			'sidebar/material/vertexcolors': 'Couleurs aux Sommets',
 			'sidebar/material/matcap': 'Matcap',
 			'sidebar/material/matcap': 'Matcap',
@@ -907,7 +907,7 @@ function Strings( config ) {
 			'sidebar/material/clearcoatroughness': '清漆粗糙度',
 			'sidebar/material/clearcoatroughness': '清漆粗糙度',
 			'sidebar/material/transmission': '透光',
 			'sidebar/material/transmission': '透光',
 			'sidebar/material/attenuationDistance': '衰减距离',
 			'sidebar/material/attenuationDistance': '衰减距离',
-			'sidebar/material/attenuationTint': '衰减色彩',
+			'sidebar/material/attenuationColor': 'Attenuation Color',
 			'sidebar/material/thickness': '厚度',
 			'sidebar/material/thickness': '厚度',
 			'sidebar/material/vertexcolors': '顶点颜色',
 			'sidebar/material/vertexcolors': '顶点颜色',
 			'sidebar/material/matcap': '材质捕获',
 			'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].",
       "!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)"
       "!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": {
     "AmbientLight": {
       "!url": "http://threejs.org/docs/#Reference/lights/AmbientLight",
       "!url": "http://threejs.org/docs/#Reference/lights/AmbientLight",
       "prototype": {
       "prototype": {

+ 1 - 1
editor/sw.js

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

+ 4 - 3
examples/files.json

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

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

@@ -40,6 +40,8 @@
 	const _endEvent = {
 	const _endEvent = {
 		type: 'end'
 		type: 'end'
 	};
 	};
+
+	const _raycaster = new THREE.Raycaster();
 	/**
 	/**
  *
  *
  * @param {Camera} camera Virtual camera used in the scene
  * @param {Camera} camera Virtual camera used in the scene
@@ -47,7 +49,8 @@
  * @param {Scene} scene The scene to be rendered
  * @param {Scene} scene The scene to be rendered
  */
  */
 
 
-	class ArcballControls extends THREE.Object3D {
+
+	class ArcballControls extends THREE.EventDispatcher {
 
 
 		constructor( _camera, domElement, scene = null ) {
 		constructor( _camera, domElement, scene = null ) {
 
 
@@ -1591,7 +1594,6 @@
 
 
 			this.calculateTbRadius = camera => {
 			this.calculateTbRadius = camera => {
 
 
-				const factor = 0.67;
 				const distance = camera.position.distanceTo( this._gizmos.position );
 				const distance = camera.position.distanceTo( this._gizmos.position );
 
 
 				if ( camera.type == 'PerspectiveCamera' ) {
 				if ( camera.type == 'PerspectiveCamera' ) {
@@ -1600,11 +1602,11 @@
 
 
 					const halfFovH = Math.atan( camera.aspect * Math.tan( halfFovV ) ); //horizontal 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' ) {
 				} 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( 'pointermove', this.onPointerMove );
 				window.removeEventListener( 'pointerup', this.onPointerUp );
 				window.removeEventListener( 'pointerup', this.onPointerUp );
 				window.removeEventListener( 'resize', this.onWindowResize );
 				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();
 				this.disposeGrid();
 
 
 			};
 			};
@@ -1788,7 +1790,7 @@
 
 
 			this.setCamera = camera => {
 			this.setCamera = camera => {
 
 
-				camera.lookAt( this._tbCenter );
+				camera.lookAt( this.target );
 				camera.updateMatrix(); //setting state
 				camera.updateMatrix(); //setting state
 
 
 				if ( camera.type == 'PerspectiveCamera' ) {
 				if ( camera.type == 'PerspectiveCamera' ) {
@@ -1807,10 +1809,10 @@
 				this._zoom0 = camera.zoom;
 				this._zoom0 = camera.zoom;
 				this._zoomState = this._zoom0;
 				this._zoomState = this._zoom0;
 				this._initialNear = camera.near;
 				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._nearPos = this._initialNear;
 				this._initialFar = camera.far;
 				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._farPos = this._initialFar;
 
 
 				this._up0.copy( camera.up );
 				this._up0.copy( camera.up );
@@ -1821,7 +1823,7 @@
 				this.camera.updateProjectionMatrix(); //making gizmos
 				this.camera.updateProjectionMatrix(); //making gizmos
 
 
 				this._tbRadius = this.calculateTbRadius( camera );
 				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.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._gizmos.position.set( x, y, z ); //for correct radius calculation
 
 
 
 
 				this._tbRadius = this.calculateTbRadius( this.camera );
 				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 ) => {
 			this.unprojectOnObj = ( cursor, camera ) => {
 
 
-				const raycaster = new THREE.Raycaster();
+				const raycaster = this.getRaycaster();
 				raycaster.near = camera.near;
 				raycaster.near = camera.near;
 				raycaster.far = camera.far;
 				raycaster.far = camera.far;
 				raycaster.setFromCamera( cursor, camera );
 				raycaster.setFromCamera( cursor, camera );
@@ -2680,6 +2682,8 @@
 			this.camera = null;
 			this.camera = null;
 			this.domElement = domElement;
 			this.domElement = domElement;
 			this.scene = scene;
 			this.scene = scene;
+			this.target = new THREE.Vector3( 0, 0, 0 );
+			this.radiusFactor = 0.67;
 			this.mouseActions = [];
 			this.mouseActions = [];
 			this._mouseOp = null; //global vectors and matrices that are used in some operations to avoid creating new objects every time (e.g. every time cursor moves)
 			this._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.minZoom = 0;
 			this.maxZoom = Infinity; //trackball parameters
 			this.maxZoom = Infinity; //trackball parameters
 
 
-			this._tbCenter = new THREE.Vector3( 0, 0, 0 );
 			this._tbRadius = 1; //FSA
 			this._tbRadius = 1; //FSA
 
 
 			this._state = STATE.IDLE;
 			this._state = STATE.IDLE;
@@ -2928,6 +2931,29 @@
 			this._gizmos.visible = value;
 			this._gizmos.visible = value;
 			this.dispatchEvent( _changeEvent );
 			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
    * 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;
 	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 ) {
 			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();
 				event.preventDefault();
 				scope.dispatchEvent( _startEvent );
 				scope.dispatchEvent( _startEvent );
 				handleMouseWheel( event );
 				handleMouseWheel( event );

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

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

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

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

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

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

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

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

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

@@ -1110,6 +1110,10 @@
 							data.parameters = parseEffectParameters( child );
 							data.parameters = parseEffectParameters( child );
 							break;
 							break;
 
 
+						case 'extra':
+							data.extra = parseEffectExtra( child );
+							break;
+
 					}
 					}
 
 
 				}
 				}
@@ -1267,6 +1271,10 @@
 
 
 							break;
 							break;
 
 
+						case 'bump':
+							data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
+							break;
+
 					}
 					}
 
 
 				}
 				}
@@ -1311,6 +1319,37 @@
 							data[ child.nodeName ] = parseInt( child.textContent );
 							data[ child.nodeName ] = parseInt( child.textContent );
 							break;
 							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 effect = getEffect( data.url );
 				const technique = effect.profile.technique;
 				const technique = effect.profile.technique;
-				const extra = effect.profile.extra;
 				let material;
 				let material;
 
 
 				switch ( technique.type ) {
 				switch ( technique.type ) {
@@ -1559,6 +1597,7 @@
 								break;
 								break;
 
 
 							default:
 							default:
+								material.opacity = 1 - transparency.float;
 								console.warn( 'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque );
 								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 ) {
 				for ( const nodeID in BindPoseNode ) {
 
 
-					if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) {
+					if ( BindPoseNode[ nodeID ].attrType === 'BindPose' && BindPoseNode[ nodeID ].NbPoseNodes > 0 ) {
 
 
 						const poseNodes = BindPoseNode[ nodeID ].PoseNode;
 						const poseNodes = BindPoseNode[ nodeID ].PoseNode;
 
 

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

@@ -23,6 +23,11 @@
 
 
 				return new GLTFTextureWebPExtension( parser );
 				return new GLTFTextureWebPExtension( parser );
 
 
+			} );
+			this.register( function ( parser ) {
+
+				return new GLTFMaterialsSheenExtension( parser );
+
 			} );
 			} );
 			this.register( function ( parser ) {
 			this.register( function ( parser ) {
 
 
@@ -336,6 +341,7 @@
 		KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 		KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 		KHR_MATERIALS_IOR: 'KHR_materials_ior',
 		KHR_MATERIALS_IOR: 'KHR_materials_ior',
 		KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
 		KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
+		KHR_MATERIALS_SHEEN: 'KHR_materials_sheen',
 		KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 		KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 		KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 		KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 		KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
 		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
  * Transmission Materials Extension
@@ -698,7 +775,7 @@
 
 
 			materialParams.attenuationDistance = extension.attenuationDistance || 0;
 			materialParams.attenuationDistance = extension.attenuationDistance || 0;
 			const colorArray = extension.attenuationColor || [ 1, 1, 1 ];
 			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 );
 			return Promise.all( pending );
 
 
 		}
 		}
@@ -794,11 +871,11 @@
 			}
 			}
 
 
 			const colorArray = extension.specularColorFactor || [ 1, 1, 1 ];
 			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 ) {
 			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;
 					texture.encoding = THREE.sRGBEncoding;
 
 
@@ -1658,34 +1735,10 @@
 		MASK: 'MASK',
 		MASK: 'MASK',
 		BLEND: 'BLEND'
 		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
  * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
  */
  */
 
 
-
 	function createDefaultMaterial( cache ) {
 	function createDefaultMaterial( cache ) {
 
 
 		if ( cache[ 'DefaultMaterial' ] === undefined ) {
 		if ( cache[ 'DefaultMaterial' ] === undefined ) {
@@ -2312,7 +2365,7 @@
 			const options = this.options;
 			const options = this.options;
 			return new Promise( function ( resolve, reject ) {
 			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 + '".' ) );
 					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();
 	const _taskCache = new WeakMap();
 
 
+	let _activeLoaders = 0;
+
 	class KTX2Loader extends THREE.Loader {
 	class KTX2Loader extends THREE.Loader {
 
 
 		constructor( manager ) {
 		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;
 			return this.transcoderPending;
@@ -207,6 +218,7 @@
 
 
 			URL.revokeObjectURL( this.workerSourceURL );
 			URL.revokeObjectURL( this.workerSourceURL );
 			this.workerPool.dispose();
 			this.workerPool.dispose();
+			_activeLoaders --;
 			return this;
 			return this;
 
 
 		}
 		}

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

@@ -220,7 +220,8 @@
 					const scanline_width = w;
 					const scanline_width = w;
 
 
 					if ( // run length encoding is not allowed so read flat
 					if ( // run length encoding is not allowed so read flat
-						scanline_width < 8 || scanline_width > 0x7fff || 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 the flat buffer
 						return new Uint8Array( buffer );
 						return new Uint8Array( buffer );

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

@@ -16,7 +16,6 @@
 			super( manager );
 			super( manager );
 			this.debug = false;
 			this.debug = false;
 			this.group = null;
 			this.group = null;
-			this.position = 0;
 			this.materials = [];
 			this.materials = [];
 			this.meshes = [];
 			this.meshes = [];
 
 
@@ -79,7 +78,6 @@
 		parse( arraybuffer, path ) {
 		parse( arraybuffer, path ) {
 
 
 			this.group = new THREE.Group();
 			this.group = new THREE.Group();
-			this.position = 0;
 			this.materials = [];
 			this.materials = [];
 			this.meshes = [];
 			this.meshes = [];
 			this.readFile( arraybuffer, path );
 			this.readFile( arraybuffer, path );
@@ -105,31 +103,30 @@
 		readFile( arraybuffer, path ) {
 		readFile( arraybuffer, path ) {
 
 
 			const data = new DataView( arraybuffer );
 			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 ) {
 			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 );
 						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 {
 					} 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.
    * Read mesh data chunk.
    *
    *
    * @method readMeshData
    * @method readMeshData
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk to read mesh from
    * @param {String} path Path for external resources.
    * @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 );
 					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.debugMessage( 'Master scale: ' + scale );
 					this.group.scale.set( scale, 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.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.debugMessage( 'Material' );
-					this.resetPosition( data );
-					this.readMaterialEntry( data, path );
+					this.readMaterialEntry( next, path );
 
 
 				} else {
 				} 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.
    * Read named object chunk.
    *
    *
    * @method readNamedObject
    * @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;
 					mesh.name = name;
 					this.meshes.push( mesh );
 					this.meshes.push( mesh );
 
 
 				} else {
 				} 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.
    * Read material data chunk and add it to the material list.
    *
    *
    * @method readMaterialEntry
    * @method readMaterialEntry
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @param {String} path Path for external resources.
    * @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();
 			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 );
 					this.debugMessage( '   Name: ' + material.name );
 
 
-				} else if ( next === MAT_WIRE ) {
+				} else if ( next.id === MAT_WIRE ) {
 
 
 					this.debugMessage( '   Wireframe' );
 					this.debugMessage( '   Wireframe' );
 					material.wireframe = true;
 					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;
 					material.wireframeLinewidth = value;
 					this.debugMessage( '   Wireframe Thickness: ' + value );
 					this.debugMessage( '   Wireframe Thickness: ' + value );
 
 
-				} else if ( next === MAT_TWO_SIDE ) {
+				} else if ( next.id === MAT_TWO_SIDE ) {
 
 
 					material.side = THREE.DoubleSide;
 					material.side = THREE.DoubleSide;
 					this.debugMessage( '   DoubleSided' );
 					this.debugMessage( '   DoubleSided' );
 
 
-				} else if ( next === MAT_ADDITIVE ) {
+				} else if ( next.id === MAT_ADDITIVE ) {
 
 
 					this.debugMessage( '   Additive Blending' );
 					this.debugMessage( '   Additive Blending' );
 					material.blending = THREE.AdditiveBlending;
 					material.blending = THREE.AdditiveBlending;
 
 
-				} else if ( next === MAT_DIFFUSE ) {
+				} else if ( next.id === MAT_DIFFUSE ) {
 
 
 					this.debugMessage( '   Diffuse THREE.Color' );
 					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' );
 					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' );
 					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;
 					material.shininess = shininess * 100;
 					this.debugMessage( '   Shininess : ' + shininess );
 					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;
 					material.opacity = 1 - transparency;
 					this.debugMessage( '  Transparency : ' + transparency );
 					this.debugMessage( '  Transparency : ' + transparency );
 					material.transparent = material.opacity < 1 ? true : false;
 					material.transparent = material.opacity < 1 ? true : false;
 
 
-				} else if ( next === MAT_TEXMAP ) {
+				} else if ( next.id === MAT_TEXMAP ) {
 
 
 					this.debugMessage( '   ColorMap' );
 					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.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.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.debugMessage( '   SpecularMap' );
-					this.resetPosition( data );
-					material.specularMap = this.readMap( data, path );
+					material.specularMap = this.readMap( next, path );
 
 
 				} else {
 				} 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;
 			this.materials[ material.name ] = material;
 
 
 		}
 		}
@@ -338,68 +321,66 @@
    * Read mesh data chunk.
    * Read mesh data chunk.
    *
    *
    * @method readMesh
    * @method readMesh
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @return {Mesh} The parsed mesh.
    * @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 geometry = new THREE.BufferGeometry();
 			const material = new THREE.MeshPhongMaterial();
 			const material = new THREE.MeshPhongMaterial();
 			const mesh = new THREE.Mesh( geometry, material );
 			const mesh = new THREE.Mesh( geometry, material );
 			mesh.name = 'mesh';
 			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
 					this.debugMessage( '   Vertex: ' + points ); //BufferGeometry
 
 
 					const vertices = [];
 					const vertices = [];
 
 
 					for ( let i = 0; i < points; i ++ ) {
 					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 ) );
 					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
 					this.debugMessage( '   UV: ' + texels ); //BufferGeometry
 
 
 					const uvs = [];
 					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 THREE.Float32BufferAttribute( uvs, 2 ) );
 					geometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
 
 
-				} else if ( next === MESH_MATRIX ) {
+				} else if ( next.id === MESH_MATRIX ) {
 
 
 					this.debugMessage( '   Tranformation Matrix (TODO)' );
 					this.debugMessage( '   Tranformation Matrix (TODO)' );
 					const values = [];
 					const values = [];
 
 
 					for ( let i = 0; i < 12; i ++ ) {
 					for ( let i = 0; i < 12; i ++ ) {
 
 
-						values[ i ] = this.readFloat( data );
+						values[ i ] = next.readFloat();
 
 
 					}
 					}
 
 
@@ -432,15 +413,14 @@
 
 
 				} else {
 				} 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();
 			geometry.computeVertexNormals();
 			return mesh;
 			return mesh;
 
 
@@ -449,22 +429,21 @@
    * Read face array data chunk.
    * Read face array data chunk.
    *
    *
    * @method readFaceArray
    * @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.
    * @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 );
 			this.debugMessage( '   Faces: ' + faces );
 			const index = [];
 			const index = [];
 
 
 			for ( let i = 0; i < faces; ++ i ) {
 			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 materialIndex = 0;
 			let start = 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 ) {
 				if ( subchunk.id === MSH_MAT_GROUP ) {
 
 
 					this.debugMessage( '      Material THREE.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
 					const count = group.index.length * 3; // assuming successive indices
 
 
 					mesh.geometry.addGroup( start, count, materialIndex );
 					mesh.geometry.addGroup( start, count, materialIndex );
@@ -498,76 +476,70 @@
 
 
 				} else {
 				} 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
 			if ( mesh.material.length === 1 ) mesh.material = mesh.material[ 0 ]; // for backwards compatibility
 
 
-			this.endChunk( chunk );
-
 		}
 		}
 		/**
 		/**
    * Read texture map data chunk.
    * Read texture map data chunk.
    *
    *
    * @method readMap
    * @method readMap
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @param {String} path Path for external resources.
    * @param {String} path Path for external resources.
    * @return {Texture} Texture read from this data chunk.
    * @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 = {};
 			let texture = {};
 			const loader = new THREE.TextureLoader( this.manager );
 			const loader = new THREE.TextureLoader( this.manager );
 			loader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
 			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 );
 					texture = loader.load( name );
 					this.debugMessage( '      File: ' + path + 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 );
 					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 );
 					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 );
 					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 );
 					this.debugMessage( '      RepeatY: ' + texture.repeat.y );
 
 
 				} else {
 				} 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;
 			return texture;
 
 
 		}
 		}
@@ -575,23 +547,22 @@
    * Read material group data chunk.
    * Read material group data chunk.
    *
    *
    * @method readMaterialGroup
    * @method readMaterialGroup
-   * @param {Dataview} data Dataview in use.
+   * @param {Chunk} chunk Chunk in use.
    * @return {Object} Object with name and index of the object.
    * @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( '         Name: ' + name );
 			this.debugMessage( '         Faces: ' + numFaces );
 			this.debugMessage( '         Faces: ' + numFaces );
 			const index = [];
 			const index = [];
 
 
 			for ( let i = 0; i < numFaces; ++ i ) {
 			for ( let i = 0; i < numFaces; ++ i ) {
 
 
-				index.push( this.readWord( data ) );
+				index.push( chunk.readWord() );
 
 
 			}
 			}
 
 
@@ -605,132 +576,191 @@
    * Read a color value.
    * Read a color value.
    *
    *
    * @method readColor
    * @method readColor
-   * @param {DataView} data Dataview.
+   * @param {Chunk} chunk Chunk.
    * @return {Color} THREE.Color value read..
    * @return {Color} THREE.Color value read..
    */
    */
 
 
 
 
-		readColor( data ) {
+		readColor( chunk ) {
 
 
-			const chunk = this.readChunk( data );
+			const subChunk = chunk.readChunk();
 			const color = new THREE.Color();
 			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 );
 				color.setRGB( r / 255, g / 255, b / 255 );
 				this.debugMessage( '      THREE.Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 				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 );
 				color.setRGB( r, g, b );
 				this.debugMessage( '      THREE.Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 				this.debugMessage( '      THREE.Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 
 
 			} else {
 			} else {
 
 
-				this.debugMessage( '      Unknown color chunk: ' + chunk.toString( 16 ) );
+				this.debugMessage( '      Unknown color chunk: ' + subChunk.hexId );
 
 
 			}
 			}
 
 
-			this.endChunk( chunk );
 			return color;
 			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 {
 			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 ) {
 			} catch ( e ) {
 
 
 				this.debugMessage( 'Unable to read chunk at ' + this.position );
 				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.
    * Read byte value.
    *
    *
    * @method readByte
    * @method readByte
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    * @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;
 			this.position += 1;
 			return v;
 			return v;
 
 
@@ -739,22 +769,22 @@
    * Read 32 bit float value.
    * Read 32 bit float value.
    *
    *
    * @method readFloat
    * @method readFloat
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    * @return {Number} Data read from the dataview.
    */
    */
 
 
 
 
-		readFloat( data ) {
+		readFloat() {
 
 
 			try {
 			try {
 
 
-				const v = data.getFloat32( this.position, true );
+				const v = this.data.getFloat32( this.position, true );
 				this.position += 4;
 				this.position += 4;
 				return v;
 				return v;
 
 
 			} catch ( e ) {
 			} 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.
    * Read 32 bit signed integer value.
    *
    *
    * @method readInt
    * @method readInt
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    * @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;
 			this.position += 4;
 			return v;
 			return v;
 
 
@@ -779,14 +808,13 @@
    * Read 16 bit signed integer value.
    * Read 16 bit signed integer value.
    *
    *
    * @method readShort
    * @method readShort
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    * @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;
 			this.position += 2;
 			return v;
 			return v;
 
 
@@ -795,14 +823,13 @@
    * Read 64 bit unsigned integer value.
    * Read 64 bit unsigned integer value.
    *
    *
    * @method readDWord
    * @method readDWord
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    * @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;
 			this.position += 4;
 			return v;
 			return v;
 
 
@@ -811,101 +838,40 @@
    * Read 32 bit unsigned integer value.
    * Read 32 bit unsigned integer value.
    *
    *
    * @method readWord
    * @method readWord
-   * @param {DataView} data Dataview to read data from.
    * @return {Number} Data read from the dataview.
    * @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;
 			this.position += 2;
 			return v;
 			return v;
 
 
 		}
 		}
 		/**
 		/**
-   * Read string value.
+   * Read NULL terminated ASCII string value from chunk-pos.
    *
    *
    * @method readString
    * @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.
    * @return {String} Data read from the dataview.
    */
    */
 
 
 
 
-		readString( data, maxLength ) {
+		readString() {
 
 
 			let s = '';
 			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 );
 				s += String.fromCharCode( c );
+				c = this.readByte();
 
 
 			}
 			}
 
 
 			return s;
 			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;
 	} // const NULL_CHUNK = 0x0000;
 
 

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

@@ -4,18 +4,19 @@
  * Port of http://webglsamples.org/blob/blob.html
  * 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 scope = this; // temp buffers used in polygonize
 
 
 			const vlist = new Float32Array( 12 * 3 );
 			const vlist = new Float32Array( 12 * 3 );
 			const nlist = new Float32Array( 12 * 3 );
 			const nlist = new Float32Array( 12 * 3 );
 			const clist = 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
 			// prototype functions kill performance
 			// (tested and it was 4x slower !!!)
 			// (tested and it was 4x slower !!!)
 
 
@@ -35,27 +36,34 @@
 				this.zd = this.size2;
 				this.zd = this.size2;
 				this.field = new Float32Array( this.size3 );
 				this.field = new Float32Array( this.size3 );
 				this.normal_cache = new Float32Array( this.size3 * 3 );
 				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.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 ) {
 				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 ) {
 				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 )
 			// (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
 				// cache indices
 				const q1 = q + 1,
 				const q1 = q + 1,
@@ -282,7 +290,7 @@
 					o1 = cubeindex + i;
 					o1 = cubeindex + i;
 					o2 = o1 + 1;
 					o2 = o1 + 1;
 					o3 = o1 + 2;
 					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;
 					i += 3;
 					numtris ++;
 					numtris ++;
 
 
@@ -290,12 +298,9 @@
 
 
 				return numtris;
 				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
 				const c = scope.count * 3; // positions
 
 
@@ -368,67 +373,7 @@
 
 
 				scope.count += 3;
 				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
 			// Metaballs
 			/////////////////////////////////////
 			/////////////////////////////////////
 			// Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after
 			// 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;
 				const smin2 = this.size - 2;
 
 
@@ -750,59 +695,28 @@
 							const fx = ( x - this.halfsize ) / this.halfsize; //+ 1
 							const fx = ( x - this.halfsize ) / this.halfsize; //+ 1
 
 
 							const q = y_offset + x;
 							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
 	// Marching cubes lookup tables
 	/////////////////////////////////////
 	/////////////////////////////////////
 	// These tables are straight from Paul Bourke's page:
 	// 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.
 	// 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 ] );
 	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 {
 	class CSS2DObject extends THREE.Object3D {
 
 
-		constructor( element ) {
+		constructor( element = document.createElement( 'div' ) ) {
 
 
 			super();
 			super();
-			this.element = element || document.createElement( 'div' );
+			this.element = element;
 			this.element.style.position = 'absolute';
 			this.element.style.position = 'absolute';
 			this.element.style.userSelect = 'none';
 			this.element.style.userSelect = 'none';
 			this.element.setAttribute( 'draggable', false );
 			this.element.setAttribute( 'draggable', false );
@@ -49,7 +49,7 @@
 
 
 	class CSS2DRenderer {
 	class CSS2DRenderer {
 
 
-		constructor() {
+		constructor( parameters = {} ) {
 
 
 			const _this = this;
 			const _this = this;
 
 
@@ -60,7 +60,7 @@
 			const cache = {
 			const cache = {
 				objects: new WeakMap()
 				objects: new WeakMap()
 			};
 			};
-			const domElement = document.createElement( 'div' );
+			const domElement = parameters.element !== undefined ? parameters.element : document.createElement( 'div' );
 			domElement.style.overflow = 'hidden';
 			domElement.style.overflow = 'hidden';
 			this.domElement = domElement;
 			this.domElement = domElement;
 
 

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

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

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

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

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

@@ -6,14 +6,14 @@ import {
 	LineBasicMaterial,
 	LineBasicMaterial,
 	Raycaster,
 	Raycaster,
 	Group,
 	Group,
-	Object3D,
 	Box3,
 	Box3,
 	Sphere,
 	Sphere,
 	Quaternion,
 	Quaternion,
 	Vector2,
 	Vector2,
 	Vector3,
 	Vector3,
 	Matrix4,
 	Matrix4,
-	MathUtils
+	MathUtils,
+	EventDispatcher
 } from '../../../build/three.module.js';
 } from '../../../build/three.module.js';
 
 
 //trackball state
 //trackball state
@@ -64,6 +64,8 @@ const _changeEvent = { type: 'change' };
 const _startEvent = { type: 'start' };
 const _startEvent = { type: 'start' };
 const _endEvent = { type: 'end' };
 const _endEvent = { type: 'end' };
 
 
+const _raycaster = new Raycaster();
+
 
 
 /**
 /**
  *
  *
@@ -71,7 +73,7 @@ const _endEvent = { type: 'end' };
  * @param {HTMLElement} domElement Renderer's dom element
  * @param {HTMLElement} domElement Renderer's dom element
  * @param {Scene} scene The scene to be rendered
  * @param {Scene} scene The scene to be rendered
  */
  */
-class ArcballControls extends Object3D {
+class ArcballControls extends EventDispatcher {
 
 
 	constructor( camera, domElement, scene = null ) {
 	constructor( camera, domElement, scene = null ) {
 
 
@@ -79,6 +81,8 @@ class ArcballControls extends Object3D {
 		this.camera = null;
 		this.camera = null;
 		this.domElement = domElement;
 		this.domElement = domElement;
 		this.scene = scene;
 		this.scene = scene;
+		this.target = new Vector3( 0, 0, 0 );
+		this.radiusFactor = 0.67;
 
 
 		this.mouseActions = [];
 		this.mouseActions = [];
 		this._mouseOp = null;
 		this._mouseOp = null;
@@ -204,7 +208,6 @@ class ArcballControls extends Object3D {
 		this.maxZoom = Infinity;
 		this.maxZoom = Infinity;
 
 
 		//trackball parameters
 		//trackball parameters
-		this._tbCenter = new Vector3( 0, 0, 0 );
 		this._tbRadius = 1;
 		this._tbRadius = 1;
 
 
 		//FSA
 		//FSA
@@ -1974,18 +1977,17 @@ class ArcballControls extends Object3D {
 	 */
 	 */
 	calculateTbRadius = ( camera ) => {
 	calculateTbRadius = ( camera ) => {
 
 
-		const factor = 0.67;
 		const distance = camera.position.distanceTo( this._gizmos.position );
 		const distance = camera.position.distanceTo( this._gizmos.position );
 
 
 		if ( camera.type == 'PerspectiveCamera' ) {
 		if ( camera.type == 'PerspectiveCamera' ) {
 
 
 			const halfFovV = MathUtils.DEG2RAD * camera.fov * 0.5; //vertical fov/2 in radians
 			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
 			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' ) {
 		} 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( 'pointerup', this.onPointerUp );
 
 
 		window.removeEventListener( 'resize', this.onWindowResize );
 		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();
 		this.disposeGrid();
 
 
 	};
 	};
@@ -2193,7 +2195,7 @@ class ArcballControls extends Object3D {
 	 */
 	 */
 	setCamera = ( camera ) => {
 	setCamera = ( camera ) => {
 
 
-		camera.lookAt( this._tbCenter );
+		camera.lookAt( this.target );
 		camera.updateMatrix();
 		camera.updateMatrix();
 
 
 		//setting state
 		//setting state
@@ -2211,11 +2213,11 @@ class ArcballControls extends Object3D {
 		this._zoomState = this._zoom0;
 		this._zoomState = this._zoom0;
 
 
 		this._initialNear = camera.near;
 		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._nearPos = this._initialNear;
 
 
 		this._initialFar = camera.far;
 		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._farPos = this._initialFar;
 
 
 		this._up0.copy( camera.up );
 		this._up0.copy( camera.up );
@@ -2226,7 +2228,7 @@ class ArcballControls extends Object3D {
 
 
 		//making gizmos
 		//making gizmos
 		this._tbRadius = this.calculateTbRadius( camera );
 		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
 	 * Creates the rotation gizmos matching trackball center and radius
 	 * @param {Vector3} tbCenter The trackball center
 	 * @param {Vector3} tbCenter The trackball center
@@ -2728,12 +2754,12 @@ class ArcballControls extends Object3D {
 	 */
 	 */
 	setTarget = ( x, y, z ) => {
 	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._gizmos.position.set( x, y, z );	//for correct radius calculation
 		this._tbRadius = this.calculateTbRadius( this.camera );
 		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
 	 * Unproject the cursor on the 3D object surface
 	 * @param {Vector2} cursor Cursor coordinates in NDC
 	 * @param {Vector2} cursor Cursor coordinates in NDC
@@ -2817,7 +2850,7 @@ class ArcballControls extends Object3D {
 	 */
 	 */
 	unprojectOnObj = ( cursor, camera ) => {
 	unprojectOnObj = ( cursor, camera ) => {
 
 
-		const raycaster = new Raycaster();
+		const raycaster = this.getRaycaster();
 		raycaster.near = camera.near;
 		raycaster.near = camera.near;
 		raycaster.far = camera.far;
 		raycaster.far = camera.far;
 		raycaster.setFromCamera( cursor, camera );
 		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 ) {
 		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();
 			event.preventDefault();
 
 

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

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

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

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

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

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

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

@@ -10,6 +10,7 @@ import {
 	Euler,
 	Euler,
 	FileLoader,
 	FileLoader,
 	Float32BufferAttribute,
 	Float32BufferAttribute,
+	FrontSide,
 	Group,
 	Group,
 	Line,
 	Line,
 	LineBasicMaterial,
 	LineBasicMaterial,
@@ -33,6 +34,7 @@ import {
 	SkinnedMesh,
 	SkinnedMesh,
 	SpotLight,
 	SpotLight,
 	TextureLoader,
 	TextureLoader,
+	Vector2,
 	Vector3,
 	Vector3,
 	VectorKeyframeTrack
 	VectorKeyframeTrack
 } from '../../../build/three.module.js';
 } from '../../../build/three.module.js';
@@ -1239,6 +1241,10 @@ class ColladaLoader extends Loader {
 						data.parameters = parseEffectParameters( child );
 						data.parameters = parseEffectParameters( child );
 						break;
 						break;
 
 
+					case 'extra':
+						data.extra = parseEffectExtra( child );
+						break;
+
 				}
 				}
 
 
 			}
 			}
@@ -1399,6 +1405,10 @@ class ColladaLoader extends Loader {
 
 
 						break;
 						break;
 
 
+					case 'bump':
+						data[ child.nodeName ] = parseEffectExtraTechniqueBump( child );
+						break;
+
 				}
 				}
 
 
 			}
 			}
@@ -1445,6 +1455,34 @@ class ColladaLoader extends Loader {
 						data[ child.nodeName ] = parseInt( child.textContent );
 						data[ child.nodeName ] = parseInt( child.textContent );
 						break;
 						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 effect = getEffect( data.url );
 			const technique = effect.profile.technique;
 			const technique = effect.profile.technique;
-			const extra = effect.profile.extra;
 
 
 			let material;
 			let material;
 
 
@@ -1700,6 +1737,7 @@ class ColladaLoader extends Loader {
 							material.opacity = color[ 0 ] * transparency.float;
 							material.opacity = color[ 0 ] * transparency.float;
 							break;
 							break;
 						default:
 						default:
+							material.opacity = 1 - transparency.float;
 							console.warn( 'THREE.ColladaLoader: Invalid opaque type "%s" of transparent tag.', transparent.opaque );
 							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 ) {
 			for ( const nodeID in BindPoseNode ) {
 
 
-				if ( BindPoseNode[ nodeID ].attrType === 'BindPose' ) {
+				if ( BindPoseNode[ nodeID ].attrType === 'BindPose' && BindPoseNode[ nodeID ].NbPoseNodes > 0 ) {
 
 
 					const poseNodes = BindPoseNode[ nodeID ].PoseNode;
 					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 ) {
 		this.register( function ( parser ) {
 
 
 			return new GLTFMaterialsTransmissionExtension( parser );
 			return new GLTFMaterialsTransmissionExtension( parser );
@@ -428,6 +434,7 @@ const EXTENSIONS = {
 	KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 	KHR_MATERIALS_CLEARCOAT: 'KHR_materials_clearcoat',
 	KHR_MATERIALS_IOR: 'KHR_materials_ior',
 	KHR_MATERIALS_IOR: 'KHR_materials_ior',
 	KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
 	KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
+	KHR_MATERIALS_SHEEN: 'KHR_materials_sheen',
 	KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 	KHR_MATERIALS_SPECULAR: 'KHR_materials_specular',
 	KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 	KHR_MATERIALS_TRANSMISSION: 'KHR_materials_transmission',
 	KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
 	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
  * Transmission Materials Extension
  *
  *
@@ -814,7 +895,7 @@ class GLTFMaterialsVolumeExtension {
 		materialParams.attenuationDistance = extension.attenuationDistance || 0;
 		materialParams.attenuationDistance = extension.attenuationDistance || 0;
 
 
 		const colorArray = extension.attenuationColor || [ 1, 1, 1 ];
 		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 );
 		return Promise.all( pending );
 
 
@@ -917,11 +998,11 @@ class GLTFMaterialsSpecularExtension {
 		}
 		}
 
 
 		const colorArray = extension.specularColorFactor || [ 1, 1, 1 ];
 		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 ) {
 		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;
 				texture.encoding = sRGBEncoding;
 
 
@@ -1887,34 +1968,6 @@ const ALPHA_MODES = {
 	BLEND: 'BLEND'
 	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
  * 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 ) {
 		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 + '".' ) );
 				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 KTX2_ALPHA_PREMULTIPLIED = 1;
 const _taskCache = new WeakMap();
 const _taskCache = new WeakMap();
 
 
+let _activeLoaders = 0;
+
 class KTX2Loader extends Loader {
 class KTX2Loader extends Loader {
 
 
 	constructor( manager ) {
 	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;
 		return this.transcoderPending;
@@ -248,6 +265,8 @@ class KTX2Loader extends Loader {
 		URL.revokeObjectURL( this.workerSourceURL );
 		URL.revokeObjectURL( this.workerSourceURL );
 		this.workerPool.dispose();
 		this.workerPool.dispose();
 
 
+		_activeLoaders--;
+
 		return this;
 		return this;
 
 
 	}
 	}

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

@@ -32,7 +32,6 @@ class TDSLoader extends Loader {
 		this.debug = false;
 		this.debug = false;
 
 
 		this.group = null;
 		this.group = null;
-		this.position = 0;
 
 
 		this.materials = [];
 		this.materials = [];
 		this.meshes = [];
 		this.meshes = [];
@@ -97,7 +96,6 @@ class TDSLoader extends Loader {
 	parse( arraybuffer, path ) {
 	parse( arraybuffer, path ) {
 
 
 		this.group = new Group();
 		this.group = new Group();
-		this.position = 0;
 		this.materials = [];
 		this.materials = [];
 		this.meshes = [];
 		this.meshes = [];
 
 
@@ -123,31 +121,30 @@ class TDSLoader extends Loader {
 	readFile( arraybuffer, path ) {
 	readFile( arraybuffer, path ) {
 
 
 		const data = new DataView( arraybuffer );
 		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 ) {
 		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 );
 					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 {
 				} 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.
 	 * Read mesh data chunk.
 	 *
 	 *
 	 * @method readMeshData
 	 * @method readMeshData
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk to read mesh from
 	 * @param {String} path Path for external resources.
 	 * @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 );
 				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.debugMessage( 'Master scale: ' + scale );
 				this.group.scale.set( scale, 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.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.debugMessage( 'Material' );
-				this.resetPosition( data );
-				this.readMaterialEntry( data, path );
+				this.readMaterialEntry( next, path );
 
 
 			} else {
 			} 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.
 	 * Read named object chunk.
 	 *
 	 *
 	 * @method readNamedObject
 	 * @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;
 				mesh.name = name;
 				this.meshes.push( mesh );
 				this.meshes.push( mesh );
-
 			} else {
 			} 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.
 	 * Read material data chunk and add it to the material list.
 	 *
 	 *
 	 * @method readMaterialEntry
 	 * @method readMaterialEntry
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @param {String} path Path for external resources.
 	 * @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 );
 				this.debugMessage( '   Name: ' + material.name );
 
 
-			} else if ( next === MAT_WIRE ) {
+			} else if ( next.id === MAT_WIRE ) {
 
 
 				this.debugMessage( '   Wireframe' );
 				this.debugMessage( '   Wireframe' );
 				material.wireframe = true;
 				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;
 				material.wireframeLinewidth = value;
 				this.debugMessage( '   Wireframe Thickness: ' + value );
 				this.debugMessage( '   Wireframe Thickness: ' + value );
 
 
-			} else if ( next === MAT_TWO_SIDE ) {
+			} else if ( next.id === MAT_TWO_SIDE ) {
 
 
 				material.side = DoubleSide;
 				material.side = DoubleSide;
 				this.debugMessage( '   DoubleSided' );
 				this.debugMessage( '   DoubleSided' );
 
 
-			} else if ( next === MAT_ADDITIVE ) {
+			} else if ( next.id === MAT_ADDITIVE ) {
 
 
 				this.debugMessage( '   Additive Blending' );
 				this.debugMessage( '   Additive Blending' );
 				material.blending = AdditiveBlending;
 				material.blending = AdditiveBlending;
 
 
-			} else if ( next === MAT_DIFFUSE ) {
+			} else if ( next.id === MAT_DIFFUSE ) {
 
 
 				this.debugMessage( '   Diffuse Color' );
 				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' );
 				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' );
 				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;
 				material.shininess = shininess * 100;
 				this.debugMessage( '   Shininess : ' + shininess );
 				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;
 				material.opacity = 1 - transparency;
 				this.debugMessage( '  Transparency : ' + transparency );
 				this.debugMessage( '  Transparency : ' + transparency );
 				material.transparent = material.opacity < 1 ? true : false;
 				material.transparent = material.opacity < 1 ? true : false;
 
 
-			} else if ( next === MAT_TEXMAP ) {
+			} else if ( next.id === MAT_TEXMAP ) {
 
 
 				this.debugMessage( '   ColorMap' );
 				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.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.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.debugMessage( '   SpecularMap' );
-				this.resetPosition( data );
-				material.specularMap = this.readMap( data, path );
+				material.specularMap = this.readMap( next, path );
 
 
 			} else {
 			} 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;
 		this.materials[ material.name ] = material;
 
 
 	}
 	}
@@ -355,13 +335,12 @@ class TDSLoader extends Loader {
 	 * Read mesh data chunk.
 	 * Read mesh data chunk.
 	 *
 	 *
 	 * @method readMesh
 	 * @method readMesh
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @return {Mesh} The parsed mesh.
 	 * @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();
 		const geometry = new BufferGeometry();
 
 
@@ -369,11 +348,11 @@ class TDSLoader extends Loader {
 		const mesh = new Mesh( geometry, material );
 		const mesh = new Mesh( geometry, material );
 		mesh.name = 'mesh';
 		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 );
 				this.debugMessage( '   Vertex: ' + points );
 
 
@@ -383,22 +362,21 @@ class TDSLoader extends Loader {
 
 
 				for ( let i = 0; i < points; i ++ )		{
 				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 ) );
 				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 );
 				this.debugMessage( '   UV: ' + texels );
 
 
@@ -406,24 +384,24 @@ class TDSLoader extends Loader {
 
 
 				const uvs = [];
 				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 ) );
 				geometry.setAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );
 
 
 
 
-			} else if ( next === MESH_MATRIX ) {
+			} else if ( next.id === MESH_MATRIX ) {
 
 
 				this.debugMessage( '   Tranformation Matrix (TODO)' );
 				this.debugMessage( '   Tranformation Matrix (TODO)' );
 
 
 				const values = [];
 				const values = [];
 				for ( let i = 0; i < 12; i ++ ) {
 				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 {
 			} 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();
 		geometry.computeVertexNormals();
 
 
 		return mesh;
 		return mesh;
@@ -483,13 +459,12 @@ class TDSLoader extends Loader {
 	 * Read face array data chunk.
 	 * Read face array data chunk.
 	 *
 	 *
 	 * @method readFaceArray
 	 * @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.
 	 * @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 );
 		this.debugMessage( '   Faces: ' + faces );
 
 
@@ -497,9 +472,9 @@ class TDSLoader extends Loader {
 
 
 		for ( let i = 0; i < faces; ++ i ) {
 		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 materialIndex = 0;
 		let start = 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 ) {
 			if ( subchunk.id === MSH_MAT_GROUP ) {
 
 
 				this.debugMessage( '      Material 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
 				const count = group.index.length * 3; // assuming successive indices
 
 
 				mesh.geometry.addGroup( start, count, materialIndex );
 				mesh.geometry.addGroup( start, count, materialIndex );
@@ -540,78 +513,71 @@ class TDSLoader extends Loader {
 
 
 			} else {
 			} 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
 		if ( mesh.material.length === 1 ) mesh.material = mesh.material[ 0 ]; // for backwards compatibility
 
 
-		this.endChunk( chunk );
-
 	}
 	}
 
 
 	/**
 	/**
 	 * Read texture map data chunk.
 	 * Read texture map data chunk.
 	 *
 	 *
 	 * @method readMap
 	 * @method readMap
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @param {String} path Path for external resources.
 	 * @param {String} path Path for external resources.
 	 * @return {Texture} Texture read from this data chunk.
 	 * @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 = {};
 		let texture = {};
 
 
 		const loader = new TextureLoader( this.manager );
 		const loader = new TextureLoader( this.manager );
 		loader.setPath( this.resourcePath || path ).setCrossOrigin( this.crossOrigin );
 		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 );
 				texture = loader.load( name );
 
 
 				this.debugMessage( '      File: ' + path + 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 );
 				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 );
 				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 );
 				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 );
 				this.debugMessage( '      RepeatY: ' + texture.repeat.y );
 
 
 			} else {
 			} 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;
 		return texture;
 
 
 	}
 	}
@@ -620,14 +586,13 @@ class TDSLoader extends Loader {
 	 * Read material group data chunk.
 	 * Read material group data chunk.
 	 *
 	 *
 	 * @method readMaterialGroup
 	 * @method readMaterialGroup
-	 * @param {Dataview} data Dataview in use.
+	 * @param {Chunk} chunk Chunk in use.
 	 * @return {Object} Object with name and index of the object.
 	 * @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( '         Name: ' + name );
 		this.debugMessage( '         Faces: ' + numFaces );
 		this.debugMessage( '         Faces: ' + numFaces );
@@ -635,7 +600,7 @@ class TDSLoader extends Loader {
 		const index = [];
 		const index = [];
 		for ( let i = 0; i < numFaces; ++ i ) {
 		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.
 	 * Read a color value.
 	 *
 	 *
 	 * @method readColor
 	 * @method readColor
-	 * @param {DataView} data Dataview.
+	 * @param {Chunk} chunk Chunk.
 	 * @return {Color} Color value read..
 	 * @return {Color} Color value read..
 	 */
 	 */
-	readColor( data ) {
+	readColor( chunk ) {
 
 
-		const chunk = this.readChunk( data );
+		const subChunk = chunk.readChunk( );
 		const color = new Color();
 		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 );
 			color.setRGB( r / 255, g / 255, b / 255 );
 
 
 			this.debugMessage( '      Color: ' + color.r + ', ' + color.g + ', ' + color.b );
 			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 );
 			color.setRGB( r, g, b );
 
 
@@ -677,101 +642,142 @@ class TDSLoader extends Loader {
 
 
 		}	else {
 		}	else {
 
 
-			this.debugMessage( '      Unknown color chunk: ' + chunk.toString( 16 ) );
+			this.debugMessage( '      Unknown color chunk: ' + subChunk.hexId );
 
 
 		}
 		}
 
 
-		this.endChunk( chunk );
 		return color;
 		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 ) {
 		}	catch ( e ) {
-
 			this.debugMessage( 'Unable to read chunk at ' + this.position );
 			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.
 	 * Read byte value.
 	 *
 	 *
 	 * @method readByte
 	 * @method readByte
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 * @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;
 		this.position += 1;
 		return v;
 		return v;
 
 
@@ -781,20 +787,20 @@ class TDSLoader extends Loader {
 	 * Read 32 bit float value.
 	 * Read 32 bit float value.
 	 *
 	 *
 	 * @method readFloat
 	 * @method readFloat
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 * @return {Number} Data read from the dataview.
 	 */
 	 */
-	readFloat( data ) {
+	readFloat() {
 
 
 		try {
 		try {
 
 
-			const v = data.getFloat32( this.position, true );
+			const v = this.data.getFloat32( this.position, true );
 			this.position += 4;
 			this.position += 4;
 			return v;
 			return v;
 
 
 		}	catch ( e ) {
 		}	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.
 	 * Read 32 bit signed integer value.
 	 *
 	 *
 	 * @method readInt
 	 * @method readInt
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 * @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;
 		this.position += 4;
 		return v;
 		return v;
 
 
@@ -819,12 +824,11 @@ class TDSLoader extends Loader {
 	 * Read 16 bit signed integer value.
 	 * Read 16 bit signed integer value.
 	 *
 	 *
 	 * @method readShort
 	 * @method readShort
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 * @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;
 		this.position += 2;
 		return v;
 		return v;
 
 
@@ -834,12 +838,11 @@ class TDSLoader extends Loader {
 	 * Read 64 bit unsigned integer value.
 	 * Read 64 bit unsigned integer value.
 	 *
 	 *
 	 * @method readDWord
 	 * @method readDWord
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 * @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;
 		this.position += 4;
 		return v;
 		return v;
 
 
@@ -849,95 +852,32 @@ class TDSLoader extends Loader {
 	 * Read 32 bit unsigned integer value.
 	 * Read 32 bit unsigned integer value.
 	 *
 	 *
 	 * @method readWord
 	 * @method readWord
-	 * @param {DataView} data Dataview to read data from.
 	 * @return {Number} Data read from the dataview.
 	 * @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;
 		this.position += 2;
 		return v;
 		return v;
 
 
 	}
 	}
 
 
 	/**
 	/**
-	 * Read string value.
+	 * Read NULL terminated ASCII string value from chunk-pos.
 	 *
 	 *
 	 * @method readString
 	 * @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.
 	 * @return {String} Data read from the dataview.
 	 */
 	 */
-	readString( data, maxLength ) {
+	readString() {
 
 
 		let s = '';
 		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 );
 			s += String.fromCharCode( c );
-
+			c = this.readByte();
 		}
 		}
 
 
 		return s;
 		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',
 	'environment',
 	'mask',
 	'mask',
 	'position',
 	'position',
-	'sheenTint'
+	'sheenColor'
 ] );
 ] );
 
 
 export { StandardNodeMaterial };
 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
 			// 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 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;
 			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.environment ) this.environment = source.environment;
 
 
-		if ( source.sheenTint ) this.sheenTint = source.sheenTint;
+		if ( source.sheenColor ) this.sheenColor = source.sheenColor;
 
 
 		return this;
 		return this;
 
 
@@ -593,7 +593,7 @@ class StandardNode extends Node {
 
 
 			if ( this.environment ) data.environment = this.environment.toJSON( meta ).uuid;
 			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,
 	BufferAttribute,
 	BufferGeometry,
 	BufferGeometry,
 	Color,
 	Color,
-	ImmediateRenderObject,
-	NoColors
+	DynamicDrawUsage,
+	Mesh
 } from '../../../build/three.module.js';
 } from '../../../build/three.module.js';
 
 
 /**
 /**
  * Port of http://webglsamples.org/blob/blob.html
  * 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;
 		const scope = this;
 
 
@@ -24,8 +26,8 @@ class MarchingCubes extends ImmediateRenderObject {
 		const nlist = new Float32Array( 12 * 3 );
 		const nlist = new Float32Array( 12 * 3 );
 		const clist = 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
 		// functions have to be object properties
 		// prototype functions kill performance
 		// prototype functions kill performance
@@ -56,28 +58,37 @@ class MarchingCubes extends ImmediateRenderObject {
 			this.normal_cache = new Float32Array( this.size3 * 3 );
 			this.normal_cache = new Float32Array( this.size3 * 3 );
 			this.palette = 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.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 ) {
 			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 ) {
 			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.
 		// Returns total number of triangles. Fills triangles.
 		// (this is where most of time is spent - it's inner work of O(n3) loop )
 		// (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
 			// cache indices
 			const q1 = q + 1,
 			const q1 = q + 1,
@@ -369,8 +380,7 @@ class MarchingCubes extends ImmediateRenderObject {
 					clist,
 					clist,
 					3 * triTable[ o1 ],
 					3 * triTable[ o1 ],
 					3 * triTable[ o2 ],
 					3 * triTable[ o2 ],
-					3 * triTable[ o3 ],
-					renderCallback
+					3 * triTable[ o3 ]
 				);
 				);
 
 
 				i += 3;
 				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;
 			const c = scope.count * 3;
 
 
@@ -477,69 +483,8 @@ class MarchingCubes extends ImmediateRenderObject {
 
 
 			scope.count += 3;
 			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
 		// 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.
 			// Triangulate. Yeah, this is slow.
 
 
@@ -890,7 +835,7 @@ class MarchingCubes extends ImmediateRenderObject {
 						const fx = ( x - this.halfsize ) / this.halfsize; //+ 1
 						const fx = ( x - this.halfsize ) / this.halfsize; //+ 1
 						const q = y_offset + x;
 						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:
 // 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.
 // who in turn got them from Cory Gene Bloyd.
 
 
 const edgeTable = new Int32Array( [
 const edgeTable = new Int32Array( [

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

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

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

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

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

@@ -1,8 +1,8 @@
 // core
 // core
 import ArrayInputNode from './core/ArrayInputNode.js';
 import ArrayInputNode from './core/ArrayInputNode.js';
 import AttributeNode from './core/AttributeNode.js';
 import AttributeNode from './core/AttributeNode.js';
+import BypassNode from './core/BypassNode.js';
 import CodeNode from './core/CodeNode.js';
 import CodeNode from './core/CodeNode.js';
-import ConstNode from './core/ConstNode.js';
 import ContextNode from './core/ContextNode.js';
 import ContextNode from './core/ContextNode.js';
 import ExpressionNode from './core/ExpressionNode.js';
 import ExpressionNode from './core/ExpressionNode.js';
 import FunctionCallNode from './core/FunctionCallNode.js';
 import FunctionCallNode from './core/FunctionCallNode.js';
@@ -15,13 +15,10 @@ import NodeCode from './core/NodeCode.js';
 import NodeFrame from './core/NodeFrame.js';
 import NodeFrame from './core/NodeFrame.js';
 import NodeFunctionInput from './core/NodeFunctionInput.js';
 import NodeFunctionInput from './core/NodeFunctionInput.js';
 import NodeKeywords from './core/NodeKeywords.js';
 import NodeKeywords from './core/NodeKeywords.js';
-import NodeSlot from './core/NodeSlot.js';
 import NodeUniform from './core/NodeUniform.js';
 import NodeUniform from './core/NodeUniform.js';
 import NodeVar from './core/NodeVar.js';
 import NodeVar from './core/NodeVar.js';
 import NodeVary from './core/NodeVary.js';
 import NodeVary from './core/NodeVary.js';
 import PropertyNode from './core/PropertyNode.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 TempNode from './core/TempNode.js';
 import VarNode from './core/VarNode.js';
 import VarNode from './core/VarNode.js';
 import VaryNode from './core/VaryNode.js';
 import VaryNode from './core/VaryNode.js';
@@ -37,11 +34,13 @@ import Object3DNode from './accessors/Object3DNode.js';
 import PointUVNode from './accessors/PointUVNode.js';
 import PointUVNode from './accessors/PointUVNode.js';
 import PositionNode from './accessors/PositionNode.js';
 import PositionNode from './accessors/PositionNode.js';
 import ReferenceNode from './accessors/ReferenceNode.js';
 import ReferenceNode from './accessors/ReferenceNode.js';
+import SkinningNode from './accessors/SkinningNode.js';
 import UVNode from './accessors/UVNode.js';
 import UVNode from './accessors/UVNode.js';
 
 
 // inputs
 // inputs
 import ColorNode from './inputs/ColorNode.js';
 import ColorNode from './inputs/ColorNode.js';
 import FloatNode from './inputs/FloatNode.js';
 import FloatNode from './inputs/FloatNode.js';
+import IntNode from './inputs/IntNode.js';
 import Matrix3Node from './inputs/Matrix3Node.js';
 import Matrix3Node from './inputs/Matrix3Node.js';
 import Matrix4Node from './inputs/Matrix3Node.js';
 import Matrix4Node from './inputs/Matrix3Node.js';
 import TextureNode from './inputs/TextureNode.js';
 import TextureNode from './inputs/TextureNode.js';
@@ -63,6 +62,7 @@ import LightNode from './lights/LightNode.js';
 import LightsNode from './lights/LightsNode.js';
 import LightsNode from './lights/LightsNode.js';
 
 
 // utils
 // utils
+import ArrayElementNode from './utils/ArrayElementNode.js';
 import JoinNode from './utils/JoinNode.js';
 import JoinNode from './utils/JoinNode.js';
 import SplitNode from './utils/SplitNode.js';
 import SplitNode from './utils/SplitNode.js';
 import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
 import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
@@ -76,21 +76,19 @@ export * from './core/constants.js';
 
 
 // functions
 // functions
 export * from './functions/BSDFs.js';
 export * from './functions/BSDFs.js';
-export * from './functions/EncodingFunctions.js';
-export * from './functions/MathFunctions.js';
-
-// consts
-export * from './consts/MathConsts.js';
 
 
 // materials
 // materials
 export * from './materials/Materials.js';
 export * from './materials/Materials.js';
 
 
+// shader node
+export * from './ShaderNode.js';
+
 export {
 export {
 	// core
 	// core
 	ArrayInputNode,
 	ArrayInputNode,
 	AttributeNode,
 	AttributeNode,
+	BypassNode,
 	CodeNode,
 	CodeNode,
-	ConstNode,
 	ContextNode,
 	ContextNode,
 	ExpressionNode,
 	ExpressionNode,
 	FunctionCallNode,
 	FunctionCallNode,
@@ -103,13 +101,10 @@ export {
 	NodeFrame,
 	NodeFrame,
 	NodeFunctionInput,
 	NodeFunctionInput,
 	NodeKeywords,
 	NodeKeywords,
-	NodeSlot,
 	NodeUniform,
 	NodeUniform,
 	NodeVar,
 	NodeVar,
 	NodeVary,
 	NodeVary,
 	PropertyNode,
 	PropertyNode,
-	StructNode,
-	StructVarNode,
 	TempNode,
 	TempNode,
 	VarNode,
 	VarNode,
 	VaryNode,
 	VaryNode,
@@ -125,11 +120,13 @@ export {
 	PointUVNode,
 	PointUVNode,
 	PositionNode,
 	PositionNode,
 	ReferenceNode,
 	ReferenceNode,
+	SkinningNode,
 	UVNode,
 	UVNode,
 
 
 	// inputs
 	// inputs
 	ColorNode,
 	ColorNode,
 	FloatNode,
 	FloatNode,
+	IntNode,
 	Matrix3Node,
 	Matrix3Node,
 	Matrix4Node,
 	Matrix4Node,
 	TextureNode,
 	TextureNode,
@@ -151,6 +148,7 @@ export {
 	LightsNode,
 	LightsNode,
 
 
 	// utils
 	// utils
+	ArrayElementNode,
 	JoinNode,
 	JoinNode,
 	SplitNode,
 	SplitNode,
 	SpriteSheetUVNode,
 	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
 // inputs
 import ColorNode from './inputs/ColorNode.js';
 import ColorNode from './inputs/ColorNode.js';
 import FloatNode from './inputs/FloatNode.js';
 import FloatNode from './inputs/FloatNode.js';
@@ -5,11 +9,17 @@ import Vector2Node from './inputs/Vector2Node.js';
 import Vector3Node from './inputs/Vector3Node.js';
 import Vector3Node from './inputs/Vector3Node.js';
 import Vector4Node from './inputs/Vector4Node.js';
 import Vector4Node from './inputs/Vector4Node.js';
 
 
+// accessors
+import PositionNode from './accessors/PositionNode.js';
+import NormalNode from './accessors/NormalNode.js';
+
 // math
 // math
-import MathNode from './math/MathNode.js';
 import OperatorNode from './math/OperatorNode.js';
 import OperatorNode from './math/OperatorNode.js';
+import CondNode from './math/CondNode.js';
+import MathNode from './math/MathNode.js';
 
 
 // utils
 // utils
+import ArrayElementNode from './utils/ArrayElementNode.js';
 import JoinNode from './utils/JoinNode.js';
 import JoinNode from './utils/JoinNode.js';
 import SplitNode from './utils/SplitNode.js';
 import SplitNode from './utils/SplitNode.js';
 
 
@@ -18,17 +28,35 @@ import { Vector2, Vector3, Vector4, Color } from 'three';
 
 
 const NodeHandler = {
 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 ) {
 		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;
 	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;
 	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
 // Node Material Shader Syntax
 //
 //
 
 
-export const uniform = ShaderNodeScript( ( inputNode ) => {
+export const uniform = new ShaderNode( ( inputNode ) => {
 
 
 	inputNode.setConst( false );
 	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 ) {
 	getNodeType( builder ) {
 
 
 		const scope = this.scope;
 		const scope = this.scope;
-		const material = builder.getContextValue( 'material' );
+		const material = builder.context.material;
 
 
 		if ( scope === MaterialNode.COLOR ) {
 		if ( scope === MaterialNode.COLOR ) {
 
 
@@ -46,7 +46,7 @@ class MaterialNode extends Node {
 
 
 	generate( builder, output ) {
 	generate( builder, output ) {
 
 
-		const material = builder.getContextValue( 'material' );
+		const material = builder.context.material;
 		const scope = this.scope;
 		const scope = this.scope;
 
 
 		let node = null;
 		let node = null;
@@ -85,15 +85,15 @@ class MaterialNode extends Node {
 
 
 		} else if ( scope === MaterialNode.SPECULAR ) {
 		} 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 {
 			} 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.position = position;
 
 
-		this._mvpMatrix = new OperatorNode( '*', new CameraNode( CameraNode.PROJECTION_MATRIX ), new ModelNode( ModelNode.VIEW_MATRIX ) );
-
 	}
 	}
 
 
 	generate( builder ) {
 	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 Node from '../core/Node.js';
 import AttributeNode from '../core/AttributeNode.js';
 import AttributeNode from '../core/AttributeNode.js';
+import NodeKeywords from '../core/NodeKeywords.js';
 import VaryNode from '../core/VaryNode.js';
 import VaryNode from '../core/VaryNode.js';
 import ModelNode from '../accessors/ModelNode.js';
 import ModelNode from '../accessors/ModelNode.js';
 import CameraNode from '../accessors/CameraNode.js';
 import CameraNode from '../accessors/CameraNode.js';
 import OperatorNode from '../math/OperatorNode.js';
 import OperatorNode from '../math/OperatorNode.js';
 import MathNode from '../math/MathNode.js';
 import MathNode from '../math/MathNode.js';
-import { inverseTransformDirection } from '../functions/MathFunctions.js';
 
 
 class NormalNode extends Node {
 class NormalNode extends Node {
 
 
+	static GEOMETRY = 'geometry';
 	static LOCAL = 'local';
 	static LOCAL = 'local';
 	static WORLD = 'world';
 	static WORLD = 'world';
 	static VIEW = 'view';
 	static VIEW = 'view';
@@ -21,16 +22,26 @@ class NormalNode extends Node {
 
 
 	}
 	}
 
 
+	getHash( /*builder*/ ) {
+
+		return `normal-${this.scope}`;
+
+	}
+
 	generate( builder ) {
 	generate( builder ) {
 
 
 		const scope = this.scope;
 		const scope = this.scope;
 
 
 		let outputNode = null;
 		let outputNode = null;
 
 
-		if ( scope === NormalNode.LOCAL ) {
+		if ( scope === NormalNode.GEOMETRY ) {
 
 
 			outputNode = new AttributeNode( 'normal', 'vec3' );
 			outputNode = new AttributeNode( 'normal', 'vec3' );
 
 
+		} else if ( scope === NormalNode.LOCAL ) {
+
+			outputNode = new VaryNode( new NormalNode( NormalNode.GEOMETRY ) );
+
 		} else if ( scope === NormalNode.VIEW ) {
 		} else if ( scope === NormalNode.VIEW ) {
 
 
 			const vertexNormalNode = new OperatorNode( '*', new ModelNode( ModelNode.NORMAL_MATRIX ), new NormalNode( NormalNode.LOCAL ) );
 			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 ) {
 		} 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 ) );
 			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' );
 		super( 'vec2' );
 
 
-		Object.defineProperty( this, 'isPointUVNode', { value: true } );
-
 	}
 	}
 
 
-	generate( builder ) {
+	generate( /*builder*/ ) {
 
 
 		return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
 		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;
 export default PointUVNode;

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

@@ -1,13 +1,14 @@
 import Node from '../core/Node.js';
 import Node from '../core/Node.js';
 import AttributeNode from '../core/AttributeNode.js';
 import AttributeNode from '../core/AttributeNode.js';
+import NodeKeywords from '../core/NodeKeywords.js';
 import VaryNode from '../core/VaryNode.js';
 import VaryNode from '../core/VaryNode.js';
 import ModelNode from '../accessors/ModelNode.js';
 import ModelNode from '../accessors/ModelNode.js';
 import MathNode from '../math/MathNode.js';
 import MathNode from '../math/MathNode.js';
 import OperatorNode from '../math/OperatorNode.js';
 import OperatorNode from '../math/OperatorNode.js';
-import { transformDirection } from '../functions/MathFunctions.js';
 
 
 class PositionNode extends Node {
 class PositionNode extends Node {
 
 
+	static GEOMETRY = 'geometry';
 	static LOCAL = 'local';
 	static LOCAL = 'local';
 	static WORLD = 'world';
 	static WORLD = 'world';
 	static VIEW = 'view';
 	static VIEW = 'view';
@@ -21,19 +22,29 @@ class PositionNode extends Node {
 
 
 	}
 	}
 
 
+	getHash( /*builder*/ ) {
+
+		return `position-${this.scope}`;
+
+	}
+
 	generate( builder ) {
 	generate( builder ) {
 
 
 		const scope = this.scope;
 		const scope = this.scope;
 
 
 		let outputNode = null;
 		let outputNode = null;
 
 
-		if ( scope === PositionNode.LOCAL ) {
-			
+		if ( scope === PositionNode.GEOMETRY ) {
+
 			outputNode = new AttributeNode( 'position', 'vec3' );
 			outputNode = new AttributeNode( 'position', 'vec3' );
-			
+
+		} else if ( scope === PositionNode.LOCAL ) {
+
+			outputNode = new VaryNode( new PositionNode( PositionNode.GEOMETRY ) );
+
 		} else if ( scope === PositionNode.WORLD ) {
 		} 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 );
 			outputNode = new VaryNode( vertexPositionNode );
 
 
 		} else if ( scope === PositionNode.VIEW ) {
 		} 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 {
 class UVNode extends AttributeNode {
 
 
-	constructor( index = 0 ) {
+	constructor( value = 0 ) {
 
 
 		super( null, 'vec2' );
 		super( null, 'vec2' );
 
 
-		this.index = index;
-
-		Object.defineProperty( this, 'isUVNode', { value: true } );
+		this.value = value;
 
 
 	}
 	}
 
 
 	getAttributeName( /*builder*/ ) {
 	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;
 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 {
 class ArrayInputNode extends InputNode {
 
 
-	constructor( value = [] ) {
+	constructor( nodes = [] ) {
 
 
 		super();
 		super();
 
 
-		this.value = value;
+		this.nodes = nodes;
 
 
 	}
 	}
 
 
 	getNodeType( builder ) {
 	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 ) {
 	setAttributeName( attributeName ) {
 
 
 		this._attributeName = 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 = [];
 		this._includes = [];
 
 
-		Object.defineProperty( this, 'isCodeNode', { value: true } );
-
 	}
 	}
 
 
 	setIncludes( includes ) {
 	setIncludes( includes ) {
@@ -34,7 +32,7 @@ class CodeNode extends Node {
 
 
 		if ( this.useKeywords === true ) {
 		if ( this.useKeywords === true ) {
 
 
-			const contextKeywords = builder.getContextValue( 'keywords' );
+			const contextKeywords = builder.context.keywords;
 
 
 			if ( contextKeywords !== undefined ) {
 			if ( contextKeywords !== undefined ) {
 
 
@@ -75,4 +73,6 @@ class CodeNode extends Node {
 
 
 }
 }
 
 
+CodeNode.prototype.isCodeNode = true;
+
 export default CodeNode;
 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 {
 class ContextNode extends Node {
 
 
-	constructor( node, nodeType, context = {} ) {
+	constructor( node, context = {} ) {
 
 
-		super( nodeType );
+		super();
 
 
 		this.node = node;
 		this.node = node;
-
 		this.context = context;
 		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 ) {
 	getNodeType( builder ) {
@@ -50,4 +33,6 @@ class ContextNode extends Node {
 
 
 }
 }
 
 
+ContextNode.prototype.isContextNode = true;
+
 export default ContextNode;
 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 {
 class ExpressionNode extends TempNode {
 
 
-	constructor( snipped = '', nodeType = null ) {
+	constructor( snipped = '', nodeType = 'void' ) {
 
 
 		super( nodeType );
 		super( nodeType );
 
 
@@ -12,7 +12,18 @@ class ExpressionNode extends TempNode {
 
 
 	generate( builder ) {
 	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 CodeNode from './CodeNode.js';
-import NodeFunctionInput from './NodeFunctionInput.js';
 import FunctionCallNode from './FunctionCallNode.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 {
 class FunctionNode extends CodeNode {
 
 
 	constructor( code = '' ) {
 	constructor( code = '' ) {
 
 
 		super( code );
 		super( code );
 
 
-		this.inputs = [];
-
-		this.name = '';
-		this.needsUpdate = true;
-
 		this.useKeywords = true;
 		this.useKeywords = true;
 
 
-		this.presicion = '';
-
-		this._includeCode = '';
-		this._internalCode = '';
-
 	}
 	}
 
 
 	getNodeType( builder ) {
 	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 );
 		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 );
 		const nodeCode = builder.getCodeFromNode( this, type );
 
 
-		if ( this.name !== '' ) {
+		if ( name !== '' ) {
 
 
 			// use a custom property name
 			// use a custom property name
 
 
-			nodeCode.name = this.name;
+			nodeCode.name = name;
 
 
 		}
 		}
 
 
 		const propertyName = builder.getPropertyName( nodeCode );
 		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' ) {
 		if ( output === 'property' ) {
 
 
@@ -189,7 +76,7 @@ class FunctionNode extends CodeNode {
 
 
 		} else {
 		} 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 {
 class InputNode extends Node {
 
 
-	constructor( nodeType ) {
+	constructor( inputType ) {
 
 
-		super( nodeType );
+		super( inputType );
+
+		this.inputType = inputType;
 
 
 		this.constant = false;
 		this.constant = false;
 
 
@@ -24,6 +26,12 @@ class InputNode extends Node {
 
 
 	}
 	}
 
 
+	getInputType( builder ) {
+
+		return this.inputType;
+
+	}
+
 	generateConst( builder ) {
 	generateConst( builder ) {
 
 
 		return builder.getConst( this.getNodeType( builder ), this.value );
 		return builder.getConst( this.getNodeType( builder ), this.value );
@@ -40,7 +48,9 @@ class InputNode extends Node {
 
 
 		} else {
 		} 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 );
 			const propertyName = builder.getPropertyName( nodeUniform );
 
 
 			return builder.format( propertyName, type, output );
 			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 { NodeUpdateType } from './constants.js';
 
 
+import { MathUtils } from 'three';
+
 class Node {
 class Node {
 
 
 	constructor( nodeType = null ) {
 	constructor( nodeType = null ) {
@@ -8,6 +10,8 @@ class Node {
 
 
 		this.updateType = NodeUpdateType.None;
 		this.updateType = NodeUpdateType.None;
 
 
+		this.uuid = MathUtils.generateUUID();
+
 	}
 	}
 
 
 	get type() {
 	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 ) {
 	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.addNode( this );
+		builder.addStack( this );
 
 
 		const isGenerateOnce = this.generate.length === 1;
 		const isGenerateOnce = this.generate.length === 1;
 
 
+		let snippet = null;
+
 		if ( isGenerateOnce ) {
 		if ( isGenerateOnce ) {
 
 
 			const type = this.getNodeType( builder );
 			const type = this.getNodeType( builder );
 			const nodeData = builder.getDataFromNode( this );
 			const nodeData = builder.getDataFromNode( this );
 
 
-			let snippet = nodeData.snippet;
+			snippet = nodeData.snippet;
 
 
 			if ( snippet === undefined ) {
 			if ( snippet === undefined ) {
 
 
-				snippet = this.generate( builder );
+				snippet = this.generate( builder ) || '';
 
 
 				nodeData.snippet = snippet;
 				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 NodeKeywords from './NodeKeywords.js';
 import { NodeUpdateType } from './constants.js';
 import { NodeUpdateType } from './constants.js';
 
 
-import { LinearEncoding } from 'three';
+import { REVISION, LinearEncoding } from 'three';
+
+const shaderStages = [ 'fragment', 'vertex' ];
 
 
 class NodeBuilder {
 class NodeBuilder {
 
 
-	constructor( material, renderer ) {
+	constructor( object, renderer, parser ) {
 
 
-		this.material = material;
+		this.object = object;
+		this.material = object.material;
 		this.renderer = renderer;
 		this.renderer = renderer;
+		this.parser = parser;
 
 
 		this.nodes = [];
 		this.nodes = [];
 		this.updateNodes = [];
 		this.updateNodes = [];
+		this.hashNodes = {};
 
 
 		this.vertexShader = null;
 		this.vertexShader = null;
 		this.fragmentShader = 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.uniforms = { vertex: [], fragment: [], index: 0 };
 		this.codes = { vertex: [], fragment: [] };
 		this.codes = { vertex: [], fragment: [] };
 		this.attributes = [];
 		this.attributes = [];
 		this.varys = [];
 		this.varys = [];
 		this.vars = { vertex: [], fragment: [] };
 		this.vars = { vertex: [], fragment: [] };
 		this.flow = { code: '' };
 		this.flow = { code: '' };
+		this.stack = [];
 
 
 		this.context = {
 		this.context = {
 			keywords: new NodeKeywords(),
 			keywords: new NodeKeywords(),
-			material: material
+			material: object.material
 		};
 		};
 
 
 		this.nodesData = new WeakMap();
 		this.nodesData = new WeakMap();
+		this.flowsData = new WeakMap();
 
 
 		this.shaderStage = null;
 		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.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 ) {
 	getConst( type, value ) {
 
 
 		if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
 		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.` );
 		throw new Error( `NodeBuilder: Type '${type}' not found in generate constant attempt.` );
 
 
 	}
 	}
 
 
+	getType( type ) {
+
+		return type;
+
+	}
+
+	generateMethod( method ) {
+
+		return method;
+
+	}
+
 	getAttribute( name, type ) {
 	getAttribute( name, type ) {
 
 
 		const attributes = this.attributes;
 		const attributes = this.attributes;
@@ -140,7 +189,7 @@ class NodeBuilder {
 
 
 	}
 	}
 
 
-	getPropertyName( node ) {
+	getPropertyName( node/*, shaderStage*/ ) {
 
 
 		return node.name;
 		return node.name;
 
 
@@ -334,30 +383,33 @@ class NodeBuilder {
 
 
 	addFlowCode( code ) {
 	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;
 		const previousFlow = this.flow;
 
 
@@ -381,17 +433,15 @@ class NodeBuilder {
 
 
 		this.setShaderStage( shaderStage );
 		this.setShaderStage( shaderStage );
 
 
-		const flowData = this.flowNode( node, output );
+		const flowData = this.flowChildNode( node, output );
 
 
 		if ( propertyName !== null ) {
 		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 );
 		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 ) {
 	getAttributes( shaderStage ) {
 
 
 		let snippet = '';
 		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 ) {
 		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;
 		return this;
 
 
@@ -577,31 +580,42 @@ class NodeBuilder {
 
 
 		switch ( typeToType ) {
 		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 {
 class NodeFunctionInput {
 
 
-	constructor( type, name, qualifier = '', isConst = false, count = 0 ) {
+	constructor( type, name, count = null, qualifier = '', isConst = false ) {
 
 
 		this.type = type;
 		this.type = type;
 		this.name = name;
 		this.name = name;
+		this.count = count;
 		this.qualifier = qualifier;
 		this.qualifier = qualifier;
 		this.isConst = isConst;
 		this.isConst = isConst;
-		this.count = count;
-
-		Object.defineProperty( this, 'isNodeFunction', { value: true } );
 
 
 	}
 	}
 
 
 }
 }
 
 
+NodeFunctionInput.isNodeFunctionInput = true;
+
 export default NodeFunctionInput;
 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 {
 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() {
 	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.nodes = [];
+		this.keywordsCallback = {};
 
 
 	}
 	}
 
 
@@ -77,120 +12,24 @@ class NodeKeywords {
 
 
 		let node = this.nodes[ name ];
 		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 codeKeywords = code.match( regExp );
 
 
-		const keywords = [];
+		const keywordNodes = [];
 
 
 		if ( codeKeywords !== null ) {
 		if ( codeKeywords !== null ) {
 
 
@@ -210,9 +49,9 @@ class NodeKeywords {
 
 
 				const node = this.getNode( keyword );
 				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 ) {
 	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 );
 			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.name = name;
 		this.type = type;
 		this.type = type;
 
 
-		Object.defineProperty( this, 'isNodeVar', { value: true } );
-
 	}
 	}
 
 
 }
 }
 
 
+NodeVar.prototype.isNodeVar = true;
+
 export default NodeVar;
 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 {
 class PropertyNode extends Node {
 
 
-	constructor( name, nodeType ) {
+	constructor( name = null, nodeType = 'vec4' ) {
 
 
 		super( nodeType );
 		super( nodeType );
 
 
@@ -10,10 +10,22 @@ class PropertyNode extends Node {
 
 
 	}
 	}
 
 
+	getHash( builder ) {
+
+		return this.name || super.getHash( builder );
+
+	}
+
 	generate( builder ) {
 	generate( builder ) {
 
 
 		const nodeVary = builder.getVarFromNode( this, this.getNodeType( 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 );
 		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 ) );
 		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 );
 			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}` );
 				builder.addFlowCode( `${propertyName} = ${snippet}` );
 
 
 				nodeData.snippet = 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 {
 class VarNode extends Node {
 
 
-	constructor( value, name = '', nodeType = null ) {
+	constructor( node, name = null, nodeType = null ) {
 
 
 		super( nodeType );
 		super( nodeType );
 
 
-		this.value = value;
+		this.node = node;
 		this.name = name;
 		this.name = name;
 
 
 	}
 	}
 
 
+	getHash( builder ) {
+
+		return this.name || super.getHash( builder );
+
+	}
+
 	getNodeType( builder ) {
 	getNodeType( builder ) {
 
 
-		return super.getNodeType( builder ) || this.value.getNodeType( builder );
+		return super.getNodeType( builder ) || this.node.getNodeType( builder );
 
 
 	}
 	}
 
 
 	generate( builder ) {
 	generate( builder ) {
 
 
 		const type = builder.getVectorType( this.getNodeType( builder ) );
 		const type = builder.getVectorType( this.getNodeType( builder ) );
+		const node = this.node;
 		const name = this.name;
 		const name = this.name;
-		const value = this.value;
 
 
+		const snippet = node.build( builder, type );
 		const nodeVar = builder.getVarFromNode( this, type );
 		const nodeVar = builder.getVarFromNode( this, type );
 
 
-		const snippet = value.build( builder, type );
-
-		if ( name !== '' ) {
+		if ( name !== null ) {
 
 
 			nodeVar.name = name;
 			nodeVar.name = name;
 
 

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