Browse Source

Merge branch 'dev' of git://github.com/mrdoob/three.js into mrdoob-dev

Rik Cabanier 3 years ago
parent
commit
08c8767abf
92 changed files with 1137 additions and 1549 deletions
  1. 1 2
      docs/api/ar/cameras/CubeCamera.html
  2. 1 2
      docs/api/en/cameras/CubeCamera.html
  3. 0 1
      docs/api/en/lights/AmbientLight.html
  4. 0 1
      docs/api/en/materials/ShaderMaterial.html
  5. 1 2
      docs/api/ko/cameras/CubeCamera.html
  6. 1 2
      docs/api/zh/cameras/CubeCamera.html
  7. 34 43
      docs/api/zh/extras/core/Curve.html
  8. 0 1
      docs/api/zh/lights/AmbientLight.html
  9. 0 1
      docs/api/zh/materials/ShaderMaterial.html
  10. 1 1
      docs/index.html
  11. 3 3
      docs/manual/ko/buildTools/Testing-with-NPM.html
  12. 3 4
      docs/manual/ko/introduction/Animation-system.html
  13. 3 3
      docs/manual/ko/introduction/FAQ.html
  14. 4 4
      docs/manual/ko/introduction/How-to-dispose-of-objects.html
  15. 3 4
      docs/manual/ko/introduction/How-to-run-things-locally.html
  16. 3 5
      docs/manual/ko/introduction/Installation.html
  17. 1 1
      docs/manual/ko/introduction/Useful-links.html
  18. 12 10
      editor/css/main.css
  19. 1 3
      editor/js/Menubar.File.js
  20. 6 0
      editor/js/Player.js
  21. 10 4
      editor/js/Resizer.js
  22. 1 1
      editor/sw.js
  23. 0 2
      examples/files.json
  24. 11 1
      examples/js/exporters/USDZExporter.js
  25. 115 0
      examples/js/geometries/ParametricGeometry.js
  26. 49 0
      examples/js/geometries/TextGeometry.js
  27. 183 0
      examples/js/loaders/FontLoader.js
  28. 89 42
      examples/js/loaders/GLTFLoader.js
  29. 4 5
      examples/js/loaders/RGBMLoader.js
  30. 1 0
      examples/js/loaders/SVGLoader.js
  31. 0 3
      examples/js/postprocessing/SSRPass.js
  32. 1 4
      examples/js/postprocessing/SSRrPass.js
  33. 3 3
      examples/js/utils/PackedPhongMaterial.js
  34. 12 1
      examples/jsm/exporters/USDZExporter.js
  35. 1 1
      examples/jsm/geometries/ParametricGeometry.js
  36. 1 1
      examples/jsm/geometries/TextGeometry.js
  37. 67 12
      examples/jsm/loaders/GLTFLoader.js
  38. 4 4
      examples/jsm/loaders/RGBMLoader.js
  39. 1 0
      examples/jsm/loaders/SVGLoader.js
  40. 7 1
      examples/jsm/renderers/nodes/Nodes.js
  41. 193 0
      examples/jsm/renderers/nodes/ShaderNode.js
  42. 7 25
      examples/jsm/renderers/nodes/accessors/CameraNode.js
  43. 2 4
      examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js
  44. 10 42
      examples/jsm/renderers/nodes/accessors/NormalNode.js
  45. 9 33
      examples/jsm/renderers/nodes/accessors/Object3DNode.js
  46. 2 5
      examples/jsm/renderers/nodes/accessors/PointUVNode.js
  47. 15 58
      examples/jsm/renderers/nodes/accessors/PositionNode.js
  48. 2 2
      examples/jsm/renderers/nodes/accessors/ReferenceNode.js
  49. 5 18
      examples/jsm/renderers/nodes/core/AttributeNode.js
  50. 2 4
      examples/jsm/renderers/nodes/core/CodeNode.js
  51. 3 4
      examples/jsm/renderers/nodes/core/ConstNode.js
  52. 4 7
      examples/jsm/renderers/nodes/core/ExpressionNode.js
  53. 2 5
      examples/jsm/renderers/nodes/core/FunctionCallNode.js
  54. 21 0
      examples/jsm/renderers/nodes/core/Node.js
  55. 3 7
      examples/jsm/renderers/nodes/core/PropertyNode.js
  56. 2 2
      examples/jsm/renderers/nodes/core/StructVarNode.js
  57. 13 3
      examples/jsm/renderers/nodes/core/TempNode.js
  58. 2 2
      examples/jsm/renderers/nodes/core/VarNode.js
  59. 2 2
      examples/jsm/renderers/nodes/core/VaryNode.js
  60. 10 18
      examples/jsm/renderers/nodes/display/ColorSpaceNode.js
  61. 11 22
      examples/jsm/renderers/nodes/display/NormalMapNode.js
  62. 3 5
      examples/jsm/renderers/nodes/lights/LightContextNode.js
  63. 4 2
      examples/jsm/renderers/nodes/lights/LightNode.js
  64. 2 2
      examples/jsm/renderers/nodes/lights/LightsNode.js
  65. 4 4
      examples/jsm/renderers/nodes/math/MathNode.js
  66. 3 3
      examples/jsm/renderers/nodes/math/OperatorNode.js
  67. 38 0
      examples/jsm/renderers/nodes/procedural/CheckerNode.js
  68. 2 4
      examples/jsm/renderers/nodes/utils/JoinNode.js
  69. 17 5
      examples/jsm/renderers/nodes/utils/SplitNode.js
  70. 21 32
      examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js
  71. 15 1
      examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
  72. 17 17
      examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js
  73. BIN
      examples/models/gltf/SittingBox.glb
  74. BIN
      examples/screenshots/webgl_animation_cloth.jpg
  75. BIN
      examples/screenshots/webgl_animation_multiple.jpg
  76. BIN
      examples/screenshots/webgl_morphtargets_face.jpg
  77. BIN
      examples/screenshots/webgl_shading_physical.jpg
  78. BIN
      examples/screenshots/webgpu_sandbox.jpg
  79. 0 2
      examples/tags.json
  80. BIN
      examples/textures/patterns/bright_squares256.png
  81. BIN
      examples/textures/patterns/circuit_pattern.png
  82. 0 6
      examples/textures/patterns/readme.txt
  83. 0 629
      examples/webgl_animation_cloth.html
  84. 22 7
      examples/webgl_morphtargets_face.html
  85. 0 363
      examples/webgl_shading_physical.html
  86. 3 1
      examples/webgpu_sandbox.html
  87. 1 1
      package.json
  88. 1 1
      src/constants.js
  89. 3 2
      src/core/Object3D.js
  90. 1 1
      src/objects/SkinnedMesh.js
  91. 18 17
      src/renderers/WebGLRenderer.js
  92. 4 3
      test/e2e/puppeteer.js

+ 1 - 2
docs/api/ar/cameras/CubeCamera.html

@@ -41,8 +41,7 @@
 		<h2>أمثلة (Examples)</h2>
 
 		<p>
-			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]<br />
-			[example:webgl_shading_physical shading / physical ]
+			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
 		</p>
 
 		<h2>المنشئ (Constructor)</h2>

+ 1 - 2
docs/api/en/cameras/CubeCamera.html

@@ -41,8 +41,7 @@
 		<h2>Examples</h2>
 
 		<p>
-			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]<br />
-			[example:webgl_shading_physical shading / physical ]
+			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
 		</p>
 
 		<h2>Constructor</h2>

+ 0 - 1
docs/api/en/lights/AmbientLight.html

@@ -26,7 +26,6 @@
 
 		<h2>Examples</h2>
 		<p>
-			[example:webgl_animation_cloth animation / cloth ]<br />
 			[example:webgl_animation_skinning_blending animation / skinning / blending ]
 		</p>
 

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

@@ -91,7 +91,6 @@
 		<h2>Examples</h2>
 
 		<p>
-			[example:webgl_animation_cloth webgl / animation / cloth ]<br />
 			[example:webgl_buffergeometry_custom_attributes_particles webgl / buffergeometry / custom / attributes / particles]<br />
 			[example:webgl_buffergeometry_selective_draw webgl / buffergeometry / selective / draw]<br />
 			[example:webgl_custom_attributes webgl / custom / attributes]<br />

+ 1 - 2
docs/api/ko/cameras/CubeCamera.html

@@ -41,8 +41,7 @@
 		<h2>예제</h2>
 
 		<p>
-			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]<br />
-			[example:webgl_shading_physical shading / physical ]
+			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
 		</p>
 
 		<h2>생성자</h2>

+ 1 - 2
docs/api/zh/cameras/CubeCamera.html

@@ -41,8 +41,7 @@
 		<h2>例子</h2>
 
 		<p>
-			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]<br />
-			[example:webgl_shading_physical shading / physical ]
+			[example:webgl_materials_cubemap_dynamic materials / cubemap / dynamic ]
 		</p>
 
 		<h2>构造器</h2>

+ 34 - 43
docs/api/zh/extras/core/Curve.html

@@ -10,8 +10,8 @@
 		<h1>[name]</h1>
 
 		<p class="desc">
-		An abstract base class for creating a [name] object that contains methods for interpolation.
-		For an array of [name]s see [page:CurvePath].
+			用于创建包含插值方法的[name]对象的抽象基类。
+			有关[name]的数组,请参见[page:CurvePath]。
 		</p>
 
 		<h2>Constructor</h2>
@@ -19,102 +19,93 @@
 
 		<h3>[name]()</h3>
 		<p>
-		This constructor creates a new [name].
+		创建一个 [name].
 		</p>
 
-		<h2>Properties</h2>
+		<h2>属性</h2>
 
 		<h3>[property:Integer arcLengthDivisions]</h3>
-		<p>This value determines the amount of divisions when calculating the cumulative segment lengths of a curve via [page:.getLengths].
-			To ensure precision when using methods like [page:.getSpacedPoints], it is recommended to increase [page:.arcLengthDivisions] if the curve is very large. Default is 200.</p>
+		<p>确定[page:.GetLength]计算曲线的累积分段长度时的分段量。
+			为确保[page:.getSpacedPoints]等方法时的精度,如果曲线非常大,建议增加[page:.arcLengthDivisions]。默认值为200</p>
 
-		<h2>Methods</h2>
+		<h2>方法</h2>
 
 		<h3>[method:Vector getPoint]( [param:Float t], [param:Vector optionalTarget] )</h3>
 		<p>
-			[page:Float t] - A position on the curve. Must be in the range [ 0, 1 ]. <br>
-			[page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
-			otherwise a new Vector will be created. <br /><br />
+			[page:Float t] - 曲线上的位置。必须在[0,1]范围内 <br>
+			[page:Vector optionalTarget] — (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。 <br /><br />
 
-			Returns a vector for a given position on the curve.
+			返回曲线上给定位置的点。
 		</p>
 
 		<h3>[method:Vector getPointAt]( [param:Float u], [param:Vector optionalTarget] )</h3>
 		<p>
-			[page:Float u] - A position on the curve according to the arc length. Must be in the range [ 0, 1 ]. <br>
-			[page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
-			otherwise a new Vector will be created. <br /><br />
+			[page:Float u] - 根据弧长在曲线上的位置。必须在范围[0,1]内。 <br>
+			[page:Vector optionalTarget] — (可选) 如果需要, (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。 <br /><br />
 
-			Returns a vector for a given position on the curve according to the arc length.
+			根据弧长返回曲线上给定位置的点。
 		</p>
 
 		<h3>[method:Array getPoints]( [param:Integer divisions] )</h3>
 		<p>
-			divisions -- number of pieces to divide the curve into. Default is *5*.<br /><br />
-
-			Returns a set of divisions + 1 points using getPoint( t ).
+			divisions -- 要将曲线划分为的分段数。默认是 *5*.<br /><br />
+			使用getPoint(t)返回一组divisions+1的点
 		</p>
 
 		<h3>[method:Array getSpacedPoints]( [param:Integer divisions] )</h3>
 		<p>
-			divisions -- number of pieces to divide the curve into. Default is *5*.<br /><br />
+			divisions -- 要将曲线划分为的分段数。默认是 *5*.<br /><br />
 
-			Returns a set of divisions + 1 equi-spaced points using getPointAt( u ).
+			使用getPointAt(u)返回一个分段+1的等距点的数组。
 		</p>
 
 		<h3>[method:Float getLength]()</h3>
-		<p>Get total curve arc length.</p>
+		<p>获取总曲线弧长。</p>
 
 		<h3>[method:Array getLengths]( [param:Integer divisions] )</h3>
-		<p>Get list of cumulative segment lengths.</p>
+		<p>获取累积段长度的列表。</p>
 
 		<h3>[method:null updateArcLengths]()</h3>
-		<p>Update the cumlative segment distance cache.</p>
+		<p>更新累积段距离缓存。</p>
 
 		<h3>[method:Float getUtoTmapping]( [param:Float u], [param:Float distance] )</h3>
 		<p>
-			Given u in the range ( 0 .. 1 ), returns [page:Float t] also in the range ( 0 .. 1 ).
-			u and t can then be used to give you points which are equidistant from the ends of the curve,
-			using [page:.getPoint].
+			给定范围(0..1)内的u,返回范围(0..1)内的[page:Float t],
+			然后可以用t来使用 [page:.getPoint]给出与曲线末端等距的点。
 		</p>
 
 		<h3>[method:Vector getTangent]( [param:Float t], [param:Vector optionalTarget] )</h3>
 		<p>
-			[page:Float t] - A position on the curve. Must be in the range [ 0, 1 ]. <br>
-			[page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
-			otherwise a new Vector will be created. <br /><br />
+			[page:Float t] -在曲线上的点,必须在范围 [ 0, 1 ]. <br>
+			[page:Vector optionalTarget] — (可选) 如果需要, (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。 <br /><br />
 
-			Returns a unit vector tangent at t. If the derived curve does not implement its
-			tangent derivation, two points a small delta apart will be used to find its gradient
-			which seems to give a reasonable approximation.
+			返回t处的单位向量切线。如果派生曲线未实现其
+			切线求导,将使用相距一个小三角形的两个点来求与其实际梯度的近似值
 		</p>
 
 		<h3>[method:Vector getTangentAt]( [param:Float u], [param:Vector optionalTarget] )</h3>
 		<p>
-			[page:Float u] - A position on the curve according to the arc length. Must be in the range [ 0, 1 ]. <br>
-			[page:Vector optionalTarget] — (optional) If specified, the result will be copied into this Vector,
-			otherwise a new Vector will be created. <br /><br />
-
-			Returns tangent at a point which is equidistant to the ends of the curve from the
-			point given in [page:.getTangent].
+			[page:Float u] - 根据弧长在曲线上的位置,必须在范围[ 0, 1 ]。 <br>
+			[page:Vector optionalTarget] —(可选) 如果需要, (可选) 如果需要, 结果将复制到此向量中,否则将创建一个新向量。 <br /><br />
+			返回一个点处的切线,该点与 [page:.getTangent]中给定的曲线的端点距离相等
 		</p>
 
 		<h3>[method:Object computeFrenetFrames]( [param:Integer segments], [param:Boolean closed] )</h3>
 		<p>
-		Generates the Frenet Frames. Requires a curve definition in 3D space. Used in geometries like [page:TubeGeometry] or [page:ExtrudeGeometry].
+			生成Frenet帧。需要三维空间中的曲线定义。用于[page:TubeGeometry]或[page:ExtradeGeometry]等几何图形。
 		</p>
 
 		<h3>[method:Curve clone]()</h3>
-		<p>Creates a clone of this instance.</p>
+		<p>创建此实例的克隆。</p>
 
 		<h3>[method:Curve copy]( [param:Curve source] )</h3>
-		<p>Copies another [name] object to this instance.</p>
+		<p>将另一个[name]对象复制到此实例。</p>
 
 		<h3>[method:Object toJSON]()</h3>
-		<p>Returns a JSON object representation of this instance.</p>
+		<p>返回此实例的JSON对象表示形式。</p>
 
 		<h3>[method:Curve fromJSON]( [param:Object json] )</h3>
-		<p>Copies the data from the given JSON object to this instance.</p>
+		<p>将给定的JSON数据复制到此实例。</p>
 
 		<h2>Source</h2>
 

+ 0 - 1
docs/api/zh/lights/AmbientLight.html

@@ -26,7 +26,6 @@
 
 		<h2>例子</h2>
 		<p>
-			[example:webgl_animation_cloth animation / cloth ]<br />
 			[example:webgl_animation_skinning_blending animation / skinning / blending ]
 		</p>
 

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

@@ -81,7 +81,6 @@
 		<h2>例子</h2>
 
 		<p>
-			[example:webgl_animation_cloth webgl / animation / cloth ]<br />
 			[example:webgl_buffergeometry_custom_attributes_particles webgl / buffergeometry / custom / attributes / particles]<br />
 			[example:webgl_buffergeometry_selective_draw webgl / buffergeometry / selective / draw]<br />
 			[example:webgl_custom_attributes webgl / custom / attributes]<br />

+ 1 - 1
docs/index.html

@@ -18,7 +18,7 @@
 
 				<div id="sections">
 					<span class="selected">docs</span>
-					<a href="../examples/#webgl_animation_cloth">examples</a>
+					<a href="../examples/#webgl_animation_keyframes">examples</a>
 				</div>
 
 				<div id="expandButton"></div>

+ 3 - 3
docs/manual/ko/buildTools/Testing-with-NPM.html

@@ -131,7 +131,7 @@ $ npm install three --save-dev
  $ npm install [email protected] --save
 							</code>
                             (이 예제에서는 0.84.0 버전을 사용했습니다.). --save 는 dev 설정이 아닌 이 프로젝트의 의존성으로 추가하는 명령어입니다.
-                            [link:https://www.npmjs.org/doc/json.html 여기]에서 더 많은 내용을 확인하세요.
+                            여기([link:https://www.npmjs.org/doc/json.html link])에서 더 많은 내용을 확인하세요.
 						</li>
 					</ul>
 				</li>
@@ -181,12 +181,12 @@ The THREE object should be able to construct a Vector3 with default of x=0: 0ms
 			<ol>
 				<li>
 					자신의 코드의 예상 결과가 들어있는 예제를 만들어, test/ 폴더 안에 두세요.
-					[link:https://github.com/air/encounter/blob/master/test/Physics-test.js 여기]에서 진짜 프로젝트의 예제를 확인할 수 있습니다.
+					여기([link:https://github.com/air/encounter/blob/master/test/Physics-test.js link])에서 진짜 프로젝트의 예제를 확인할 수 있습니다.
 				</li>
 
 				<li>
                     nodeJS에서 알아볼 수 있는, require를 사용해 기능들을 내보내기 하세요. 
-                    [link:https://github.com/air/encounter/blob/master/js/Physics.js 여기]를 참고하세요.
+                    여기([link:https://github.com/air/encounter/blob/master/js/Physics.js link])를 참고하세요.
 				</li>
 
 				<li>

+ 3 - 4
docs/manual/ko/introduction/Animation-system.html

@@ -13,15 +13,14 @@
 
 		<p class="desc">
             three.js 애니메이션 시스템에서는 모델의 다양한 속성을 설정할 수 있습니다:
-            [page:SkinnedMesh 스킨 및 리깅 모델], 모프타깃의 골자, 서로 다른 재질의 속성(색상, 불투명도, 참/거짓 연산),
+            [page:SkinnedMesh skinned and rigged model]의 뼈, 모프타깃, 서로 다른 재질의 속성(색상, 불투명도, 참/거짓 연산),
             가시성과 변환이 그 예입니다. 애니메이션의 속성은 페이드 아웃, 페이드 아웃, 크로스페이드, 랩이 있습니다.
             한 오브젝트에 대한 동시에 일어나는 다른 확대 시간 및 가중치 조절이나, 서로 다른 오브젝트간의 애니메이션도 전부 개별로 변화시킬 수 있습니다.
             같은, 혹은 서로 다른 오브젝트틀간의 다양한 애니메이션도 싱크를 맞출 수 있습니다.
             <br /><br />
 
-            이를 한 시스템 안에 구현하기 위해서, three.js 애니메이션 시스템은
-			[link:https://github.com/mrdoob/three.js/issues/6881 2015년에 완전히 변경]
-            (지난 정보임에 주의하세요!)되었으며, 현재는 Unity/Unreal Engine 4와 유사한 구조를 가지고 있습니다.
+            이를 한 시스템 안에 구현하기 위해서, three.js 애니메이션 시스템은 2015년에 완전히 변경되었으며([link:https://github.com/mrdoob/three.js/issues/6881 Link])
+            되었으며(지난 정보에 주의하세요!), 현재는 Unity/Unreal Engine 4와 유사한 구조를 가지고 있습니다.
             이 페이지에서는 어떻게 시스템 메인 컴포넌트가 구성되고 동작되는지를 간단하게 알아보겠습니다.
 
 		</p>

+ 3 - 3
docs/manual/ko/introduction/FAQ.html

@@ -15,7 +15,7 @@
 				불러오기 및 내보내기 용으로 추천되는 포맷은 glTF (GL Transmission Format)입니다. glTF는 런타임 에셋 딜리버리에 초점이 맞추어져 있기 때문에, 전송에 적합하고 로딩이 빠릅니다.
 			</p>
 			<p>
-				three.js 널리 쓰이는 포맷인 FBX, Collada 나 OBJ 도 지원합니다. 하지만 첫 프로젝트에서는 glTF 기반의 워크플로우로 작업해야 합니다. 더 자세한 내용은, [link:#manual/introduction/Loading-3D-models 3D 모델 로딩]을 참고하세요.
+				three.js 널리 쓰이는 포맷인 FBX, Collada 나 OBJ 도 지원합니다. 하지만 첫 프로젝트에서는 glTF 기반의 워크플로우로 작업해야 합니다. 더 자세한 내용은, [link:#manual/introduction/Loading-3D-models loading 3D models]를 참고하세요.
 			</p>
 		</div>
 
@@ -41,8 +41,8 @@ visible_height = 2 * Math.tan( ( Math.PI / 180 ) * camera.fov / 2 ) * distance_f
 			</code>
             화면 높이를 특정 비율로 늘리면, 모든 가시 높이와 거리가 같은 비율로 증가해야 합니다.
 
-			이는 카메라 위치를 변경하는 것으로는 불가능합니다. 대신에 카메라 field-of-view를 변경해야합니다..
-			[link:http://jsfiddle.net/Q4Jpu/ 예제].
+			이는 카메라 위치를 변경하는 것으로는 불가능합니다. 대신에 카메라 field-of-view를 변경해야합니다.
+			[link:http://jsfiddle.net/Q4Jpu/ Example].
 		</p>
 
 		<h2>왜 오브젝트 일부가 안 보일까요?</h2>

+ 4 - 4
docs/manual/ko/introduction/How-to-dispose-of-objects.html

@@ -21,7 +21,7 @@
 	<h2>기하학</h2>
 
 	<p>
-        기하학은 속성 집합으로 정의된 꼭짓점 정보를 표시하는데, *three.js*는 속성마다 하나의 [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer WebGLBuffer] 유형의 대상을 만들어 내부에 저장합니다.
+        기하학은 속성 집합으로 정의된 꼭짓점 정보를 표시하는데, *three.js*는 속성마다 하나의 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLBuffer WebGLBuffer] 유형의 대상을 만들어 내부에 저장합니다.
         이러한 개체는 [page:BufferGeometry.dispose]를 호출할 때만 폐기됩니다.
         만약 애플리케이션에서 기하학을 더이상 사용하지 않는다면, 이 방법을 실행하여 모든 관련 자원을 폐기하세요.
 	</p>
@@ -38,15 +38,15 @@
 
 	<p>
         재질의 폐기는 텍스쳐에 영향을 미치지 않습니다. 이들은 분리되어 있어 하나의 텍스쳐를 여러 재질로 동시에 사용할 수 있습니다.
-        [page:Texture] 인스턴스를 만들 때마다 three.js는 내부에서 [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture] 인스턴스를 만듭니다.
+        [page:Texture] 인스턴스를 만들 때마다 three.js는 내부에서 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture] 인스턴스를 만듭니다.
         buffer와 비슷하게, 이 오브젝트는 [page:Texture.dispose]() 호출로만 삭제가 가능합니다.
 	</p>
 
 	<h2>렌더링 대상</h2>
 
 	<p>
-        [page:WebGLRenderTarget] 타입의 오브젝트는 [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture]의 인스턴스가 할당되어 있을 뿐만 아니라,
-        [link: https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer WebGLFramebuffer]와 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderbuffer WebGLRenderbuffer] 도 할당되어, 
+        [page:WebGLRenderTarget] 타입의 오브젝트는 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLTexture WebGLTexture]의 인스턴스가 할당되어 있을 뿐만 아니라,
+        [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLFramebuffer WebGLFramebuffer]와 [link:https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderbuffer WebGLRenderbuffer] 도 할당되어, 
         커스텀 렌더링 목표를 실체화합니다. 이러한 오브젝트는 [page:WebGLRenderTarget.dispose]()를 실행해야만 폐기할 수 있습니다.
 	</p>
 

+ 3 - 4
docs/manual/ko/introduction/How-to-run-things-locally.html

@@ -43,8 +43,7 @@
     <h2>로컬 서버에서 실행</h2>
     <div>
         <p>
-            많은 프로그래밍 언어는 기본적으로 간단한 HTTP 서버가 설치되어 있습니다. [link:https://www.apache.org/ Apache]나 [link:https://nginx.org
-            NGINX]같은 프로덕션용 정도로 갖추어져 있지는 않지만, three.js를 테스트해보기에는 충분합니다.
+            많은 프로그래밍 언어는 기본적으로 간단한 HTTP 서버가 설치되어 있습니다. [link:https://www.apache.org/ Apache]나 [link:https://nginx.org NGINX]같은 프로덕션용 정도로 갖추어져 있지는 않지만, three.js를 테스트해보기에는 충분합니다.
         </p>
 
         <h3>유명 코드 에디터 관련 플러그인</h3>
@@ -120,7 +119,7 @@ ruby -r webrick -e "s = WEBrick::HTTPServer.new(:Port => 8000, :DocumentRoot =>
                 </li>
                 <li>
                     웹서버에서 실행하고자 하는 디렉토리에 lighttpd.conf라는 설정파일을 만듭니다.
-                    예제는 [link:http://redmine.lighttpd.net/projects/lighttpd/wiki/TutorialConfiguration 여기]에서 확인할 수 있습니다.
+                    예제는 여기([link:http://redmine.lighttpd.net/projects/lighttpd/wiki/TutorialConfiguration link])에서 확인할 수 있습니다.
                 </li>
                 <li>
                     설정 파일에서, server.document-root를 서버로 쓰고자 하는 디렉토리로 설정합니다.
@@ -141,7 +140,7 @@ ruby -r webrick -e "s = WEBrick::HTTPServer.new(:Port => 8000, :DocumentRoot =>
             <p>기본적으로 IIS는 .fbx, .obj 파일의 다운로드를 막아 놓았습니다. IIS에서 이러한 파일들이 다운로드 될 수 있도록 설정해야 합니다.</p>
         </div>
         <p>
-            다른 간단한 방법으로는 Stack Overflow의 [link:http://stackoverflow.com/q/12905426/24874 이곳에서 토론]을 확인해 보세요.
+            다른 간단한 방법으로는 Stack Overflow에서 논의된 내용([link:http://stackoverflow.com/q/12905426/24874 link])을 확인해 보세요.
         </p>
     </div>
 

+ 3 - 5
docs/manual/ko/introduction/Installation.html

@@ -141,8 +141,7 @@
 
     <p>
         대부분의 자바스크립트 번들러는 이제 ES 모듈을 기본적으로 지원하지만, 오래된 빌드 도구들은 그렇지 않을 수 있습니다.
-        이 경우에, 번들러에 ES 모듈을 인식할 수 있도록 설정해줄 수 있습니다. 예를들어 [link:http://browserify.org/
-        Browserify] 는 [link:https://github.com/babel/babelify babelify] 플러그인을 불러오기만 하면 됩니다.
+        이 경우에, 번들러에 ES 모듈을 인식할 수 있도록 설정해줄 수 있습니다. 예를들어 [link:http://browserify.org/ Browserify] 는 [link:https://github.com/babel/babelify babelify] 플러그인을 불러오기만 하면 됩니다.
     </p>
 
     <h3>maps 불러오기</h3>
@@ -159,7 +158,7 @@
         이는 npm 패키지에서 주로 쓰이는 경로 작성법과 일치하고, 두 사용자군 모두에게 파일을 불러오는 데에 동일한 코드를 사용할 수 있게 해 줄 것입니다.
         빌드 도구를 사용하지 않는 것을 선호하는 사용자들에게도, 간단한 JSON 맵핑을 통해 CDN이나 직접 파일 폴더에서 불러오는 것을 가능하게 해 줄 것입니다.
         실험적 방법으로, [link:https://glitch.com/edit/#!/three-import-map?path=index.html import map
-        예제]처럼 map polyfill과 함께 import 해서 더 간단하게 사용해볼 수도 있습니다.
+        example]처럼 map polyfill과 함께 import 해서 더 간단하게 사용해볼 수도 있습니다.
     </p>
 
     <h3>Node.js</h3>
@@ -170,8 +169,7 @@
 
     <p>
         첫 번째로, three.js는 웹을 목적으로 만들어졌기때문에, Node.js에서 항상 활용 가능하다고 보증할 수 없는 브라우저와 DOM API에 의존하고 있는 까닭입니다.
-        이러한 문제들은 shims를 통해 [link:https://github.com/stackgl/headless-gl
-        headless-gl]처럼 해결하거나, [page:TextureLoader] 같은 컴포넌트를 커스터마이징 해서 해결 가능합니다. 다른 DOM API는 관련된 코드가 더 복잡하게 연관되어 있어, 수정하기 더 까다롭습니다.
+        이러한 문제들은 shims를 통해 [link:https://github.com/stackgl/headless-gl headless-gl]처럼 해결하거나, [page:TextureLoader] 같은 컴포넌트를 커스터마이징 해서 해결 가능합니다. 다른 DOM API는 관련된 코드가 더 복잡하게 연관되어 있어, 수정하기 더 까다롭습니다.
         Node.js 지원을 향상시키기 위한 더 간단하고, 유지보수 가능한 pull 요청은 언제든지 환영이지만, 본인의 작업을 위한 issue 생성을 더 권장합니다.
     </p>
 

+ 1 - 1
docs/manual/ko/introduction/Useful-links.html

@@ -20,7 +20,7 @@
 
 		<h2>도움이 되는 포럼</h2>
 		<p>
-			Three.js는 공식적으로[link:https://discourse.threejs.org/ 포럼] 과 [link:http://stackoverflow.com/tags/three.js/info Stack Overflow]에서 지원 요청을 받고 있습니다.
+			Three.js는 공식적으로 [link:https://discourse.threejs.org/ forum]과 [link:http://stackoverflow.com/tags/three.js/info Stack Overflow]에서 지원 요청을 받고 있습니다.
 			도움이 필요하다면, 저기로 가면 됩니다. 깃허브에서 도움 요청 이슈를 생성하지 마세요.
 		</p>
 

+ 12 - 10
editor/css/main.css

@@ -74,19 +74,21 @@ textarea, input { outline: none; } /* osx */
 	position: relative;
 	display: block;
 	width: 100%;
+	min-width: 300px;
 }
 
-.TabbedPanel .Tabs .Tab {
-	padding: 10px;
-	text-transform: uppercase;
-}
+	.TabbedPanel .Tabs .Tab {
+		padding: 10px;
+		text-transform: uppercase;
+	}
 
-.TabbedPanel .Tabs .Panels {
-	position: relative;
-	display: block;
-	width: 100%;
-	height: 100%;
-}
+	.TabbedPanel .Panels {
+		position: relative;
+		display: block;
+		width: 100%;
+		height: 100%;
+		min-width: 300px;
+	}
 
 /* Listbox */
 .Listbox {

+ 1 - 3
editor/js/Menubar.File.js

@@ -447,19 +447,17 @@ function MenubarFile( editor ) {
 			if ( config.getKey( 'project/editable' ) ) {
 
 				editButton = [
-					'',
 					'			var button = document.createElement( \'a\' );',
 					'			button.href = \'https://threejs.org/editor/#file=\' + location.href.split( \'/\' ).slice( 0, - 1 ).join( \'/\' ) + \'/app.json\';',
 					'			button.style.cssText = \'position: absolute; bottom: 20px; right: 20px; padding: 10px 16px; color: #fff; border: 1px solid #fff; border-radius: 20px; text-decoration: none;\';',
 					'			button.target = \'_blank\';',
 					'			button.textContent = \'EDIT\';',
 					'			document.body.appendChild( button );',
-					''
 				].join( '\n' );
 
 			}
 
-			content = content.replace( '\n\t\t\t/* edit button */\n', editButton );
+			content = content.replace( '\t\t\t/* edit button */', editButton );
 
 			toZip[ 'index.html' ] = strToU8( content );
 

+ 6 - 0
editor/js/Player.js

@@ -21,6 +21,12 @@ function Player( editor ) {
 
 	} );
 
+	signals.windowResize.add( function () {
+
+		player.setSize( container.dom.clientWidth, container.dom.clientHeight );
+
+	} );
+
 	signals.startPlayer.add( function () {
 
 		container.setDisplay( '' );

+ 10 - 4
editor/js/Resizer.js

@@ -31,13 +31,19 @@ function Resizer( editor ) {
 
 		if ( event.isPrimary === false ) return;
 
-		const rect = dom.getBoundingClientRect();
-		const x = ( document.body.offsetWidth - rect.right ) - event.movementX;
+		const offsetWidth = document.body.offsetWidth;
+		const clientX = event.clientX;
+
+		const cX = clientX < 0 ? 0 : clientX > offsetWidth ? offsetWidth : clientX;
+
+		const x = offsetWidth - cX;
 
 		dom.style.right = x + 'px';
 
-		document.getElementById( 'sidebar' ).style.width = ( x + rect.width ) + 'px';
-		document.getElementById( 'viewport' ).style.right = ( x + rect.width ) + 'px';
+		document.getElementById( 'sidebar' ).style.width = x + 'px';
+		document.getElementById( 'player' ).style.right = x + 'px';
+		document.getElementById( 'script' ).style.right = x + 'px';
+		document.getElementById( 'viewport' ).style.right = x + 'px';
 
 		signals.windowResize.dispatch();
 

+ 1 - 1
editor/sw.js

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

+ 0 - 2
examples/files.json

@@ -1,6 +1,5 @@
 {
 	"webgl": [
-		"webgl_animation_cloth",
 		"webgl_animation_keyframes",
 		"webgl_animation_skinning_blending",
 		"webgl_animation_skinning_additive_blending",
@@ -206,7 +205,6 @@
 		"webgl_shaders_ocean",
 		"webgl_shaders_sky",
 		"webgl_shaders_tonemapping",
-		"webgl_shading_physical",
 		"webgl_shadow_contact",
 		"webgl_shadowmap",
 		"webgl_shadowmap_performance",

+ 11 - 1
examples/js/exporters/USDZExporter.js

@@ -418,7 +418,17 @@ ${array.join( '' )}
 
 		}
 
-		inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
+		if ( material.alphaMap !== null ) {
+
+			inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
+			inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
+			samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
+
+		} else {
+
+			inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
+
+		}
 
 		if ( material.isMeshPhysicalMaterial ) {
 

+ 115 - 0
examples/js/geometries/ParametricGeometry.js

@@ -0,0 +1,115 @@
+( function () {
+
+	/**
+ * Parametric Surfaces Geometry
+ * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html
+ */
+
+	class ParametricGeometry extends THREE.BufferGeometry {
+
+		constructor( func = ( u, v, target ) => target.set( u, v, Math.cos( u ) * Math.sin( v ) ), slices = 8, stacks = 8 ) {
+
+			super();
+			this.type = 'ParametricGeometry';
+			this.parameters = {
+				func: func,
+				slices: slices,
+				stacks: stacks
+			}; // buffers
+
+			const indices = [];
+			const vertices = [];
+			const normals = [];
+			const uvs = [];
+			const EPS = 0.00001;
+			const normal = new THREE.Vector3();
+			const p0 = new THREE.Vector3(),
+				p1 = new THREE.Vector3();
+			const pu = new THREE.Vector3(),
+				pv = new THREE.Vector3();
+
+			if ( func.length < 3 ) {
+
+				console.error( 'THREE.ParametricGeometry: Function must now modify a THREE.Vector3 as third parameter.' );
+
+			} // generate vertices, normals and uvs
+
+
+			const sliceCount = slices + 1;
+
+			for ( let i = 0; i <= stacks; i ++ ) {
+
+				const v = i / stacks;
+
+				for ( let j = 0; j <= slices; j ++ ) {
+
+					const u = j / slices; // vertex
+
+					func( u, v, p0 );
+					vertices.push( p0.x, p0.y, p0.z ); // normal
+					// approximate tangent vectors via finite differences
+
+					if ( u - EPS >= 0 ) {
+
+						func( u - EPS, v, p1 );
+						pu.subVectors( p0, p1 );
+
+					} else {
+
+						func( u + EPS, v, p1 );
+						pu.subVectors( p1, p0 );
+
+					}
+
+					if ( v - EPS >= 0 ) {
+
+						func( u, v - EPS, p1 );
+						pv.subVectors( p0, p1 );
+
+					} else {
+
+						func( u, v + EPS, p1 );
+						pv.subVectors( p1, p0 );
+
+					} // cross product of tangent vectors returns surface normal
+
+
+					normal.crossVectors( pu, pv ).normalize();
+					normals.push( normal.x, normal.y, normal.z ); // uv
+
+					uvs.push( u, v );
+
+				}
+
+			} // generate indices
+
+
+			for ( let i = 0; i < stacks; i ++ ) {
+
+				for ( let j = 0; j < slices; j ++ ) {
+
+					const a = i * sliceCount + j;
+					const b = i * sliceCount + j + 1;
+					const c = ( i + 1 ) * sliceCount + j + 1;
+					const d = ( i + 1 ) * sliceCount + j; // faces one and two
+
+					indices.push( a, b, d );
+					indices.push( b, c, d );
+
+				}
+
+			} // build geometry
+
+
+			this.setIndex( indices );
+			this.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+			this.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+			this.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+
+		}
+
+	}
+
+	THREE.ParametricGeometry = ParametricGeometry;
+
+} )();

+ 49 - 0
examples/js/geometries/TextGeometry.js

@@ -0,0 +1,49 @@
+( function () {
+
+	/**
+ * Text = 3D Text
+ *
+ * parameters = {
+ *  font: <THREE.Font>, // font
+ *
+ *  size: <float>, // size of the text
+ *  height: <float>, // thickness to extrude text
+ *  curveSegments: <int>, // number of points on the curves
+ *
+ *  bevelEnabled: <bool>, // turn on bevel
+ *  bevelThickness: <float>, // how deep into text bevel goes
+ *  bevelSize: <float>, // how far from text outline (including bevelOffset) is bevel
+ *  bevelOffset: <float> // how far from text outline does bevel start
+ * }
+ */
+
+	class TextGeometry extends THREE.ExtrudeGeometry {
+
+		constructor( text, parameters = {} ) {
+
+			const font = parameters.font;
+
+			if ( ! ( font && font.isFont ) ) {
+
+				console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );
+				return new THREE.BufferGeometry();
+
+			}
+
+			const shapes = font.generateShapes( text, parameters.size ); // translate parameters to THREE.ExtrudeGeometry API
+
+			parameters.depth = parameters.height !== undefined ? parameters.height : 50; // defaults
+
+			if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;
+			if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;
+			if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;
+			super( shapes, parameters );
+			this.type = 'TextGeometry';
+
+		}
+
+	}
+
+	THREE.TextGeometry = TextGeometry;
+
+} )();

+ 183 - 0
examples/js/loaders/FontLoader.js

@@ -0,0 +1,183 @@
+( function () {
+
+	class FontLoader extends THREE.Loader {
+
+		constructor( manager ) {
+
+			super( manager );
+
+		}
+
+		load( url, onLoad, onProgress, onError ) {
+
+			const scope = this;
+			const loader = new THREE.FileLoader( this.manager );
+			loader.setPath( this.path );
+			loader.setRequestHeader( this.requestHeader );
+			loader.setWithCredentials( scope.withCredentials );
+			loader.load( url, function ( text ) {
+
+				let json;
+
+				try {
+
+					json = JSON.parse( text );
+
+				} catch ( e ) {
+
+					console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' );
+					json = JSON.parse( text.substring( 65, text.length - 2 ) );
+
+				}
+
+				const font = scope.parse( json );
+				if ( onLoad ) onLoad( font );
+
+			}, onProgress, onError );
+
+		}
+
+		parse( json ) {
+
+			return new Font( json );
+
+		}
+
+	} //
+
+
+	class Font {
+
+		constructor( data ) {
+
+			this.type = 'Font';
+			this.data = data;
+
+		}
+
+		generateShapes( text, size = 100 ) {
+
+			const shapes = [];
+			const paths = createPaths( text, size, this.data );
+
+			for ( let p = 0, pl = paths.length; p < pl; p ++ ) {
+
+				Array.prototype.push.apply( shapes, paths[ p ].toShapes() );
+
+			}
+
+			return shapes;
+
+		}
+
+	}
+
+	function createPaths( text, size, data ) {
+
+		const chars = Array.from( text );
+		const scale = size / data.resolution;
+		const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
+		const paths = [];
+		let offsetX = 0,
+			offsetY = 0;
+
+		for ( let i = 0; i < chars.length; i ++ ) {
+
+			const char = chars[ i ];
+
+			if ( char === '\n' ) {
+
+				offsetX = 0;
+				offsetY -= line_height;
+
+			} else {
+
+				const ret = createPath( char, scale, offsetX, offsetY, data );
+				offsetX += ret.offsetX;
+				paths.push( ret.path );
+
+			}
+
+		}
+
+		return paths;
+
+	}
+
+	function createPath( char, scale, offsetX, offsetY, data ) {
+
+		const glyph = data.glyphs[ char ] || data.glyphs[ '?' ];
+
+		if ( ! glyph ) {
+
+			console.error( 'THREE.Font: character "' + char + '" does not exists in font family ' + data.familyName + '.' );
+			return;
+
+		}
+
+		const path = new THREE.ShapePath();
+		let x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;
+
+		if ( glyph.o ) {
+
+			const outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );
+
+			for ( let i = 0, l = outline.length; i < l; ) {
+
+				const action = outline[ i ++ ];
+
+				switch ( action ) {
+
+					case 'm':
+						// moveTo
+						x = outline[ i ++ ] * scale + offsetX;
+						y = outline[ i ++ ] * scale + offsetY;
+						path.moveTo( x, y );
+						break;
+
+					case 'l':
+						// lineTo
+						x = outline[ i ++ ] * scale + offsetX;
+						y = outline[ i ++ ] * scale + offsetY;
+						path.lineTo( x, y );
+						break;
+
+					case 'q':
+						// quadraticCurveTo
+						cpx = outline[ i ++ ] * scale + offsetX;
+						cpy = outline[ i ++ ] * scale + offsetY;
+						cpx1 = outline[ i ++ ] * scale + offsetX;
+						cpy1 = outline[ i ++ ] * scale + offsetY;
+						path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );
+						break;
+
+					case 'b':
+						// bezierCurveTo
+						cpx = outline[ i ++ ] * scale + offsetX;
+						cpy = outline[ i ++ ] * scale + offsetY;
+						cpx1 = outline[ i ++ ] * scale + offsetX;
+						cpy1 = outline[ i ++ ] * scale + offsetY;
+						cpx2 = outline[ i ++ ] * scale + offsetX;
+						cpy2 = outline[ i ++ ] * scale + offsetY;
+						path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );
+						break;
+
+				}
+
+			}
+
+		}
+
+		return {
+			offsetX: glyph.ha * scale,
+			path: path
+		};
+
+	}
+
+	Font.prototype.isFont = true;
+
+	THREE.Font = Font;
+	THREE.FontLoader = FontLoader;
+
+} )();

+ 89 - 42
examples/js/loaders/GLTFLoader.js

@@ -581,9 +581,8 @@
 
 				if ( extension.clearcoatNormalTexture.scale !== undefined ) {
 
-					const scale = extension.clearcoatNormalTexture.scale; // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-
-					materialParams.clearcoatNormalScale = new THREE.Vector2( scale, - scale );
+					const scale = extension.clearcoatNormalTexture.scale;
+					materialParams.clearcoatNormalScale = new THREE.Vector2( scale, scale );
 
 				}
 
@@ -2110,7 +2109,28 @@
 		_getNodeRef( cache, index, object ) {
 
 			if ( cache.refs[ index ] <= 1 ) return object;
-			const ref = object.clone();
+			const ref = object.clone(); // Propagates mappings to the cloned object, prevents mappings on the
+			// original object from being lost.
+
+			const updateMappings = ( original, clone ) => {
+
+				const mappings = this.associations.get( original );
+
+				if ( mappings != null ) {
+
+					this.associations.set( clone, mappings );
+
+				}
+
+				for ( const [ i, child ] of original.children.entries() ) {
+
+					updateMappings( child, clone.children[ i ] );
+
+				}
+
+			};
+
+			updateMappings( object, ref );
 			ref.name += '_instance_' + cache.uses[ index ] ++;
 			return ref;
 
@@ -2488,28 +2508,12 @@
 			const URL = self.URL || self.webkitURL;
 			let sourceURI = source.uri || '';
 			let isObjectURL = false;
-			let hasAlpha = true;
-			const isJPEG = sourceURI.search( /\.jpe?g($|\?)/i ) > 0 || sourceURI.search( /^data\:image\/jpeg/ ) === 0;
-			if ( source.mimeType === 'image/jpeg' || isJPEG ) hasAlpha = false;
 
 			if ( source.bufferView !== undefined ) {
 
 				// Load binary image data from bufferView, if provided.
 				sourceURI = parser.getDependency( 'bufferView', source.bufferView ).then( function ( bufferView ) {
 
-					if ( source.mimeType === 'image/png' ) {
-
-						// Inspect the PNG 'IHDR' chunk to determine whether the image could have an
-						// alpha channel. This check is conservative — the image could have an alpha
-						// channel with all values == 1, and the indexed type (colorType == 3) only
-						// sometimes contains alpha.
-						//
-						// https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header
-						const colorType = new DataView( bufferView, 25, 1 ).getUint8( 0, false );
-						hasAlpha = colorType === 6 || colorType === 4 || colorType === 3;
-
-					}
-
 					isObjectURL = true;
 					const blob = new Blob( [ bufferView ], {
 						type: source.mimeType
@@ -2557,9 +2561,7 @@
 				}
 
 				texture.flipY = false;
-				if ( textureDef.name ) texture.name = textureDef.name; // When there is definitely no alpha channel in the texture, set THREE.RGBFormat to save space.
-
-				if ( ! hasAlpha ) texture.format = THREE.RGBFormat;
+				if ( textureDef.name ) texture.name = textureDef.name;
 				const samplers = json.samplers || {};
 				const sampler = samplers[ textureDef.sampler ] || {};
 				texture.magFilter = WEBGL_FILTERS[ sampler.magFilter ] || THREE.LinearFilter;
@@ -2567,8 +2569,7 @@
 				texture.wrapS = WEBGL_WRAPPINGS[ sampler.wrapS ] || THREE.RepeatWrapping;
 				texture.wrapT = WEBGL_WRAPPINGS[ sampler.wrapT ] || THREE.RepeatWrapping;
 				parser.associations.set( texture, {
-					type: 'textures',
-					index: textureIndex
+					textures: textureIndex
 				} );
 				return texture;
 
@@ -2638,7 +2639,7 @@
 
 			const geometry = mesh.geometry;
 			let material = mesh.material;
-			const useVertexTangents = geometry.attributes.tangent !== undefined;
+			const useDerivativeTangents = geometry.attributes.tangent === undefined;
 			const useVertexColors = geometry.attributes.color !== undefined;
 			const useFlatShading = geometry.attributes.normal === undefined;
 
@@ -2680,11 +2681,11 @@
 			} // Clone the material if it will be modified
 
 
-			if ( useVertexTangents || useVertexColors || useFlatShading ) {
+			if ( useDerivativeTangents || useVertexColors || useFlatShading ) {
 
 				let cacheKey = 'ClonedMaterial:' + material.uuid + ':';
 				if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
-				if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
+				if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:';
 				if ( useVertexColors ) cacheKey += 'vertex-colors:';
 				if ( useFlatShading ) cacheKey += 'flat-shading:';
 				let cachedMaterial = this.cache.get( cacheKey );
@@ -2695,7 +2696,7 @@
 					if ( useVertexColors ) cachedMaterial.vertexColors = true;
 					if ( useFlatShading ) cachedMaterial.flatShading = true;
 
-					if ( useVertexTangents ) {
+					if ( useDerivativeTangents ) {
 
 						// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
 						if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1;
@@ -2832,13 +2833,13 @@
 
 			if ( materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial ) {
 
-				pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) ); // https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-
-				materialParams.normalScale = new THREE.Vector2( 1, - 1 );
+				pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );
+				materialParams.normalScale = new THREE.Vector2( 1, 1 );
 
 				if ( materialDef.normalTexture.scale !== undefined ) {
 
-					materialParams.normalScale.set( materialDef.normalTexture.scale, - materialDef.normalTexture.scale );
+					const scale = materialDef.normalTexture.scale;
+					materialParams.normalScale.set( scale, scale );
 
 				}
 
@@ -2888,8 +2889,7 @@
 				if ( material.emissiveMap ) material.emissiveMap.encoding = THREE.sRGBEncoding;
 				assignExtrasToUserData( material, materialDef );
 				parser.associations.set( material, {
-					type: 'materials',
-					index: materialIndex
+					materials: materialIndex
 				} );
 				if ( materialDef.extensions ) addUnknownExtensionsToUserData( extensions, material, materialDef );
 				return material;
@@ -3082,6 +3082,15 @@
 
 				}
 
+				for ( let i = 0, il = meshes.length; i < il; i ++ ) {
+
+					parser.associations.set( meshes[ i ], {
+						meshes: meshIndex,
+						primitives: i
+					} );
+
+				}
+
 				if ( meshes.length === 1 ) {
 
 					return meshes[ 0 ];
@@ -3089,6 +3098,9 @@
 				}
 
 				const group = new THREE.Group();
+				parser.associations.set( group, {
+					meshes: meshIndex
+				} );
 
 				for ( let i = 0, il = meshes.length; i < il; i ++ ) {
 
@@ -3466,10 +3478,13 @@
 
 				}
 
-				parser.associations.set( node, {
-					type: 'nodes',
-					index: nodeIndex
-				} );
+				if ( ! parser.associations.has( node ) ) {
+
+					parser.associations.set( node, {} );
+
+				}
+
+				parser.associations.get( node ).nodes = nodeIndex;
 				return node;
 
 			} );
@@ -3499,12 +3514,44 @@
 
 			for ( let i = 0, il = nodeIds.length; i < il; i ++ ) {
 
-				pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) );
+				pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) );
 
 			}
 
 			return Promise.all( pending ).then( function () {
 
+				// Removes dangling associations, associations that reference a node that
+				// didn't make it into the scene.
+				const reduceAssociations = node => {
+
+					const reducedAssociations = new Map();
+
+					for ( const [ key, value ] of parser.associations ) {
+
+						if ( key instanceof THREE.Material || key instanceof THREE.Texture ) {
+
+							reducedAssociations.set( key, value );
+
+						}
+
+					}
+
+					node.traverse( node => {
+
+						const mappings = parser.associations.get( node );
+
+						if ( mappings != null ) {
+
+							reducedAssociations.set( node, mappings );
+
+						}
+
+					} );
+					return reducedAssociations;
+
+				};
+
+				parser.associations = reduceAssociations( scene );
 				return scene;
 
 			} );
@@ -3513,7 +3560,7 @@
 
 	}
 
-	function buildNodeHierachy( nodeId, parentObject, json, parser ) {
+	function buildNodeHierarchy( nodeId, parentObject, json, parser ) {
 
 		const nodeDef = json.nodes[ nodeId ];
 		return parser.getDependency( 'node', nodeId ).then( function ( node ) {
@@ -3587,7 +3634,7 @@
 				for ( let i = 0, il = children.length; i < il; i ++ ) {
 
 					const child = children[ i ];
-					pending.push( buildNodeHierachy( child, node, json, parser ) );
+					pending.push( buildNodeHierarchy( child, node, json, parser ) );
 
 				}
 

+ 4 - 5
examples/js/loaders/RGBMLoader.js

@@ -310,7 +310,7 @@
 				} else if ( depth == 16 ) for ( var x = 0; x < w; x ++ ) {
 
 					var gr = data[ off + ( x << 1 ) ],
-						al = rs( data, off + ( x << i ) ) == tr ? 0 : 255;
+						al = rs( data, off + ( x << 1 ) ) == tr ? 0 : 255;
 					bf32[ to + x ] = al << 24 | gr << 16 | gr << 8 | gr;
 
 				}
@@ -485,7 +485,7 @@
 
 				break;
 
-			} //else {  log("unknown chunk type", type, len);  }
+			} //else {  console.log("unknown chunk type", type, len);  out.tabs[type]=data.slice(offset,offset+len);  }
 
 
 			offset += len;
@@ -498,7 +498,6 @@
 
 			var fr = out.frames[ out.frames.length - 1 ];
 			fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height );
-			foff = 0;
 
 		}
 
@@ -1111,8 +1110,8 @@
 			paeth = UPNG.decode._paeth;
 
 		bpp = Math.ceil( bpp / 8 );
-		var i = 0,
-			di = 1,
+		var i,
+			di,
 			type = data[ off ],
 			x = 0;
 		if ( type > 1 ) data[ off ] = [ 0, 0, 1 ][ type - 2 ];

+ 1 - 0
examples/js/loaders/SVGLoader.js

@@ -856,6 +856,7 @@
 
 				addStyle( 'fill', 'fill' );
 				addStyle( 'fill-opacity', 'fillOpacity', clamp );
+				addStyle( 'fill-rule', 'fillRule' );
 				addStyle( 'opacity', 'opacity', clamp );
 				addStyle( 'stroke', 'stroke' );
 				addStyle( 'stroke-opacity', 'strokeOpacity', clamp );

+ 0 - 3
examples/js/postprocessing/SSRPass.js

@@ -9,7 +9,6 @@
 			width,
 			height,
 			selects,
-			encoding,
 			bouncing = false,
 			groundReflector
 		} ) {
@@ -26,7 +25,6 @@
 			this.output = 0;
 			this.maxDistance = THREE.SSRShader.uniforms.maxDistance.value;
 			this.thickness = THREE.SSRShader.uniforms.thickness.value;
-			this.encoding = encoding;
 			this.tempColor = new THREE.Color();
 			this._selects = selects;
 			this.selective = Array.isArray( this._selects );
@@ -307,7 +305,6 @@
 		) {
 
 			// render beauty and depth
-			if ( this.encoding ) this.beautyRenderTarget.texture.encoding = this.encoding;
 			renderer.setRenderTarget( this.beautyRenderTarget );
 			renderer.clear();
 

+ 1 - 4
examples/js/postprocessing/SSRrPass.js

@@ -8,8 +8,7 @@
 			camera,
 			width,
 			height,
-			selects,
-			encoding
+			selects
 		} ) {
 
 			super();
@@ -24,7 +23,6 @@
 			this.ior = THREE.SSRrShader.uniforms.ior.value;
 			this.maxDistance = THREE.SSRrShader.uniforms.maxDistance.value;
 			this.surfDist = THREE.SSRrShader.uniforms.surfDist.value;
-			this.encoding = encoding;
 			this.tempColor = new THREE.Color();
 			this.selects = selects;
 			this._specular = THREE.SSRrShader.defines.SPECULAR;
@@ -225,7 +223,6 @@
 		) {
 
 			// render beauty and depth
-			if ( this.encoding ) this.beautyRenderTarget.texture.encoding = this.encoding;
 			renderer.setRenderTarget( this.beautyRenderTarget );
 			renderer.clear();
 			this.scene.children.forEach( child => {

+ 3 - 3
examples/js/utils/PackedPhongMaterial.js

@@ -21,7 +21,7 @@
 					value: null
 				}
 			} ] );
-			this.vertexShader = [ '#define PHONG', 'varying vec3 vViewPosition;', '#ifndef FLAT_SHADED', 'varying vec3 vNormal;', '#endif', THREE.ShaderChunk.common, THREE.ShaderChunk.uv_pars_vertex, THREE.ShaderChunk.uv2_pars_vertex, THREE.ShaderChunk.displacementmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.fog_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, THREE.ShaderChunk.logdepthbuf_pars_vertex, THREE.ShaderChunk.clipping_planes_pars_vertex, `#ifdef USE_PACKED_NORMAL
+			this.vertexShader = [ '#define PHONG', 'varying vec3 vViewPosition;', THREE.ShaderChunk.common, THREE.ShaderChunk.uv_pars_vertex, THREE.ShaderChunk.uv2_pars_vertex, THREE.ShaderChunk.displacementmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.fog_pars_vertex, THREE.ShaderChunk.normal_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, THREE.ShaderChunk.logdepthbuf_pars_vertex, THREE.ShaderChunk.clipping_planes_pars_vertex, `#ifdef USE_PACKED_NORMAL
 					#if USE_PACKED_NORMAL == 0
 						vec3 decodeNormal(vec3 packedNormal)
 						{
@@ -87,13 +87,13 @@
 				#ifdef USE_TANGENT
 					vec3 objectTangent = vec3( tangent.xyz );
 				#endif
-				`, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, '#ifndef FLAT_SHADED', '	vNormal = normalize( transformedNormal );', '#endif', THREE.ShaderChunk.begin_vertex, `#ifdef USE_PACKED_POSITION
+				`, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, THREE.ShaderChunk.normal_vertex, THREE.ShaderChunk.begin_vertex, `#ifdef USE_PACKED_POSITION
 					#if USE_PACKED_POSITION == 0
 						transformed = ( vec4(transformed, 1.0) * quantizeMatPos ).xyz;
 					#endif
 				#endif`, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.displacementmap_vertex, THREE.ShaderChunk.project_vertex, THREE.ShaderChunk.logdepthbuf_vertex, THREE.ShaderChunk.clipping_planes_vertex, 'vViewPosition = - mvPosition.xyz;', THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.shadowmap_vertex, THREE.ShaderChunk.fog_vertex, '}' ].join( '\n' ); // Use the original THREE.MeshPhongMaterial's fragmentShader.
 
-			this.fragmentShader = [ '#define PHONG', 'uniform vec3 diffuse;', 'uniform vec3 emissive;', 'uniform vec3 specular;', 'uniform float shininess;', 'uniform float opacity;', THREE.ShaderChunk.common, THREE.ShaderChunk.packing, THREE.ShaderChunk.dithering_pars_fragment, THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.uv_pars_fragment, THREE.ShaderChunk.uv2_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.alphamap_pars_fragment, THREE.ShaderChunk.aomap_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.emissivemap_pars_fragment, THREE.ShaderChunk.envmap_common_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.cube_uv_reflection_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.bsdfs, THREE.ShaderChunk.lights_pars_begin, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, THREE.ShaderChunk.logdepthbuf_pars_fragment, THREE.ShaderChunk.clipping_planes_pars_fragment, 'void main() {', THREE.ShaderChunk.clipping_planes_fragment, 'vec4 diffuseColor = vec4( diffuse, opacity );', 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', 'vec3 totalEmissiveRadiance = emissive;', THREE.ShaderChunk.logdepthbuf_fragment, THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.alphamap_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.normal_fragment_begin, THREE.ShaderChunk.normal_fragment_maps, THREE.ShaderChunk.emissivemap_fragment, // accumulation
+			this.fragmentShader = [ '#define PHONG', 'uniform vec3 diffuse;', 'uniform vec3 emissive;', 'uniform vec3 specular;', 'uniform float shininess;', 'uniform float opacity;', THREE.ShaderChunk.common, THREE.ShaderChunk.packing, THREE.ShaderChunk.dithering_pars_fragment, THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.uv_pars_fragment, THREE.ShaderChunk.uv2_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.alphamap_pars_fragment, THREE.ShaderChunk.aomap_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.emissivemap_pars_fragment, THREE.ShaderChunk.envmap_common_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.cube_uv_reflection_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.bsdfs, THREE.ShaderChunk.lights_pars_begin, THREE.ShaderChunk.normal_pars_fragment, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, THREE.ShaderChunk.logdepthbuf_pars_fragment, THREE.ShaderChunk.clipping_planes_pars_fragment, 'void main() {', THREE.ShaderChunk.clipping_planes_fragment, 'vec4 diffuseColor = vec4( diffuse, opacity );', 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', 'vec3 totalEmissiveRadiance = emissive;', THREE.ShaderChunk.logdepthbuf_fragment, THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.alphamap_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.normal_fragment_begin, THREE.ShaderChunk.normal_fragment_maps, THREE.ShaderChunk.emissivemap_fragment, // accumulation
 				THREE.ShaderChunk.lights_phong_fragment, THREE.ShaderChunk.lights_fragment_begin, THREE.ShaderChunk.lights_fragment_maps, THREE.ShaderChunk.lights_fragment_end, // modulation
 				THREE.ShaderChunk.aomap_fragment, 'vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;', THREE.ShaderChunk.envmap_fragment, 'gl_FragColor = vec4( outgoingLight, diffuseColor.a );', THREE.ShaderChunk.tonemapping_fragment, THREE.ShaderChunk.encodings_fragment, THREE.ShaderChunk.fog_fragment, THREE.ShaderChunk.premultiplied_alpha_fragment, THREE.ShaderChunk.dithering_fragment, '}' ].join( '\n' );
 			this.setValues( parameters );

+ 12 - 1
examples/jsm/exporters/USDZExporter.js

@@ -449,7 +449,18 @@ function buildMaterial( material, textures ) {
 
 	}
 
-	inputs.push( `${ pad }float inputs:opacity = ${ material.opacity }` );
+	if ( material.alphaMap !== null ) {
+
+		inputs.push( `${pad}float inputs:opacity.connect = </Materials/Material_${material.id}/Texture_${material.alphaMap.id}_opacity.outputs:r>` );
+		inputs.push( `${pad}float inputs:opacityThreshold = 0.0001` );
+
+		samplers.push( buildTexture( material.alphaMap, 'opacity' ) );
+
+	} else {
+
+		inputs.push( `${pad}float inputs:opacity = ${material.opacity}` );
+
+	}
 
 	if ( material.isMeshPhysicalMaterial ) {
 

+ 1 - 1
examples/jsm/geometries/ParametricGeometry.js

@@ -132,4 +132,4 @@ class ParametricGeometry extends BufferGeometry {
 
 }
 
-export { ParametricGeometry, ParametricGeometry as ParametricBufferGeometry };
+export { ParametricGeometry };

+ 1 - 1
examples/jsm/geometries/TextGeometry.js

@@ -54,4 +54,4 @@ class TextGeometry extends ExtrudeGeometry {
 }
 
 
-export { TextGeometry, TextGeometry as TextBufferGeometry };
+export { TextGeometry };

+ 67 - 12
examples/jsm/loaders/GLTFLoader.js

@@ -692,8 +692,7 @@ class GLTFMaterialsClearcoatExtension {
 
 				const scale = extension.clearcoatNormalTexture.scale;
 
-				// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-				materialParams.clearcoatNormalScale = new Vector2( scale, - scale );
+				materialParams.clearcoatNormalScale = new Vector2( scale, scale );
 
 			}
 
@@ -2369,6 +2368,27 @@ class GLTFParser {
 
 		const ref = object.clone();
 
+		// Propagates mappings to the cloned object, prevents mappings on the
+		// original object from being lost.
+		const updateMappings = ( original, clone ) => {
+
+			const mappings = this.associations.get( original );
+			if ( mappings != null ) {
+
+				this.associations.set( clone, mappings );
+
+			}
+
+			for ( const [ i, child ] of original.children.entries() ) {
+
+				updateMappings( child, clone.children[ i ] );
+
+			}
+
+		};
+
+		updateMappings( object, ref );
+
 		ref.name += '_instance_' + ( cache.uses[ index ] ++ );
 
 		return ref;
@@ -2899,7 +2919,7 @@ class GLTFParser {
 		const geometry = mesh.geometry;
 		let material = mesh.material;
 
-		const useVertexTangents = geometry.attributes.tangent !== undefined;
+		const useDerivativeTangents = geometry.attributes.tangent === undefined;
 		const useVertexColors = geometry.attributes.color !== undefined;
 		const useFlatShading = geometry.attributes.normal === undefined;
 
@@ -2944,12 +2964,12 @@ class GLTFParser {
 		}
 
 		// Clone the material if it will be modified
-		if ( useVertexTangents || useVertexColors || useFlatShading ) {
+		if ( useDerivativeTangents || useVertexColors || useFlatShading ) {
 
 			let cacheKey = 'ClonedMaterial:' + material.uuid + ':';
 
 			if ( material.isGLTFSpecularGlossinessMaterial ) cacheKey += 'specular-glossiness:';
-			if ( useVertexTangents ) cacheKey += 'vertex-tangents:';
+			if ( useDerivativeTangents ) cacheKey += 'derivative-tangents:';
 			if ( useVertexColors ) cacheKey += 'vertex-colors:';
 			if ( useFlatShading ) cacheKey += 'flat-shading:';
 
@@ -2962,7 +2982,7 @@ class GLTFParser {
 				if ( useVertexColors ) cachedMaterial.vertexColors = true;
 				if ( useFlatShading ) cachedMaterial.flatShading = true;
 
-				if ( useVertexTangents ) {
+				if ( useDerivativeTangents ) {
 
 					// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
 					if ( cachedMaterial.normalScale ) cachedMaterial.normalScale.y *= - 1;
@@ -3109,12 +3129,13 @@ class GLTFParser {
 
 			pending.push( parser.assignTexture( materialParams, 'normalMap', materialDef.normalTexture ) );
 
-			// https://github.com/mrdoob/three.js/issues/11438#issuecomment-507003995
-			materialParams.normalScale = new Vector2( 1, - 1 );
+			materialParams.normalScale = new Vector2( 1, 1 );
 
 			if ( materialDef.normalTexture.scale !== undefined ) {
 
-				materialParams.normalScale.set( materialDef.normalTexture.scale, - materialDef.normalTexture.scale );
+				const scale = materialDef.normalTexture.scale;
+
+				materialParams.normalScale.set( scale, scale );
 
 			}
 
@@ -3848,12 +3869,46 @@ class GLTFParser {
 
 		for ( let i = 0, il = nodeIds.length; i < il; i ++ ) {
 
-			pending.push( buildNodeHierachy( nodeIds[ i ], scene, json, parser ) );
+			pending.push( buildNodeHierarchy( nodeIds[ i ], scene, json, parser ) );
 
 		}
 
 		return Promise.all( pending ).then( function () {
 
+			// Removes dangling associations, associations that reference a node that
+			// didn't make it into the scene.
+			const reduceAssociations = ( node ) => {
+
+				const reducedAssociations = new Map();
+
+				for ( const [ key, value ] of parser.associations ) {
+
+					if ( key instanceof Material || key instanceof Texture ) {
+
+						reducedAssociations.set( key, value );
+
+					}
+
+				}
+
+				node.traverse( ( node ) => {
+
+					const mappings = parser.associations.get( node );
+
+					if ( mappings != null ) {
+
+						reducedAssociations.set( node, mappings );
+
+					}
+
+				} );
+
+				return reducedAssociations;
+
+			};
+
+			parser.associations = reduceAssociations( scene );
+
 			return scene;
 
 		} );
@@ -3862,7 +3917,7 @@ class GLTFParser {
 
 }
 
-function buildNodeHierachy( nodeId, parentObject, json, parser ) {
+function buildNodeHierarchy( nodeId, parentObject, json, parser ) {
 
 	const nodeDef = json.nodes[ nodeId ];
 
@@ -3946,7 +4001,7 @@ function buildNodeHierachy( nodeId, parentObject, json, parser ) {
 			for ( let i = 0, il = children.length; i < il; i ++ ) {
 
 				const child = children[ i ];
-				pending.push( buildNodeHierachy( child, node, json, parser ) );
+				pending.push( buildNodeHierarchy( child, node, json, parser ) );
 
 			}
 

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

@@ -251,7 +251,7 @@ UPNG.toRGBA8.decodeImage = function ( data, w, h, out ) {
 			}
 			else if ( depth == 16 ) for ( var x = 0; x < w; x ++ ) {
 
-				var gr = data[ off + ( x << 1 ) ], al = ( rs( data, off + ( x << i ) ) == tr ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
+				var gr = data[ off + ( x << 1 ) ], al = ( rs( data, off + ( x << 1 ) ) == tr ) ? 0 : 255; bf32[ to + x ] = ( al << 24 ) | ( gr << 16 ) | ( gr << 8 ) | gr;
 
 			}
 
@@ -397,7 +397,7 @@ UPNG.decode = function ( buff ) {
 
 		}
 
-		//else {  log("unknown chunk type", type, len);  }
+		//else {  console.log("unknown chunk type", type, len);  out.tabs[type]=data.slice(offset,offset+len);  }
 		offset += len;
 		bin.readUint( data, offset ); offset += 4;
 
@@ -406,7 +406,7 @@ UPNG.decode = function ( buff ) {
 	if ( foff != 0 ) {
 
 		var fr = out.frames[ out.frames.length - 1 ];
-		fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height ); foff = 0;
+		fr.data = UPNG.decode._decompress( out, fd.slice( 0, foff ), fr.rect.width, fr.rect.height );
 
 	}
 
@@ -819,7 +819,7 @@ UPNG.decode._filterZero = function ( data, out, off, w, h ) {
 	var bpp = UPNG.decode._getBPP( out ), bpl = Math.ceil( w * bpp / 8 ), paeth = UPNG.decode._paeth;
 	bpp = Math.ceil( bpp / 8 );
 
-	var i = 0, di = 1, type = data[ off ], x = 0;
+	var i, di, type = data[ off ], x = 0;
 
 	if ( type > 1 ) data[ off ] = [ 0, 0, 1 ][ type - 2 ];
 	if ( type == 3 ) for ( x = bpp; x < bpl; x ++ ) data[ x + 1 ] = ( data[ x + 1 ] + ( data[ x + 1 - bpp ] >>> 1 ) ) & 255;

+ 1 - 0
examples/jsm/loaders/SVGLoader.js

@@ -1007,6 +1007,7 @@ class SVGLoader extends Loader {
 
 			addStyle( 'fill', 'fill' );
 			addStyle( 'fill-opacity', 'fillOpacity', clamp );
+			addStyle( 'fill-rule', 'fillRule' );
 			addStyle( 'opacity', 'opacity', clamp );
 			addStyle( 'stroke', 'stroke' );
 			addStyle( 'stroke-opacity', 'strokeOpacity', clamp );

+ 7 - 1
examples/jsm/renderers/nodes/Nodes.js

@@ -68,6 +68,9 @@ import SplitNode from './utils/SplitNode.js';
 import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
 import TimerNode from './utils/TimerNode.js';
 
+// procedural
+import CheckerNode from './procedural/CheckerNode.js';
+
 // core
 export * from './core/constants.js';
 
@@ -151,6 +154,9 @@ export {
 	JoinNode,
 	SplitNode,
 	SpriteSheetUVNode,
-	TimerNode
+	TimerNode,
+
+	// procedural
+	CheckerNode
 };
 

+ 193 - 0
examples/jsm/renderers/nodes/ShaderNode.js

@@ -0,0 +1,193 @@
+// inputs
+import ColorNode from './inputs/ColorNode.js';
+import FloatNode from './inputs/FloatNode.js';
+import Vector2Node from './inputs/Vector2Node.js';
+import Vector3Node from './inputs/Vector3Node.js';
+import Vector4Node from './inputs/Vector4Node.js';
+
+// math
+import MathNode from './math/MathNode.js';
+import OperatorNode from './math/OperatorNode.js';
+
+// utils
+import JoinNode from './utils/JoinNode.js';
+import SplitNode from './utils/SplitNode.js';
+
+// core
+import { Vector2, Vector3, Vector4, Color } from 'three';
+
+const NodeHandler = {
+
+	get: function ( node, prop ) {
+
+		// Split Properties Pass
+
+		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
+
+			const splitProps = prop.match( /^[xyzw]{1,4}$/ );
+
+			if ( splitProps !== null ) {
+
+				return ShaderNodeObject( new SplitNode( node, splitProps[ 0 ] ) );
+
+			}
+
+		}
+
+		return node[ prop ];
+
+	}
+
+};
+
+export const ShaderNodeObject = ( obj ) => {
+
+	const type = typeof obj;
+
+	if ( type === 'number' ) {
+
+		return ShaderNodeObject( new FloatNode( obj ).setConst( true ) );
+
+	} else if ( type === 'object' ) {
+
+		if ( obj.isNode === true ) {
+
+			const node = obj;
+
+			if ( node.isProxyNode !== true ) {
+
+				node.isProxyNode = true;
+
+				return new Proxy( node, NodeHandler );
+
+			}
+
+		}
+
+	}
+
+	return obj;
+
+};
+
+export const ShaderNodeArray = ( array ) => {
+
+	const len = array.length;
+
+	for ( let i = 0; i < len; i ++ ) {
+
+		array[ i ] = ShaderNodeObject( array[ i ] );
+
+	}
+
+	return array;
+
+};
+
+export const ShaderNodeScript = ( jsFunc ) => {
+
+	return ( ...params ) => {
+
+		ShaderNodeArray( params );
+
+		return ShaderNodeObject( jsFunc( ...params ) );
+
+	};
+
+};
+
+export const ShaderNode = ( obj ) => {
+
+	return ShaderNodeScript( obj );
+
+};
+
+//
+// Node Material Shader Syntax
+//
+
+export const uniform = ShaderNodeScript( ( inputNode ) => {
+
+	inputNode.setConst( false );
+
+	return inputNode;
+
+} );
+
+export const float = ( val ) => {
+
+	return ShaderNodeObject( new FloatNode( val ).setConst( true ) );
+
+};
+
+export const color = ( ...params ) => {
+
+	return ShaderNodeObject( new ColorNode( new Color( ...params ) ).setConst( true ) );
+
+};
+
+export const join = ( ...params ) => {
+
+	return ShaderNodeObject( new JoinNode( ShaderNodeArray( params ) ) );
+
+};
+
+export const vec2 = ( ...params ) => {
+
+	return ShaderNodeObject( new Vector2Node( new Vector2( ...params ) ).setConst( true ) );
+
+};
+
+export const vec3 = ( ...params ) => {
+
+	return ShaderNodeObject( new Vector3Node( new Vector3( ...params ) ).setConst( true ) );
+
+};
+
+export const vec4 = ( ...params ) => {
+
+	return ShaderNodeObject( new Vector4Node( new Vector4( ...params ) ).setConst( true ) );
+
+};
+
+export const add = ( ...params ) => {
+
+	return ShaderNodeObject( new OperatorNode( '+', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const sub = ( ...params ) => {
+
+	return new OperatorNode( '-', ...ShaderNodeArray( params ) );
+
+};
+
+export const mul = ( ...params ) => {
+
+	return ShaderNodeObject( new OperatorNode( '*', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const div = ( ...params ) => {
+
+	return ShaderNodeObject( new OperatorNode( '/', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const floor = ( ...params ) => {
+
+	return ShaderNodeObject( new MathNode( 'floor', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const mod = ( ...params ) => {
+
+	return ShaderNodeObject( new MathNode( 'mod', ...ShaderNodeArray( params ) ) );
+
+};
+
+export const sign = ( ...params ) => {
+
+	return ShaderNodeObject( new MathNode( 'sign', ...ShaderNodeArray( params ) ) );
+
+};

+ 7 - 25
examples/jsm/renderers/nodes/accessors/CameraNode.js

@@ -9,6 +9,8 @@ class CameraNode extends Object3DNode {
 
 		super( scope );
 
+		this._inputNode = null;
+
 	}
 
 	getNodeType( builder ) {
@@ -47,37 +49,17 @@ class CameraNode extends Object3DNode {
 
 	}
 
-	generate( builder, output ) {
-
-		const nodeData = builder.getDataFromNode( this );
-
-		let inputNode = this._inputNode;
-
-		if ( nodeData.inputNode === undefined ) {
-
-			const scope = this.scope;
-
-			if ( scope === CameraNode.PROJECTION_MATRIX ) {
-
-				if ( inputNode === null || inputNode.isMatrix4Node !== true ) {
+	generate( builder ) {
 
-					inputNode = new Matrix4Node( null );
-
-				}
-
-			} else {
-
-				return super.generate( builder, output );
-
-			}
+		const scope = this.scope;
 
-			this._inputNode = inputNode;
+		if ( scope === CameraNode.PROJECTION_MATRIX ) {
 
-			nodeData.inputNode = inputNode;
+			this._inputNode = new Matrix4Node( null );
 
 		}
 
-		return inputNode.build( builder, output );
+		return super.generate( builder );
 
 	}
 

+ 2 - 4
examples/jsm/renderers/nodes/accessors/ModelViewProjectionNode.js

@@ -16,14 +16,12 @@ class ModelViewProjectionNode extends Node {
 
 	}
 
-	generate( builder, output ) {
-
-		const type = this.getNodeType( builder );
+	generate( builder ) {
 
 		const mvpSnipped = this._mvpMatrix.build( builder );
 		const positionSnipped = this.position.build( builder, 'vec3' );
 
-		return builder.format( `( ${mvpSnipped} * vec4( ${positionSnipped}, 1.0 ) )`, type, output );
+		return `( ${mvpSnipped} * vec4( ${positionSnipped}, 1.0 ) )`;
 
 	}
 

+ 10 - 42
examples/jsm/renderers/nodes/accessors/NormalNode.js

@@ -21,61 +21,29 @@ class NormalNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const type = this.getNodeType( builder );
-		const nodeData = builder.getDataFromNode( this, builder.shaderStage );
 		const scope = this.scope;
 
-		let localNormalNode = nodeData.localNormalNode;
+		let outputNode = null;
 
-		if ( localNormalNode === undefined ) {
+		if ( scope === NormalNode.LOCAL ) {
 
-			localNormalNode = new AttributeNode( 'normal', 'vec3' );
+			outputNode = new AttributeNode( 'normal', 'vec3' );
 
-			nodeData.localNormalNode = localNormalNode;
+		} else if ( scope === NormalNode.VIEW ) {
 
-		}
-
-		let outputNode = localNormalNode;
-
-		if ( scope === NormalNode.VIEW ) {
-
-			let viewNormalNode = nodeData.viewNormalNode;
-
-			if ( viewNormalNode === undefined ) {
-
-				const vertexNormalNode = new OperatorNode( '*', new ModelNode( ModelNode.NORMAL_MATRIX ), localNormalNode );
-
-				viewNormalNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
-
-				nodeData.viewNormalNode = viewNormalNode;
-
-			}
-
-			outputNode = viewNormalNode;
+			const vertexNormalNode = new OperatorNode( '*', new ModelNode( ModelNode.NORMAL_MATRIX ), new NormalNode( NormalNode.LOCAL ) );
+			outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
 
 		} else if ( scope === NormalNode.WORLD ) {
 
-			let worldNormalNode = nodeData.worldNormalNode;
-
-			if ( worldNormalNode === undefined ) {
-
-				const vertexNormalNode = inverseTransformDirection.call( { dir: new NormalNode( NormalNode.VIEW ), matrix: new CameraNode( CameraNode.VIEW_MATRIX ) } );
-
-				worldNormalNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
-
-				nodeData.worldNormalNode = worldNormalNode;
-
-			}
-
-			outputNode = worldNormalNode;
+			const vertexNormalNode = inverseTransformDirection.call( { dir: new NormalNode( NormalNode.VIEW ), matrix: new CameraNode( CameraNode.VIEW_MATRIX ) } );
+			outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
 
 		}
 
-		const normalSnipped = outputNode.build( builder, type );
-
-		return builder.format( normalSnipped, type, output );
+		return outputNode.build( builder );
 
 	}
 

+ 9 - 33
examples/jsm/renderers/nodes/accessors/Object3DNode.js

@@ -78,49 +78,25 @@ class Object3DNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const nodeData = builder.getDataFromNode( this );
-
-		let inputNode = this._inputNode;
-
-		if ( nodeData.inputNode === undefined ) {
-
-			const scope = this.scope;
-
-			if ( scope === Object3DNode.WORLD_MATRIX || scope === Object3DNode.VIEW_MATRIX ) {
-
-				if ( inputNode === null || inputNode.isMatrix4Node !== true ) {
-
-					inputNode = new Matrix4Node( /*null*/ );
-
-				}
-
-			} else if ( scope === Object3DNode.NORMAL_MATRIX ) {
-
-				if ( inputNode === null || inputNode.isMatrix3Node !== true ) {
-
-					inputNode = new Matrix3Node( /*null*/ );
-
-				}
-
-			} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION ) {
+		const scope = this.scope;
 
-				if ( inputNode === null || inputNode.isVector3Node !== true ) {
+		if ( scope === Object3DNode.WORLD_MATRIX || scope === Object3DNode.VIEW_MATRIX ) {
 
-					inputNode = new Vector3Node();
+			this._inputNode = new Matrix4Node( /*null*/ );
 
-				}
+		} else if ( scope === Object3DNode.NORMAL_MATRIX ) {
 
-			}
+			this._inputNode = new Matrix3Node( /*null*/ );
 
-			this._inputNode = inputNode;
+		} else if ( scope === Object3DNode.POSITION || scope === Object3DNode.VIEW_POSITION ) {
 
-			nodeData.inputNode = inputNode;
+			this._inputNode = new Vector3Node();
 
 		}
 
-		return inputNode.build( builder, output );
+		return this._inputNode.build( builder );
 
 	}
 

+ 2 - 5
examples/jsm/renderers/nodes/accessors/PointUVNode.js

@@ -10,12 +10,9 @@ class PointUVNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const type = this.getNodeType( builder );
-		const snippet = 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
-
-		return builder.format( snippet, type, output );
+		return 'vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y )';
 
 	}
 

+ 15 - 58
examples/jsm/renderers/nodes/accessors/PositionNode.js

@@ -13,7 +13,7 @@ class PositionNode extends Node {
 	static VIEW = 'view';
 	static VIEW_DIRECTION = 'viewDirection';
 
-	constructor( scope = PositionNode.POSITION ) {
+	constructor( scope = PositionNode.LOCAL ) {
 
 		super( 'vec3' );
 
@@ -21,77 +21,34 @@ class PositionNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const type = this.getNodeType( builder );
-		const nodeData = builder.getDataFromNode( this );
 		const scope = this.scope;
 
-		let localPositionNode = nodeData.localPositionNode;
+		let outputNode = null;
 
-		if ( localPositionNode === undefined ) {
+		if ( scope === PositionNode.LOCAL ) {
+			
+			outputNode = new AttributeNode( 'position', 'vec3' );
+			
+		} else if ( scope === PositionNode.WORLD ) {
 
-			localPositionNode = new AttributeNode( 'position', 'vec3' );
-
-			nodeData.localPositionNode = localPositionNode;
-
-		}
-
-		let outputNode = localPositionNode;
-
-		if ( scope === PositionNode.WORLD ) {
-
-			let worldPositionNode = nodeData.worldPositionNode;
-
-			if ( worldPositionNode === undefined ) {
-
-				const vertexPositionNode = transformDirection.call( { dir: localPositionNode, matrix: new ModelNode( ModelNode.WORLD_MATRIX ) } );
-
-				worldPositionNode = new VaryNode( vertexPositionNode );
-
-				nodeData.worldPositionNode = worldPositionNode;
-
-			}
-
-			outputNode = worldPositionNode;
+			const vertexPositionNode = transformDirection.call( { dir: new PositionNode( PositionNode.LOCAL ), matrix: new ModelNode( ModelNode.WORLD_MATRIX ) } );
+			outputNode = new VaryNode( vertexPositionNode );
 
 		} else if ( scope === PositionNode.VIEW ) {
 
-			let viewPositionNode = nodeData.viewPositionNode;
-
-			if ( viewPositionNode === undefined ) {
-
-				const vertexPositionNode = new OperatorNode( '*', new ModelNode( ModelNode.VIEW_MATRIX ), localPositionNode );
-
-				viewPositionNode = new VaryNode( vertexPositionNode );
-
-				nodeData.viewPositionNode = viewPositionNode;
-
-			}
-
-			outputNode = viewPositionNode;
+			const vertexPositionNode = new OperatorNode( '*', new ModelNode( ModelNode.VIEW_MATRIX ), new PositionNode( PositionNode.LOCAL ) );
+			outputNode = new VaryNode( vertexPositionNode );
 
 		} else if ( scope === PositionNode.VIEW_DIRECTION ) {
 
-			let viewDirPositionNode = nodeData.viewDirPositionNode;
-
-			if ( viewDirPositionNode === undefined ) {
-
-				const vertexPositionNode = new MathNode( MathNode.NEGATE, new PositionNode( PositionNode.VIEW ) );
-
-				viewDirPositionNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexPositionNode ) );
-
-				nodeData.viewDirPositionNode = viewDirPositionNode;
-
-			}
-
-			outputNode = viewDirPositionNode;
+			const vertexPositionNode = new MathNode( MathNode.NEGATE, new PositionNode( PositionNode.VIEW ) );
+			outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexPositionNode ) );
 
 		}
 
-		const positionSnipped = outputNode.build( builder, type );
-
-		return builder.format( positionSnipped, type, output );
+		return outputNode.build( builder, this.getNodeType( builder ) );
 
 	}
 

+ 2 - 2
examples/jsm/renderers/nodes/accessors/ReferenceNode.js

@@ -80,9 +80,9 @@ class ReferenceNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		return this.node.build( builder, output );
+		return this.node.build( builder, this.getNodeType( builder ) );
 
 	}
 

+ 5 - 18
examples/jsm/renderers/nodes/core/AttributeNode.js

@@ -25,32 +25,19 @@ class AttributeNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const attributeName = this.getAttributeName( builder );
-		const attributeType = this.getNodeType( builder );
-
-		const attribute = builder.getAttribute( attributeName, attributeType );
+		const attribute = builder.getAttribute( this.getAttributeName( builder ), this.getNodeType( builder ) );
 
 		if ( builder.isShaderStage( 'vertex' ) ) {
 
-			return builder.format( attribute.name, attribute.type, output );
+			return attribute.name;
 
 		} else {
 
-			const nodeData = builder.getDataFromNode( this, builder.shaderStage );
-
-			let nodeVary = nodeData.varyNode;
-
-			if ( nodeVary === undefined ) {
-
-				nodeVary = new VaryNode( this );
-
-				nodeData.nodeVary = nodeVary;
-
-			}
+			const nodeVary = new VaryNode( this );
 
-			return nodeVary.build( builder, output );
+			return nodeVary.build( builder, attribute.type );
 
 		}
 

+ 2 - 4
examples/jsm/renderers/nodes/core/CodeNode.js

@@ -30,7 +30,7 @@ class CodeNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		if ( this.useKeywords === true ) {
 
@@ -66,9 +66,7 @@ class CodeNode extends Node {
 
 		}
 
-		const type = this.getNodeType( builder );
-		const nodeCode = builder.getCodeFromNode( this, type );
-
+		const nodeCode = builder.getCodeFromNode( this, this.getNodeType( builder ) );
 		nodeCode.code = this.code;
 
 		return nodeCode.code;

+ 3 - 4
examples/jsm/renderers/nodes/core/ConstNode.js

@@ -12,12 +12,11 @@ class ConstNode extends CodeNode {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const code = super.generate( builder );
 
-		const type = this.getNodeType( builder );
-		const nodeCode = builder.getCodeFromNode( this, type );
+		const nodeCode = builder.getCodeFromNode( this, this.getNodeType( builder ) );
 
 		if ( this.name !== '' ) {
 
@@ -31,7 +30,7 @@ class ConstNode extends CodeNode {
 
 		nodeCode.code = `#define ${propertyName} ${code}`;
 
-		return builder.format( propertyName, type, output );
+		return propertyName;
 
 	}
 

+ 4 - 7
examples/jsm/renderers/nodes/core/ExpressionNode.js

@@ -1,6 +1,6 @@
-import Node from './Node.js';
+import TempNode from './TempNode.js';
 
-class ExpressionNode extends Node {
+class ExpressionNode extends TempNode {
 
 	constructor( snipped = '', nodeType = null ) {
 
@@ -10,12 +10,9 @@ class ExpressionNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const type = this.getNodeType( builder );
-		const snipped = this.snipped;
-
-		return builder.format( `( ${ snipped } )`, type, output );
+		return `( ${ this.snipped } )`;
 
 	}
 

+ 2 - 5
examples/jsm/renderers/nodes/core/FunctionCallNode.js

@@ -31,7 +31,7 @@ class FunctionCallNode extends TempNode {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const params = [];
 
@@ -56,12 +56,9 @@ class FunctionCallNode extends TempNode {
 
 		}
 
-		const type = this.getNodeType( builder );
 		const functionName = functionNode.build( builder, 'property' );
 
-		const callSnippet = `${functionName}( ${params.join( ', ' )} )`;
-
-		return builder.format( callSnippet, type, output );
+		return `${functionName}( ${params.join( ', ' )} )`;
 
 	}
 

+ 21 - 0
examples/jsm/renderers/nodes/core/Node.js

@@ -50,6 +50,27 @@ class Node {
 
 		builder.addNode( this );
 
+		const isGenerateOnce = this.generate.length === 1;
+
+		if ( isGenerateOnce ) {
+
+			const type = this.getNodeType( builder );
+			const nodeData = builder.getDataFromNode( this );
+
+			let snippet = nodeData.snippet;
+
+			if ( snippet === undefined ) {
+
+				snippet = this.generate( builder );
+
+				nodeData.snippet = snippet;
+
+			}
+
+			return builder.format( snippet, type, output );
+
+		}
+
 		return this.generate( builder, output );
 
 	}

+ 3 - 7
examples/jsm/renderers/nodes/core/PropertyNode.js

@@ -10,16 +10,12 @@ class PropertyNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const type = this.getNodeType( builder );
-
-		const nodeVary = builder.getVarFromNode( this, type );
+		const nodeVary = builder.getVarFromNode( this, this.getNodeType( builder ) );
 		nodeVary.name = this.name;
 
-		const propertyName = builder.getPropertyName( nodeVary );
-
-		return builder.format( propertyName, type, output );
+		return builder.getPropertyName( nodeVary );
 
 	}
 

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

@@ -20,7 +20,7 @@ class StructVarNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const type = this.getNodeType( builder );
 
@@ -66,7 +66,7 @@ class StructVarNode extends Node {
 
 		}
 
-		return builder.format( property, type, output );
+		return property;
 
 	}
 

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

@@ -17,15 +17,25 @@ class TempNode extends Node {
 			const nodeVar = builder.getVarFromNode( this, type );
 			const propertyName = builder.getPropertyName( nodeVar );
 
-			const snippet = super.build( builder, type );
+			const nodeData = builder.getDataFromNode( this );
 
-			builder.addFlowCode( `${propertyName} = ${snippet}` );
+			let snippet = nodeData.snippet;
+
+			if ( snippet === undefined ) {
+
+				snippet = super.build( builder, type );
+
+				builder.addFlowCode( `${propertyName} = ${snippet}` );
+
+				nodeData.snippet = snippet;
+
+			}
 
 			return builder.format( propertyName, type, output );
 
 		} else {
 
-			return super.build( builder, type );
+			return super.build( builder, output );
 
 		}
 

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

@@ -17,7 +17,7 @@ class VarNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const type = builder.getVectorType( this.getNodeType( builder ) );
 		const name = this.name;
@@ -37,7 +37,7 @@ class VarNode extends Node {
 
 		builder.addFlowCode( `${propertyName} = ${snippet}` );
 
-		return builder.format( propertyName, type, output );
+		return propertyName;
 
 	}
 

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

@@ -19,7 +19,7 @@ class VaryNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const type = this.getNodeType( builder );
 		const value = this.value;
@@ -30,7 +30,7 @@ class VaryNode extends Node {
 		// force nodeVary.snippet work in vertex stage
 		builder.flowNodeFromShaderStage( NodeShaderStage.Vertex, value, type, propertyName );
 
-		return builder.format( propertyName, type, output );
+		return propertyName;
 
 	}
 

+ 10 - 18
examples/jsm/renderers/nodes/display/ColorSpaceNode.js

@@ -85,35 +85,27 @@ class ColorSpaceNode extends TempNode {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
+
+		const type = this.getNodeType( builder );
 
 		const method = this.method;
 		const input = this.input;
 
 		if ( method !== ColorSpaceNode.LINEAR_TO_LINEAR ) {
 
-			const nodeData = builder.getDataFromNode( this );
-
-			let encodingFunctionCallNode = nodeData.encodingFunctionCallNode;
-
-			if (encodingFunctionCallNode === undefined) {
-
-				const encodingFunctionNode = EncodingFunctions[ method ];
-
-				encodingFunctionCallNode = encodingFunctionNode.call( {
-					value: input,
-					factor: this.factor
-				} );
-
-				nodeData.encodingFunctionCallNode = encodingFunctionCallNode;
+			const encodingFunctionNode = EncodingFunctions[ method ];
 
-			}
+			const encodingFunctionCallNode = encodingFunctionNode.call( {
+				value: input,
+				factor: this.factor
+			} );
 
-			return encodingFunctionCallNode.build( builder, output );
+			return encodingFunctionCallNode.build( builder, type );
 
 		} else {
 
-			return input.build( builder, output );
+			return input.build( builder, type );
 
 		}
 

+ 11 - 22
examples/jsm/renderers/nodes/display/NormalMapNode.js

@@ -50,12 +50,11 @@ class NormalMapNode extends TempNode {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const type = this.getNodeType( builder );
-		const normalMapType = this.normalMapType;
 
-		const nodeData = builder.getDataFromNode( this );
+		const normalMapType = this.normalMapType;
 
 		const normalOP = new OperatorNode( '*', this.value, new FloatNode( 2.0 ).setConst( true ) );
 		const normalMap = new OperatorNode( '-', normalOP, new FloatNode( 1.0 ).setConst( true ) );
@@ -66,29 +65,19 @@ class NormalMapNode extends TempNode {
 
 			const normal = new MathNode( MathNode.NORMALIZE, vertexNormalNode );
 
-			return normal.build( builder, output );
+			return normal.build( builder, type );
 
 		} else if ( normalMapType === TangentSpaceNormalMap ) {
 
-			let perturbNormal2ArbCall = nodeData.perturbNormal2ArbCall;
-
-			if (perturbNormal2ArbCall === undefined) {
-
-				perturbNormal2ArbCall = perturbNormal2Arb.call( {
-					eye_pos: new PositionNode( PositionNode.VIEW ),
-					surf_norm: new NormalNode( NormalNode.VIEW ),
-					mapN: normalMap,
-					faceDirection: new FloatNode( 1.0 ).setConst( true ),
-					uv: new UVNode()
-				} );
+			const perturbNormal2ArbCall = perturbNormal2Arb.call( {
+				eye_pos: new PositionNode( PositionNode.VIEW ),
+				surf_norm: new NormalNode( NormalNode.VIEW ),
+				mapN: normalMap,
+				faceDirection: new FloatNode( 1.0 ).setConst( true ),
+				uv: new UVNode()
+			} );
 
-				nodeData.perturbNormal2ArbCall = perturbNormal2ArbCall;
-
-			}
-
-			const snippet = perturbNormal2ArbCall.build( builder, output );
-			
-			return builder.format( snippet, type, output );
+			return perturbNormal2ArbCall.build( builder, type );
 			
 		}
 

+ 3 - 5
examples/jsm/renderers/nodes/lights/LightContextNode.js

@@ -15,7 +15,7 @@ class LightContextNode extends ContextNode {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const type = this.getNodeType( builder );
 
@@ -40,13 +40,11 @@ class LightContextNode extends ContextNode {
 
 		}
 
-		const totalLightSnippet = `( ${reflectedLight}.directDiffuse + ${reflectedLight}.directSpecular )`;
-
 		// add code
 
-		super.generate( builder, output );
+		super.generate( builder, type );
 
-		return builder.format( totalLightSnippet, type, output );
+		return `( ${reflectedLight}.directDiffuse + ${reflectedLight}.directSpecular )`;
 
 	}
 

+ 4 - 2
examples/jsm/renderers/nodes/lights/LightNode.js

@@ -52,7 +52,9 @@ class LightNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
+
+		const type = this.getNodeType( builder );
 
 		this.lightPositionView.object3d = this.light;
 
@@ -72,7 +74,7 @@ class LightNode extends Node {
 
 		}
 
-		return this.color.build( builder, output );
+		return this.color.build( builder, type );
 
 	}
 

+ 2 - 2
examples/jsm/renderers/nodes/lights/LightsNode.js

@@ -11,7 +11,7 @@ class LightsNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const lightNodes = this.lightNodes;
 
@@ -21,7 +21,7 @@ class LightsNode extends Node {
 
 		}
 
-		return builder.format( 'vec3( 0.0 )', this.getNodeType( builder ), output );
+		return 'vec3( 0.0 )';
 
 	}
 

+ 4 - 4
examples/jsm/renderers/nodes/math/MathNode.js

@@ -105,7 +105,7 @@ class MathNode extends TempNode {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const method = this.method;
 
@@ -114,11 +114,11 @@ class MathNode extends TempNode {
 
 		if ( method === MathNode.NEGATE ) {
 
-			return builder.format( '( -' + this.a.build( builder, inputType ) + ' )', type, output );
+			return '( -' + this.a.build( builder, inputType ) + ' )';
 
 		} else if ( method === MathNode.INVERT ) {
 
-			return builder.format( '( 1.0 - ' + this.a.build( builder, inputType ) + ' )', type, output );
+			return '( 1.0 - ' + this.a.build( builder, inputType ) + ' )';
 
 		} else {
 
@@ -177,7 +177,7 @@ class MathNode extends TempNode {
 
 			}
 
-			return builder.format( `${method}( ${params.join(', ')} )`, type, output );
+			return `${method}( ${params.join(', ')} )`;
 
 		}
 

+ 3 - 3
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -22,13 +22,13 @@ class OperatorNode extends TempNode {
 
 			// matrix x vector
 
-			return typeB;
+			return builder.getVectorFromMatrix( typeA );
 
 		} else if ( builder.isVector( typeA ) && builder.isMatrix( typeB ) ) {
 
 			// vector x matrix
 
-			return typeA;
+			return builder.getVectorFromMatrix( typeB );
 
 		} else if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
 
@@ -72,7 +72,7 @@ class OperatorNode extends TempNode {
 		const a = this.a.build( builder, typeA );
 		const b = this.b.build( builder, typeB );
 
-		return builder.format( `( ${a} ${this.op} ${b} )`, type, output );
+		return `( ${a} ${this.op} ${b} )`;
 
 	}
 

+ 38 - 0
examples/jsm/renderers/nodes/procedural/CheckerNode.js

@@ -0,0 +1,38 @@
+import FunctionNode from '../core/FunctionNode.js';
+import Node from '../core/Node.js';
+import UVNode from '../accessors/UVNode.js';
+
+import { ShaderNode, float, add, mul, floor, mod, sign } from '../ShaderNode.js';
+
+// Three.JS Shader Language
+const checkerShaderNode = ShaderNode( ( uv ) => {
+
+	uv = mul( uv, 2.0 );
+
+	const cx = floor( uv.x );
+	const cy = floor( uv.y );
+	const result = mod( add( cx, cy ), 2.0 );
+
+	return sign( result );
+
+} );
+
+class CheckerNode extends Node {
+
+	constructor( uv = new UVNode() ) {
+
+		super( 'float' );
+
+		this.uv = uv;
+
+	}
+
+	generate( builder, output ) {
+
+		return checkerShaderNode( this.uv ).build( builder, output );
+
+	}
+
+}
+
+export default CheckerNode;

+ 2 - 4
examples/jsm/renderers/nodes/utils/JoinNode.js

@@ -16,7 +16,7 @@ class JoinNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const type = this.getNodeType( builder );
 		const values = this.values;
@@ -33,9 +33,7 @@ class JoinNode extends Node {
 
 		}
 
-		const snippet = `${type}( ${ snippetValues.join( ', ' ) } )`;
-
-		return builder.format( snippet, type, output );
+		return `${type}( ${ snippetValues.join( ', ' ) } )`;
 
 	}
 

+ 17 - 5
examples/jsm/renderers/nodes/utils/SplitNode.js

@@ -17,14 +17,26 @@ class SplitNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const type = this.node.getNodeType( builder );
-		const nodeSnippet = this.node.build( builder, type );
+		const node = this.node;
+		const nodeTypeLength = builder.getTypeLength( node.getNodeType( builder ) );
 
-		const snippet = `${nodeSnippet}.${this.components}`;
+		const components = this.components;
 
-		return builder.format( snippet, this.getNodeType( builder ), output );
+		let type = null;
+
+		if ( components.length >= nodeTypeLength ) {
+
+			// need expand the input node
+
+			type = this.getNodeType( builder );
+
+		}
+
+		const nodeSnippet = node.build( builder, type );
+
+		return `${nodeSnippet}.${this.components}`;
 
 	}
 

+ 21 - 32
examples/jsm/renderers/nodes/utils/SpriteSheetUVNode.js

@@ -18,49 +18,38 @@ class SpriteSheetUVNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		const nodeData = builder.getDataFromNode( this );
+		const uv = this.uv;
+		const count = this.count;
+		const frame = this.frame;
 
-		let uvFrame = nodeData.uvFrame;
+		const one = new FloatNode( 1 ).setConst( true );
 
-		if ( nodeData.uvFrame === undefined ) {
+		const width = new SplitNode( count, 'x' );
+		const height = new SplitNode( count, 'y' );
 
-			const uv = this.uv;
-			const count = this.count;
-			const frame = this.frame;
+		const total = new OperatorNode( '*', width, height );
 
-			const one = new FloatNode( 1 ).setConst( true );
+		const roundFrame = new MathNode( MathNode.FLOOR, new MathNode( MathNode.MOD, frame, total ) );
 
-			const width = new SplitNode( count, 'x' );
-			const height = new SplitNode( count, 'y' );
+		const frameNum = new OperatorNode( '+', roundFrame, one );
 
-			const total = new OperatorNode( '*', width, height );
+		const cell = new MathNode( MathNode.MOD, roundFrame, width );
+		const row = new MathNode( MathNode.CEIL, new OperatorNode( '/', frameNum, width ) );
+		const rowInv = new OperatorNode( '-', height, row );
 
-			const roundFrame = new MathNode( MathNode.FLOOR, new MathNode( MathNode.MOD, frame, total ) );
+		const scale = new OperatorNode( '/', one, count );
 
-			const frameNum = new OperatorNode( '+', roundFrame, one );
+		const uvFrameOffset = new JoinNode( [
+			new OperatorNode( '*', cell, new SplitNode( scale, 'x' ) ),
+			new OperatorNode( '*', rowInv, new SplitNode( scale, 'y' ) )
+		] );
 
-			const cell = new MathNode( MathNode.MOD, roundFrame, width );
-			const row = new MathNode( MathNode.CEIL, new OperatorNode( '/', frameNum, width ) );
-			const rowInv = new OperatorNode( '-', height, row );
+		const uvScale = new OperatorNode( '*', uv, scale );
+		const uvFrame = new OperatorNode( '+', uvScale, uvFrameOffset );
 
-			const scale = new OperatorNode( '/', one, count );
-
-			const uvFrameOffset = new JoinNode( [
-				new OperatorNode( '*', cell, new SplitNode( scale, 'x' ) ),
-				new OperatorNode( '*', rowInv, new SplitNode( scale, 'y' ) )
-			] );
-
-			const uvScale = new OperatorNode( '*', uv, scale );
-
-			uvFrame = new OperatorNode( '+', uvScale, uvFrameOffset );
-
-			nodeData.uvFrame = uvFrame;
-
-		}
-
-		return uvFrame.build( builder, output );
+		return uvFrame.build( builder, this.getNodeType( builder ) );
 
 	}
 

+ 15 - 1
examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js

@@ -2,7 +2,7 @@ import NodeBuilder from '../../nodes/core/NodeBuilder.js';
 import NodeSlot from '../../nodes/core/NodeSlot.js';
 import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
 
-import { ShaderChunk } from 'three';
+import { ShaderChunk, LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
 
 const shaderStages = [ 'vertex', 'fragment' ];
 
@@ -262,6 +262,20 @@ class WebGLNodeBuilder extends NodeBuilder {
 
 	}
 
+	getTextureEncodingFromMap( map ) {
+
+		const isWebGL2 = this.renderer.capabilities.isWebGL2;
+
+		if ( isWebGL2 && map && map.isTexture && map.format === RGBAFormat && map.type === UnsignedByteType && map.encoding === sRGBEncoding ) {
+
+			return LinearEncoding; // disable inline decode for sRGB textures in WebGL 2
+
+		}
+
+		return super.getTextureEncodingFromMap( map );
+
+	}
+
 	build() {
 
 		super.build();

+ 17 - 17
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -393,21 +393,6 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	}
 
-	composeShaderCode( code, snippet ) {
-
-		// use regex maybe for security?
-		const versionStrIndex = code.indexOf( '\n' );
-
-		let finalCode = code.substr( 0, versionStrIndex ) + '\n\n';
-
-		finalCode += snippet;
-
-		finalCode += code.substr( versionStrIndex );
-
-		return finalCode;
-
-	}
-
 	build() {
 
 		const keywords = this.getContextValue( 'keywords' );
@@ -422,13 +407,28 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 		super.build();
 
-		this.vertexShader = this.composeShaderCode( this.nativeShader.vertexShader, this.vertexShader );
-		this.fragmentShader = this.composeShaderCode( this.nativeShader.fragmentShader, this.fragmentShader );
+		this.vertexShader = this._composeShaderCode( this.nativeShader.vertexShader, this.vertexShader );
+		this.fragmentShader = this._composeShaderCode( this.nativeShader.fragmentShader, this.fragmentShader );
 
 		return this;
 
 	}
 
+	_composeShaderCode( code, snippet ) {
+
+		// use regex maybe for security?
+		const versionStrIndex = code.indexOf( '\n' );
+
+		let finalCode = code.substr( 0, versionStrIndex ) + '\n\n';
+
+		finalCode += snippet;
+
+		finalCode += code.substr( versionStrIndex );
+
+		return finalCode;
+
+	}
+
 	_getNodeUniform( uniformNode, type ) {
 
 		if ( type === 'float' ) return new FloatNodeUniform( uniformNode );

BIN
examples/models/gltf/SittingBox.glb


BIN
examples/screenshots/webgl_animation_cloth.jpg


BIN
examples/screenshots/webgl_animation_multiple.jpg


BIN
examples/screenshots/webgl_morphtargets_face.jpg


BIN
examples/screenshots/webgl_shading_physical.jpg


BIN
examples/screenshots/webgpu_sandbox.jpg


+ 0 - 2
examples/tags.json

@@ -1,5 +1,4 @@
 {
-	"webgl_animation_cloth": [ "physics", "integration", "shadow" ],
 	"webgl_clipping": [ "solid" ],
 	"webgl_clipping_advanced": [ "solid" ],
 	"webgl_clipping_intersection": [ "solid" ],
@@ -65,7 +64,6 @@
 	"webgl_shaders_ocean": [ "water" ],
 	"webgl_shaders_sky": [ "sun" ],
 	"webgl_shaders_tonemapping": [ "hrd" ],
-	"webgl_shading_physical": [ "pbr" ],
 	"webgl_shadow_contact": [ "onBeforeCompile", "soft" ],
 	"webgl_shadowmap_viewer": [ "directional", "spot" ],
 	"webgl_skinning_simple": [ "animation" ],

BIN
examples/textures/patterns/bright_squares256.png


BIN
examples/textures/patterns/circuit_pattern.png


+ 0 - 6
examples/textures/patterns/readme.txt

@@ -1,6 +0,0 @@
-Texture "bright_squares256.png" from http://subtlepatterns.com/
-
-Slightly modified to have more GPU friendly sizes.
-
-Licensed under a Creative Commons Attribution 3.0 Unported License:
-http://creativecommons.org/licenses/by/3.0/

+ 0 - 629
examples/webgl_animation_cloth.html

@@ -1,629 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js webgl - cloth simulation</title>
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<link type="text/css" rel="stylesheet" href="main.css">
-		<style>
-			body {
-				background-color: #cce0ff;
-				color: #000;
-			}
-			a {
-				color: #080;
-			}
-		</style>
-	</head>
-
-	<body>
-		<div id="info">Simple Cloth Simulation<br/>
-			Verlet integration with relaxed constraints<br/>
-		</div>
-
-		<script type="module">
-
-			import * as THREE from '../build/three.module.js';
-
-			import Stats from './jsm/libs/stats.module.js';
-			import { GUI } from './jsm/libs/dat.gui.module.js';
-
-			import { OrbitControls } from './jsm/controls/OrbitControls.js';
-			import { ParametricGeometry } from './jsm/geometries/ParametricGeometry.js';
-
-			/*
-			 * Cloth Simulation using a relaxed constraints solver
-			 */
-
-			// Suggested Readings
-
-			// Advanced Character Physics by Thomas Jakobsen Character
-			// http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
-			// http://en.wikipedia.org/wiki/Cloth_modeling
-			// http://cg.alexandra.dk/tag/spring-mass-system/
-			// Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf
-
-			const params = {
-				enableWind: true,
-				showBall: false,
-				togglePins: togglePins
-			};
-
-			const DAMPING = 0.03;
-			const DRAG = 1 - DAMPING;
-			const MASS = 0.1;
-			const restDistance = 25;
-
-			const xSegs = 10;
-			const ySegs = 10;
-
-			const clothFunction = plane( restDistance * xSegs, restDistance * ySegs );
-
-			const GRAVITY = 981 * 1.4;
-			const gravity = new THREE.Vector3( 0, - GRAVITY, 0 ).multiplyScalar( MASS );
-
-
-			const TIMESTEP = 18 / 1000;
-			const TIMESTEP_SQ = TIMESTEP * TIMESTEP;
-
-			let pins = [];
-
-			const windForce = new THREE.Vector3( 0, 0, 0 );
-
-			const ballPosition = new THREE.Vector3( 0, - 45, 0 );
-			const ballSize = 60; //40
-
-			const tmpForce = new THREE.Vector3();
-			const diff = new THREE.Vector3();
-
-			class Particle {
-
-				constructor( x, y, z, mass ) {
-
-					this.position = new THREE.Vector3();
-					this.previous = new THREE.Vector3();
-					this.original = new THREE.Vector3();
-					this.a = new THREE.Vector3( 0, 0, 0 ); // acceleration
-					this.mass = mass;
-					this.invMass = 1 / mass;
-					this.tmp = new THREE.Vector3();
-					this.tmp2 = new THREE.Vector3();
-
-					// init
-
-					clothFunction( x, y, this.position ); // position
-					clothFunction( x, y, this.previous ); // previous
-					clothFunction( x, y, this.original );
-
-				}
-
-				// Force -> Acceleration
-
-				addForce( force ) {
-
-					this.a.add(
-						this.tmp2.copy( force ).multiplyScalar( this.invMass )
-					);
-
-				}
-
-				// Performs Verlet integration
-
-				integrate( timesq ) {
-
-					const newPos = this.tmp.subVectors( this.position, this.previous );
-					newPos.multiplyScalar( DRAG ).add( this.position );
-					newPos.add( this.a.multiplyScalar( timesq ) );
-
-					this.tmp = this.previous;
-					this.previous = this.position;
-					this.position = newPos;
-
-					this.a.set( 0, 0, 0 );
-
-				}
-
-			}
-
-			class Cloth {
-
-				constructor( w = 10, h = 10 ) {
-
-					this.w = w;
-					this.h = h;
-
-					const particles = [];
-					const constraints = [];
-
-					// Create particles
-
-					for ( let v = 0; v <= h; v ++ ) {
-
-						for ( let u = 0; u <= w; u ++ ) {
-
-							particles.push(
-								new Particle( u / w, v / h, 0, MASS )
-							);
-
-						}
-
-					}
-
-					// Structural
-
-					for ( let v = 0; v < h; v ++ ) {
-
-						for ( let u = 0; u < w; u ++ ) {
-
-							constraints.push( [
-								particles[ index( u, v ) ],
-								particles[ index( u, v + 1 ) ],
-								restDistance
-							] );
-
-							constraints.push( [
-								particles[ index( u, v ) ],
-								particles[ index( u + 1, v ) ],
-								restDistance
-							] );
-
-						}
-
-					}
-
-					for ( let u = w, v = 0; v < h; v ++ ) {
-
-						constraints.push( [
-							particles[ index( u, v ) ],
-							particles[ index( u, v + 1 ) ],
-							restDistance
-
-						] );
-
-					}
-
-					for ( let v = h, u = 0; u < w; u ++ ) {
-
-						constraints.push( [
-							particles[ index( u, v ) ],
-							particles[ index( u + 1, v ) ],
-							restDistance
-						] );
-
-					}
-
-
-					// While many systems use shear and bend springs,
-					// the relaxed constraints model seems to be just fine
-					// using structural springs.
-					// Shear
-					// const diagonalDist = Math.sqrt(restDistance * restDistance * 2);
-
-
-					// for (v=0;v<h;v++) {
-					// 	for (u=0;u<w;u++) {
-
-					// 		constraints.push([
-					// 			particles[index(u, v)],
-					// 			particles[index(u+1, v+1)],
-					// 			diagonalDist
-					// 		]);
-
-					// 		constraints.push([
-					// 			particles[index(u+1, v)],
-					// 			particles[index(u, v+1)],
-					// 			diagonalDist
-					// 		]);
-
-					// 	}
-					// }
-
-
-					this.particles = particles;
-					this.constraints = constraints;
-
-					function index( u, v ) {
-
-						return u + v * ( w + 1 );
-
-					}
-
-					this.index = index;
-
-				}
-
-			}
-
-			function plane( width, height ) {
-
-				return function ( u, v, target ) {
-
-					const x = ( u - 0.5 ) * width;
-					const y = ( v + 0.5 ) * height;
-					const z = 0;
-
-					target.set( x, y, z );
-
-				};
-
-			}
-
-			function satisfyConstraints( p1, p2, distance ) {
-
-				diff.subVectors( p2.position, p1.position );
-				const currentDist = diff.length();
-				if ( currentDist === 0 ) return; // prevents division by 0
-				const correction = diff.multiplyScalar( 1 - distance / currentDist );
-				const correctionHalf = correction.multiplyScalar( 0.5 );
-				p1.position.add( correctionHalf );
-				p2.position.sub( correctionHalf );
-
-			}
-
-			function simulate( now ) {
-
-				const windStrength = Math.cos( now / 7000 ) * 20 + 40;
-
-				windForce.set( Math.sin( now / 2000 ), Math.cos( now / 3000 ), Math.sin( now / 1000 ) );
-				windForce.normalize();
-				windForce.multiplyScalar( windStrength );
-
-				// Aerodynamics forces
-
-				const particles = cloth.particles;
-
-				if ( params.enableWind ) {
-
-					let indx;
-					const normal = new THREE.Vector3();
-					const indices = clothGeometry.index;
-					const normals = clothGeometry.attributes.normal;
-
-					for ( let i = 0, il = indices.count; i < il; i += 3 ) {
-
-						for ( let j = 0; j < 3; j ++ ) {
-
-							indx = indices.getX( i + j );
-							normal.fromBufferAttribute( normals, indx );
-							tmpForce.copy( normal ).normalize().multiplyScalar( normal.dot( windForce ) );
-							particles[ indx ].addForce( tmpForce );
-
-						}
-
-					}
-
-				}
-
-				for ( let i = 0, il = particles.length; i < il; i ++ ) {
-
-					const particle = particles[ i ];
-					particle.addForce( gravity );
-
-					particle.integrate( TIMESTEP_SQ );
-
-				}
-
-				// Start Constraints
-
-				const constraints = cloth.constraints;
-				const il = constraints.length;
-
-				for ( let i = 0; i < il; i ++ ) {
-
-					const constraint = constraints[ i ];
-					satisfyConstraints( constraint[ 0 ], constraint[ 1 ], constraint[ 2 ] );
-
-				}
-
-				// Ball Constraints
-
-				ballPosition.z = - Math.sin( now / 600 ) * 90; //+ 40;
-				ballPosition.x = Math.cos( now / 400 ) * 70;
-
-				if ( params.showBall ) {
-
-					sphere.visible = true;
-
-					for ( let i = 0, il = particles.length; i < il; i ++ ) {
-
-						const particle = particles[ i ];
-						const pos = particle.position;
-						diff.subVectors( pos, ballPosition );
-						if ( diff.length() < ballSize ) {
-
-							// collided
-							diff.normalize().multiplyScalar( ballSize );
-							pos.copy( ballPosition ).add( diff );
-
-						}
-
-					}
-
-				} else {
-
-					sphere.visible = false;
-
-				}
-
-
-				// Floor Constraints
-
-				for ( let i = 0, il = particles.length; i < il; i ++ ) {
-
-					const particle = particles[ i ];
-					const pos = particle.position;
-					if ( pos.y < - 250 ) {
-
-						pos.y = - 250;
-
-					}
-
-				}
-
-				// Pin Constraints
-
-				for ( let i = 0, il = pins.length; i < il; i ++ ) {
-
-					const xy = pins[ i ];
-					const p = particles[ xy ];
-					p.position.copy( p.original );
-					p.previous.copy( p.original );
-
-				}
-
-
-			}
-
-			/* testing cloth simulation */
-
-			const cloth = new Cloth( xSegs, ySegs );
-
-			const pinsFormation = [];
-			pins = [ 6 ];
-
-			pinsFormation.push( pins );
-
-			pins = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
-			pinsFormation.push( pins );
-
-			pins = [ 0 ];
-			pinsFormation.push( pins );
-
-			pins = []; // cut the rope ;)
-			pinsFormation.push( pins );
-
-			pins = [ 0, cloth.w ]; // classic 2 pins
-			pinsFormation.push( pins );
-
-			pins = pinsFormation[ 1 ];
-
-			function togglePins() {
-
-				pins = pinsFormation[ ~ ~ ( Math.random() * pinsFormation.length ) ];
-
-			}
-
-			let container, stats;
-			let camera, scene, renderer;
-
-			let clothGeometry;
-			let sphere;
-			let object;
-
-			init();
-			animate( 0 );
-
-			function init() {
-
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
-
-				// scene
-
-				scene = new THREE.Scene();
-				scene.background = new THREE.Color( 0xcce0ff );
-				scene.fog = new THREE.Fog( 0xcce0ff, 500, 10000 );
-
-				// camera
-
-				camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 1, 10000 );
-				camera.position.set( 1000, 50, 1500 );
-
-				// lights
-
-				scene.add( new THREE.AmbientLight( 0x666666 ) );
-
-				const light = new THREE.DirectionalLight( 0xdfebff, 1 );
-				light.position.set( 50, 200, 100 );
-				light.position.multiplyScalar( 1.3 );
-
-				light.castShadow = true;
-
-				light.shadow.mapSize.width = 1024;
-				light.shadow.mapSize.height = 1024;
-
-				const d = 300;
-
-				light.shadow.camera.left = - d;
-				light.shadow.camera.right = d;
-				light.shadow.camera.top = d;
-				light.shadow.camera.bottom = - d;
-
-				light.shadow.camera.far = 1000;
-
-				scene.add( light );
-
-				// cloth material
-
-				const loader = new THREE.TextureLoader();
-				const clothTexture = loader.load( 'textures/patterns/circuit_pattern.png' );
-				clothTexture.anisotropy = 16;
-
-				const clothMaterial = new THREE.MeshLambertMaterial( {
-					alphaMap: clothTexture,
-					side: THREE.DoubleSide,
-					alphaTest: 0.5
-				} );
-
-				// cloth geometry
-
-				clothGeometry = new ParametricGeometry( clothFunction, cloth.w, cloth.h );
-
-				// cloth mesh
-
-				object = new THREE.Mesh( clothGeometry, clothMaterial );
-				object.position.set( 0, 0, 0 );
-				object.castShadow = true;
-				scene.add( object );
-
-				// sphere
-
-				const ballGeo = new THREE.SphereGeometry( ballSize, 32, 16 );
-				const ballMaterial = new THREE.MeshLambertMaterial();
-
-				sphere = new THREE.Mesh( ballGeo, ballMaterial );
-				sphere.castShadow = true;
-				sphere.receiveShadow = true;
-				sphere.visible = false;
-				scene.add( sphere );
-
-				// ground
-
-				const groundTexture = loader.load( 'textures/terrain/grasslight-big.jpg' );
-				groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
-				groundTexture.repeat.set( 25, 25 );
-				groundTexture.anisotropy = 16;
-				groundTexture.encoding = THREE.sRGBEncoding;
-
-				const groundMaterial = new THREE.MeshLambertMaterial( { map: groundTexture } );
-
-				let mesh = new THREE.Mesh( new THREE.PlaneGeometry( 20000, 20000 ), groundMaterial );
-				mesh.position.y = - 250;
-				mesh.rotation.x = - Math.PI / 2;
-				mesh.receiveShadow = true;
-				scene.add( mesh );
-
-				// poles
-
-				const poleGeo = new THREE.BoxGeometry( 5, 375, 5 );
-				const poleMat = new THREE.MeshLambertMaterial();
-
-				mesh = new THREE.Mesh( poleGeo, poleMat );
-				mesh.position.x = - 125;
-				mesh.position.y = - 62;
-				mesh.receiveShadow = true;
-				mesh.castShadow = true;
-				scene.add( mesh );
-
-				mesh = new THREE.Mesh( poleGeo, poleMat );
-				mesh.position.x = 125;
-				mesh.position.y = - 62;
-				mesh.receiveShadow = true;
-				mesh.castShadow = true;
-				scene.add( mesh );
-
-				mesh = new THREE.Mesh( new THREE.BoxGeometry( 255, 5, 5 ), poleMat );
-				mesh.position.y = - 250 + ( 750 / 2 );
-				mesh.position.x = 0;
-				mesh.receiveShadow = true;
-				mesh.castShadow = true;
-				scene.add( mesh );
-
-				const gg = new THREE.BoxGeometry( 10, 10, 10 );
-				mesh = new THREE.Mesh( gg, poleMat );
-				mesh.position.y = - 250;
-				mesh.position.x = 125;
-				mesh.receiveShadow = true;
-				mesh.castShadow = true;
-				scene.add( mesh );
-
-				mesh = new THREE.Mesh( gg, poleMat );
-				mesh.position.y = - 250;
-				mesh.position.x = - 125;
-				mesh.receiveShadow = true;
-				mesh.castShadow = true;
-				scene.add( mesh );
-
-				// renderer
-
-				renderer = new THREE.WebGLRenderer( { antialias: true } );
-				renderer.setPixelRatio( window.devicePixelRatio );
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
-				container.appendChild( renderer.domElement );
-
-				renderer.outputEncoding = THREE.sRGBEncoding;
-
-				renderer.shadowMap.enabled = true;
-
-				// controls
-				const controls = new OrbitControls( camera, renderer.domElement );
-				controls.maxPolarAngle = Math.PI * 0.5;
-				controls.minDistance = 1000;
-				controls.maxDistance = 5000;
-
-				// performance monitor
-
-				stats = new Stats();
-				container.appendChild( stats.dom );
-
-				//
-
-				window.addEventListener( 'resize', onWindowResize );
-
-				//
-
-				const gui = new GUI();
-				gui.add( params, 'enableWind' ).name( 'Enable wind' );
-				gui.add( params, 'showBall' ).name( 'Show ball' );
-				gui.add( params, 'togglePins' ).name( 'Toggle pins' );
-
-			}
-
-			//
-
-			function onWindowResize() {
-
-				camera.aspect = window.innerWidth / window.innerHeight;
-				camera.updateProjectionMatrix();
-
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
-			}
-
-			//
-
-			function animate( now ) {
-
-				requestAnimationFrame( animate );
-				simulate( now );
-				render();
-				stats.update();
-
-			}
-
-			function render() {
-
-				const p = cloth.particles;
-
-				for ( let i = 0, il = p.length; i < il; i ++ ) {
-
-					const v = p[ i ].position;
-
-					clothGeometry.attributes.position.setXYZ( i, v.x, v.y, v.z );
-
-				}
-
-				clothGeometry.attributes.position.needsUpdate = true;
-
-				clothGeometry.computeVertexNormals();
-
-				sphere.position.copy( ballPosition );
-
-				renderer.render( scene, camera );
-
-			}
-
-		</script>
-	</body>
-</html>

+ 22 - 7
examples/webgl_morphtargets_face.html

@@ -7,10 +7,7 @@
 		<link type="text/css" rel="stylesheet" href="main.css">
 		<style>
 			body {
-				color: black;
-			}
-			a {
-				color: #f00;
+				background-color: #666666;
 			}
 		</style>
 	</head>
@@ -35,6 +32,8 @@
 
 			import { RoomEnvironment } from './jsm/environments/RoomEnvironment.js';
 
+			import { GUI } from './jsm/libs/dat.gui.module.js';
+
 			init();
 
 			function init() {
@@ -77,22 +76,38 @@
 
 						mixer.clipAction( gltf.animations[ 0 ] ).play();
 
+						// GUI
+
+						const head = mesh.getObjectByName( 'mesh_2' );
+						const influences = head.morphTargetInfluences;
+
+						const gui = new GUI();
+						gui.close();
+
+						for ( const [ key, value ] of Object.entries( head.morphTargetDictionary ) ) {
+
+							gui.add( influences, value, 0, 1, 0.01 )
+								.name( key.replace( 'blendShape1.', '' ) )
+								.listen( influences );
+
+						}
+
 					} );
 
 				const environment = new RoomEnvironment();
 				const pmremGenerator = new THREE.PMREMGenerator( renderer );
 
-				scene.background = new THREE.Color( 0xD5F7F7 );
+				scene.background = new THREE.Color( 0x666666 );
 				scene.environment = pmremGenerator.fromScene( environment ).texture;
 
 				const controls = new OrbitControls( camera, renderer.domElement );
+				controls.enableDamping = true;
 				controls.minDistance = 2.5;
 				controls.maxDistance = 5;
 				controls.minAzimuthAngle = - Math.PI / 2;
 				controls.maxAzimuthAngle = Math.PI / 2;
 				controls.maxPolarAngle = Math.PI / 1.8;
-				controls.target.set( 0, 0, - 0.2 );
-				controls.enableDamping = true;
+				controls.target.set( 0, 0.15, - 0.2 );
 
 				const stats = new Stats();
 				container.appendChild( stats.dom );

+ 0 - 363
examples/webgl_shading_physical.html

@@ -1,363 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js webgl - physically based shading</title>
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
-		<link type="text/css" rel="stylesheet" href="main.css">
-	</head>
-
-	<body>
-		<div id="info">
-			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - webgl physically based shading testbed
-		</div>
-
-		<script type="module">
-
-			import * as THREE from '../build/three.module.js';
-
-			import Stats from './jsm/libs/stats.module.js';
-
-			import { GUI } from './jsm/libs/dat.gui.module.js';
-			import { TrackballControls } from './jsm/controls/TrackballControls.js';
-			import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
-
-			let stats;
-
-			let camera, scene, renderer;
-
-			let mesh;
-
-			let controls;
-
-			let cubeCamera;
-
-			let sunLight, pointLight, ambientLight;
-
-			let mixer;
-
-			const clock = new THREE.Clock();
-
-			let gui, shadowCameraHelper;
-
-			const shadowConfig = {
-
-				shadowCameraVisible: false,
-				shadowCameraNear: 750,
-				shadowCameraFar: 4000,
-				shadowBias: - 0.0002
-
-			};
-
-			init();
-			animate();
-
-			function init() {
-
-				const container = document.createElement( 'div' );
-				document.body.appendChild( container );
-
-				// CAMERA
-
-				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 2, 10000 );
-				camera.position.set( 500, 400, 1200 );
-
-				// SCENE
-
-				scene = new THREE.Scene();
-				scene.fog = new THREE.Fog( 0, 1000, 10000 );
-
-				// CUBE CAMERA
-
-				const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 128, {
-					format: THREE.RGBFormat,
-					generateMipmaps: true,
-					minFilter: THREE.LinearMipmapLinearFilter,
-					encoding: THREE.sRGBEncoding
-				} );
-				cubeCamera = new THREE.CubeCamera( 1, 10000, cubeRenderTarget );
-
-				// TEXTURES
-				const textureLoader = new THREE.TextureLoader();
-
-				const textureSquares = textureLoader.load( "textures/patterns/bright_squares256.png" );
-				textureSquares.repeat.set( 50, 50 );
-				textureSquares.wrapS = textureSquares.wrapT = THREE.RepeatWrapping;
-				textureSquares.magFilter = THREE.NearestFilter;
-				textureSquares.encoding = THREE.sRGBEncoding;
-
-				const textureNoiseColor = textureLoader.load( "textures/disturb.jpg" );
-				textureNoiseColor.repeat.set( 1, 1 );
-				textureNoiseColor.wrapS = textureNoiseColor.wrapT = THREE.RepeatWrapping;
-				textureNoiseColor.encoding = THREE.sRGBEncoding;
-
-				const textureLava = textureLoader.load( "textures/lava/lavatile.jpg" );
-				textureLava.repeat.set( 6, 2 );
-				textureLava.wrapS = textureLava.wrapT = THREE.RepeatWrapping;
-				textureLava.encoding = THREE.sRGBEncoding;
-
-				// GROUND
-
-				const groundMaterial = new THREE.MeshPhongMaterial( {
-					shininess: 80,
-					color: 0xffffff,
-					specular: 0xffffff,
-					map: textureSquares
-				} );
-
-				const planeGeometry = new THREE.PlaneGeometry( 100, 100 );
-
-				const ground = new THREE.Mesh( planeGeometry, groundMaterial );
-				ground.position.set( 0, 0, 0 );
-				ground.rotation.x = - Math.PI / 2;
-				ground.scale.set( 1000, 1000, 1000 );
-				ground.receiveShadow = true;
-				scene.add( ground );
-
-				// MATERIALS
-
-				const materialLambert = new THREE.MeshPhongMaterial( { shininess: 50, color: 0xffffff, map: textureNoiseColor } );
-				const materialPhong = new THREE.MeshPhongMaterial( { shininess: 50, color: 0xffffff, specular: 0x999999, map: textureLava } );
-				const materialPhongCube = new THREE.MeshPhongMaterial( { shininess: 50, color: 0xffffff, specular: 0x999999, envMap: cubeRenderTarget.texture } );
-
-				// OBJECTS
-
-				const sphereGeometry = new THREE.SphereGeometry( 100, 64, 32 );
-				const torusGeometry = new THREE.TorusGeometry( 240, 60, 32, 64 );
-				const cubeGeometry = new THREE.BoxGeometry( 150, 150, 150 );
-
-				addObject( torusGeometry, materialPhong, 0, 100, 0, 0 );
-				addObject( cubeGeometry, materialLambert, 350, 75, 300, 0 );
-
-				mesh = addObject( sphereGeometry, materialPhongCube, 350, 100, - 350, 0 );
-				mesh.add( cubeCamera );
-
-				function addObjectColor( geometry, color, x, y, z, ry ) {
-
-					const material = new THREE.MeshPhongMaterial( { color: 0xffffff } );
-
-					return addObject( geometry, material, x, y, z, ry );
-
-				}
-
-				function addObject( geometry, material, x, y, z, ry ) {
-
-					const tmpMesh = new THREE.Mesh( geometry, material );
-
-					tmpMesh.material.color.offsetHSL( 0.1, - 0.1, 0 );
-
-					tmpMesh.position.set( x, y, z );
-
-					tmpMesh.rotation.y = ry;
-
-					tmpMesh.castShadow = true;
-					tmpMesh.receiveShadow = true;
-
-					scene.add( tmpMesh );
-
-					return tmpMesh;
-
-				}
-
-				const bigCube = new THREE.BoxGeometry( 50, 500, 50 );
-				const midCube = new THREE.BoxGeometry( 50, 200, 50 );
-				const smallCube = new THREE.BoxGeometry( 100, 100, 100 );
-
-				addObjectColor( bigCube, 0xff0000, - 500, 250, 0, 0 );
-				addObjectColor( smallCube, 0xff0000, - 500, 50, - 150, 0 );
-
-				addObjectColor( midCube, 0x00ff00, 500, 100, 0, 0 );
-				addObjectColor( smallCube, 0x00ff00, 500, 50, - 150, 0 );
-
-				addObjectColor( midCube, 0x0000ff, 0, 100, - 500, 0 );
-				addObjectColor( smallCube, 0x0000ff, - 150, 50, - 500, 0 );
-
-				addObjectColor( midCube, 0xff00ff, 0, 100, 500, 0 );
-				addObjectColor( smallCube, 0xff00ff, - 150, 50, 500, 0 );
-
-				addObjectColor( new THREE.BoxGeometry( 500, 10, 10 ), 0xffff00, 0, 600, 0, Math.PI / 4 );
-				addObjectColor( new THREE.BoxGeometry( 250, 10, 10 ), 0xffff00, 0, 600, 0, 0 );
-
-				addObjectColor( new THREE.SphereGeometry( 100, 32, 26 ), 0xffffff, - 300, 100, 300, 0 );
-
-				// MORPHS
-
-				const loader = new GLTFLoader();
-
-				loader.load( "models/gltf/SittingBox.glb", function ( gltf ) {
-
-					const mesh = gltf.scene.children[ 0 ];
-
-					mixer = new THREE.AnimationMixer( mesh );
-
-					mixer.clipAction( gltf.animations[ 0 ] ).setDuration( 10 ).play();
-
-					const s = 200;
-					mesh.scale.set( s, s, s );
-
-					mesh.castShadow = true;
-					mesh.receiveShadow = true;
-
-					scene.add( mesh );
-
-				} );
-
-				// LIGHTS
-
-				ambientLight = new THREE.AmbientLight( 0x3f2806 );
-				scene.add( ambientLight );
-
-				pointLight = new THREE.PointLight( 0xffaa00, 1, 5000 );
-				scene.add( pointLight );
-
-				sunLight = new THREE.DirectionalLight( 0xffffff, 0.3 );
-				sunLight.position.set( 1000, 2000, 1000 );
-				sunLight.castShadow = true;
-				sunLight.shadow.camera.top = 750;
-				sunLight.shadow.camera.bottom = - 750;
-				sunLight.shadow.camera.left = - 750;
-				sunLight.shadow.camera.right = 750;
-				sunLight.shadow.camera.near = shadowConfig.shadowCameraNear;
-				sunLight.shadow.camera.far = shadowConfig.shadowCameraFar;
-				sunLight.shadow.mapSize.set( 1024, 1024 );
-				sunLight.shadow.bias = shadowConfig.shadowBias;
-
-				scene.add( sunLight );
-
-				// SHADOW CAMERA HELPER
-
-				shadowCameraHelper = new THREE.CameraHelper( sunLight.shadow.camera );
-				shadowCameraHelper.visible = shadowConfig.shadowCameraVisible;
-				scene.add( shadowCameraHelper );
-
-				// RENDERER
-
-				renderer = new THREE.WebGLRenderer( { antialias: true } );
-				renderer.setPixelRatio( window.devicePixelRatio );
-				renderer.setSize( window.innerWidth, window.innerHeight );
-				container.appendChild( renderer.domElement );
-
-				//
-
-				renderer.shadowMap.enabled = true;
-				renderer.shadowMap.type = THREE.PCFSoftShadowMap;
-				renderer.outputEncoding = THREE.sRGBEncoding;
-
-				//
-
-				controls = new TrackballControls( camera, renderer.domElement );
-				controls.target.set( 0, 120, 0 );
-
-				controls.rotateSpeed = 1.0;
-				controls.zoomSpeed = 1.2;
-				controls.panSpeed = 0.8;
-
-				controls.staticMoving = true;
-
-				controls.keys = [ 'KeyA', 'KeyS', 'KeyD' ];
-
-
-				// STATS
-
-				stats = new Stats();
-				container.appendChild( stats.dom );
-
-				// EVENTS
-
-				window.addEventListener( 'resize', onWindowResize );
-
-				// GUI
-
-				gui = new GUI( { width: 400 } );
-
-				// SHADOW
-
-				const shadowGUI = gui.addFolder( "Shadow" );
-
-    		shadowGUI.add( shadowConfig, 'shadowCameraVisible' ).onChange( function () {
-
-					shadowCameraHelper.visible = shadowConfig.shadowCameraVisible;
-
-				} );
-
-				shadowGUI.add( shadowConfig, 'shadowCameraNear', 1, 1500 ).onChange( function () {
-
-					sunLight.shadow.camera.near = shadowConfig.shadowCameraNear;
-					sunLight.shadow.camera.updateProjectionMatrix();
-					shadowCameraHelper.update();
-
-				} );
-
-				shadowGUI.add( shadowConfig, 'shadowCameraFar', 1501, 5000 ).onChange( function () {
-
-					sunLight.shadow.camera.far = shadowConfig.shadowCameraFar;
-					sunLight.shadow.camera.updateProjectionMatrix();
-					shadowCameraHelper.update();
-
-				} );
-
-				shadowGUI.add( shadowConfig, 'shadowBias', - 0.01, 0.01 ).onChange( function () {
-
-					sunLight.shadow.bias = shadowConfig.shadowBias;
-
-				} );
-
-				shadowGUI.open();
-
-			}
-
-			//
-
-			function onWindowResize() {
-
-				camera.aspect = window.innerWidth / window.innerHeight;
-				camera.updateProjectionMatrix();
-
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
-				controls.handleResize();
-
-			}
-
-			//
-
-			function animate() {
-
-				requestAnimationFrame( animate );
-
-				stats.begin();
-				render();
-				stats.end();
-
-			}
-
-			function render() {
-
-				// update
-
-				const delta = clock.getDelta();
-
-				controls.update();
-
-				if ( mixer ) {
-
-					mixer.update( delta );
-
-				}
-
-				// render cube map
-
-				mesh.visible = false;
-				cubeCamera.update( renderer, scene );
-				mesh.visible = true;
-
-				// render scene
-
-				renderer.render( scene, camera );
-
-			}
-
-		</script>
-
-	</body>
-</html>

+ 3 - 1
examples/webgpu_sandbox.html

@@ -80,7 +80,9 @@
 				const timerScaleNode = new Nodes.OperatorNode( '*', timerNode, new Nodes.Vector2Node( new THREE.Vector2( - 0.5, 0.1 ) ).setConst( true ) );
 				const animateUV = new Nodes.OperatorNode( '+', new Nodes.UVNode(), timerScaleNode );
 
-				materialBox.colorNode = new Nodes.TextureNode( texture, animateUV );
+				const textureNode = new Nodes.TextureNode( texture, animateUV );
+
+				materialBox.colorNode = new Nodes.MathNode( 'mix', textureNode, new Nodes.CheckerNode( animateUV ), new Nodes.FloatNode( .5) );
 
 				// test uv 2
 				//geometryBox.setAttribute( 'uv2', geometryBox.getAttribute( 'uv' ) );

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "three",
-  "version": "0.132.2",
+  "version": "0.133.0",
   "description": "JavaScript 3D library",
   "main": "build/three.js",
   "module": "build/three.module.js",

+ 1 - 1
src/constants.js

@@ -1,4 +1,4 @@
-export const REVISION = '133dev';
+export const REVISION = '133';
 export const MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 };
 export const TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 };
 export const CullFaceNone = 0;

+ 3 - 2
src/core/Object3D.js

@@ -113,8 +113,9 @@ class Object3D extends EventDispatcher {
 
 	}
 
-	onBeforeRender() {}
-	onAfterRender() {}
+	onBeforeRender( /* renderer, scene, camera, geometry, material, group */ ) {}
+
+	onAfterRender( /* renderer, scene, camera, geometry, material, group */ ) {}
 
 	applyMatrix4( matrix ) {
 

+ 1 - 1
src/objects/SkinnedMesh.js

@@ -123,7 +123,7 @@ class SkinnedMesh extends Mesh {
 		_skinIndex.fromBufferAttribute( geometry.attributes.skinIndex, index );
 		_skinWeight.fromBufferAttribute( geometry.attributes.skinWeight, index );
 
-		_basePosition.fromBufferAttribute( geometry.attributes.position, index ).applyMatrix4( this.bindMatrix );
+		_basePosition.copy( target ).applyMatrix4( this.bindMatrix );
 
 		target.set( 0, 0, 0 );
 

+ 18 - 17
src/renderers/WebGLRenderer.js

@@ -737,7 +737,7 @@ function WebGLRenderer( parameters = {} ) {
 
 		const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
 
-		const program = setProgram( camera, scene, material, object );
+		const program = setProgram( camera, scene, geometry, material, object );
 
 		state.setMaterial( material, frontFaceCW );
 
@@ -769,12 +769,6 @@ function WebGLRenderer( parameters = {} ) {
 
 		}
 
-		if ( geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined ) {
-
-			morphtargets.update( object, geometry, material, program );
-
-		}
-
 		bindingStates.setup( object, material, program, geometry, index );
 
 		let attribute;
@@ -1340,7 +1334,7 @@ function WebGLRenderer( parameters = {} ) {
 
 		if ( object.isImmediateRenderObject ) {
 
-			const program = setProgram( camera, scene, material, object );
+			const program = setProgram( camera, scene, geometry, material, object );
 
 			state.setMaterial( material );
 
@@ -1505,7 +1499,7 @@ function WebGLRenderer( parameters = {} ) {
 
 	}
 
-	function setProgram( camera, scene, material, object ) {
+	function setProgram( camera, scene, geometry, material, object ) {
 
 		if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
 
@@ -1515,11 +1509,11 @@ function WebGLRenderer( parameters = {} ) {
 		const environment = material.isMeshStandardMaterial ? scene.environment : null;
 		const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding;
 		const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment );
-		const vertexAlphas = material.vertexColors === true && !! object.geometry && !! object.geometry.attributes.color && object.geometry.attributes.color.itemSize === 4;
-		const vertexTangents = !! material.normalMap && !! object.geometry && !! object.geometry.attributes.tangent;
-		const morphTargets = !! object.geometry && !! object.geometry.morphAttributes.position;
-		const morphNormals = !! object.geometry && !! object.geometry.morphAttributes.normal;
-		const morphTargetsCount = ( !! object.geometry && !! object.geometry.morphAttributes.position ) ? object.geometry.morphAttributes.position.length : 0;
+		const vertexAlphas = material.vertexColors === true && !! geometry && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4;
+		const vertexTangents = !! material.normalMap && !! geometry && !! geometry.attributes.tangent;
+		const morphTargets = !! geometry && !! geometry.morphAttributes.position;
+		const morphNormals = !! geometry && !! geometry.morphAttributes.normal;
+		const morphTargetsCount = ( !! geometry && !! geometry.morphAttributes.position ) ? geometry.morphAttributes.position.length : 0;
 
 		const materialProperties = properties.get( material );
 		const lights = currentRenderState.state.lights;
@@ -1717,9 +1711,9 @@ function WebGLRenderer( parameters = {} ) {
 
 		}
 
-		// skinning uniforms must be set even if material didn't change
-		// auto-setting of texture unit for bone texture must go before other textures
-		// otherwise textures used for skinning can take over texture units reserved for other material textures
+		// skinning and morph target uniforms must be set even if material didn't change
+		// auto-setting of texture unit for bone and morph texture must go before other textures
+		// otherwise textures used for skinning and morphing can take over texture units reserved for other material textures
 
 		if ( object.isSkinnedMesh ) {
 
@@ -1747,6 +1741,13 @@ function WebGLRenderer( parameters = {} ) {
 
 		}
 
+		if ( !! geometry && ( geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined ) ) {
+
+			morphtargets.update( object, geometry, material, program );
+
+		}
+
+
 		if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {
 
 			materialProperties.receiveShadow = object.receiveShadow;

+ 4 - 3
test/e2e/puppeteer.js

@@ -31,6 +31,7 @@ const exceptionList = [
 	'webgl_loader_texture_lottie', // not sure why this fails
 	'webgl_loader_texture_pvrtc', // not supported in CI, useless
 	'webgl_materials_standard_nodes', // puppeteer does not support import maps yet
+	'webgl_morphtargets_face', // To investigate...
 	'webgl_postprocessing_crossfade', // fails for some misterious reason
 	'webgl_raymarching_reflect', // exception for Github Actions
 	'webgl_test_memory2', // gives fatal error in puppeteer
@@ -147,12 +148,12 @@ const pup = puppeteer.launch( {
 	let endId = files.length;
 
 	if ( 'CI' in process.env ) {
-		
+
 		const jobs = 8;
-		
+
 		beginId = Math.floor( parseInt( process.env.CI.slice( 0, 1 ) ) * files.length / jobs );
 		endId = Math.floor( ( parseInt( process.env.CI.slice( - 1 ) ) + 1 ) * files.length / jobs );
-		
+
 	}
 
 	for ( let id = beginId; id < endId; ++ id ) {