Browse Source

Merge remote-tracking branch 'upstream/dev' into dev34

Mugen87 6 years ago
parent
commit
199d70b0b4
67 changed files with 2054 additions and 1282 deletions
  1. 5 5
      build/three.js
  2. 464 464
      build/three.min.js
  3. 5 5
      build/three.module.js
  4. 0 20
      docs/api/en/loaders/Loader.html
  5. 4 4
      docs/api/en/materials/MeshPhysicalMaterial.html
  6. 5 25
      docs/api/zh/loaders/Loader.html
  7. 4 4
      docs/api/zh/materials/MeshPhysicalMaterial.html
  8. 2 2
      docs/scenes/material-browser.html
  9. 50 50
      editor/js/Sidebar.Material.js
  10. 4 4
      editor/js/Strings.js
  11. 1 0
      examples/files.js
  12. 1 1
      examples/js/controls/OrthographicTrackballControls.js
  13. 8 3
      examples/js/exporters/ColladaExporter.js
  14. 42 3
      examples/js/exporters/GLTFExporter.js
  15. 2 2
      examples/js/loaders/LWOLoader.js
  16. 79 25
      examples/js/loaders/STLLoader.js
  17. 265 1
      examples/js/loaders/deprecated/LegacyJSONLoader.js
  18. 1 1
      examples/js/shaders/TranslucentShader.js
  19. 1 1
      examples/jsm/controls/OrthographicTrackballControls.js
  20. 8 3
      examples/jsm/exporters/ColladaExporter.js
  21. 42 3
      examples/jsm/exporters/GLTFExporter.js
  22. 2 2
      examples/jsm/loaders/LWOLoader.js
  23. 79 25
      examples/jsm/loaders/STLLoader.js
  24. 281 2
      examples/jsm/loaders/deprecated/LegacyJSONLoader.js
  25. 2 2
      examples/jsm/loaders/sea3d/SEA3DLoader.js
  26. 3 2
      examples/jsm/nodes/accessors/NormalNode.js
  27. 52 7
      examples/jsm/nodes/accessors/ReflectNode.js
  28. 2 2
      examples/jsm/nodes/bsdfs/BlinnShininessExponentNode.js
  29. 5 3
      examples/jsm/nodes/materials/StandardNodeMaterial.js
  30. 62 26
      examples/jsm/nodes/materials/nodes/StandardNode.js
  31. 1 1
      examples/jsm/shaders/TranslucentShader.js
  32. 1 1
      examples/misc_controls_transform.html
  33. BIN
      examples/models/fbx/cloth.fbx
  34. 9 9
      examples/webgl_materials_clearcoat_normalmap.html
  35. 6 3
      examples/webgl_materials_cubemap_mipmaps.html
  36. 34 28
      examples/webgl_materials_nodes.html
  37. 203 0
      examples/webgl_materials_sheen.html
  38. 6 6
      examples/webgl_materials_variations_physical.html
  39. 73 62
      package-lock.json
  40. 4 4
      package.json
  41. 0 11
      src/loaders/Loader.d.ts
  42. 1 286
      src/loaders/Loader.js
  43. 4 4
      src/loaders/MaterialLoader.js
  44. 5 5
      src/materials/Material.js
  45. 12 9
      src/materials/MeshPhysicalMaterial.d.ts
  46. 20 12
      src/materials/MeshPhysicalMaterial.js
  47. 0 2
      src/materials/MeshStandardMaterial.d.ts
  48. 7 6
      src/renderers/WebGLRenderer.js
  49. 31 0
      src/renderers/shaders/ShaderChunk/bsdfs.glsl.js
  50. 2 2
      src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_begin.glsl.js
  51. 4 4
      src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js
  52. 2 2
      src/renderers/shaders/ShaderChunk/clearcoat_normalmap_pars_fragment.glsl.js
  53. 4 2
      src/renderers/shaders/ShaderChunk/common.glsl.js
  54. 3 7
      src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js
  55. 1 1
      src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js
  56. 2 2
      src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js
  57. 5 2
      src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js
  58. 33 21
      src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js
  59. 1 1
      src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js
  60. 5 4
      src/renderers/shaders/ShaderLib.js
  61. 7 3
      src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js
  62. 4 2
      src/renderers/webgl/WebGLBufferRenderer.js
  63. 5 3
      src/renderers/webgl/WebGLIndexedBufferRenderer.js
  64. 5 3
      src/renderers/webgl/WebGLProgram.js
  65. 7 4
      src/renderers/webgl/WebGLPrograms.js
  66. 66 60
      src/renderers/webgl/WebGLTextures.js
  67. 0 13
      test/unit/src/loaders/Loader.tests.js

File diff suppressed because it is too large
+ 5 - 5
build/three.js


File diff suppressed because it is too large
+ 464 - 464
build/three.min.js


File diff suppressed because it is too large
+ 5 - 5
build/three.module.js


+ 0 - 20
docs/api/en/loaders/Loader.html

@@ -42,26 +42,6 @@
 		Default is *"anonymous"*.
 		</p>
 
-		<h2>Methods</h2>
-
-		<h3>[method:Material createMaterial]( [param:object m], [param:string texturePath] )</h3>
-		<p>
-		[page:Object m] — The parameters to create the material. <br />
-		[page:String texturePath] — The base path of the textures.
-		</p>
-		<p>
-		Creates the Material based on the parameters m.
-		</p>
-
-		<h3>[method:Array initMaterials]( [param:Array materials], [param:string texturePath] )</h3>
-		<p>
-		[page:Array materials] — an array of parameters to create materials. <br />
-		[page:String texturePath] —  The base path of the textures.
-		</p>
-		<p>
-		Creates an array of [page:Material] based on the array of parameters m. The index of the parameters decide the correct index of the materials.
-		</p>
-
 		<h2>Handlers</h2>
 
 		<p>

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

@@ -55,13 +55,13 @@
 		<h2>Properties</h2>
 		<p>See the base [page:Material] and [page:MeshStandardMaterial] classes for common properties.</p>
 
-		<h3>[property:Float clearCoat]</h3>
+		<h3>[property:Float clearcoat]</h3>
 		<p>
-		ClearCoat level, from *0.0* to *1.0*. Default is *0.0*.
+		Clearcoat level, from *0.0* to *1.0*. Default is *0.0*.
 		</p>
 
-		<h3>[property:Float clearCoatRoughness]</h3>
-		<p>How rough the clearCoat appears, from *0.0* to *1.0*. Default is *0.0*.</p>
+		<h3>[property:Float clearcoatRoughness]</h3>
+		<p>How rough the clearcoat appears, from *0.0* to *1.0*. Default is *0.0*.</p>
 
 		<h3>[property:Boolean isMeshPhysicalMaterial]</h3>
 		<p>

+ 5 - 25
docs/api/zh/loaders/Loader.html

@@ -38,27 +38,7 @@
 
 		<h3>[property:string crossOrigin]</h3>
 		<p>
-            跨域字符串,用于实现跨域,以便从允许CORS从其他域加载url。默认为"anonymous"。
-		</p>
-
-		<h2>Methods</h2>
-
-		<h3>[method:Material createMaterial]( [param:object m], [param:string texturePath] )</h3>
-		<p>
-		[page:Object m] — 所要创建的材质的参数。 <br />
-		[page:String texturePath] — 纹理加载路径。
-		</p>
-		<p>
-		基于参数m来创建材质。
-		</p>
-
-		<h3>[method:Array initMaterials]( [param:Array materials], [param:string texturePath] )</h3>
-		<p>
-		[page:Array materials] — 用于创建材质的参数数组。 <br />
-		[page:String texturePath] —  纹理加载路径。
-		</p>
-		<p>
-		基于参数数组m,来创建 [page:Material] 数组. 参数索引与材质的索引一致。
+			跨域字符串,用于实现跨域,以便从允许CORS从其他域加载url。默认为"anonymous"。
 		</p>
 
 		<h2>Handlers</h2>
@@ -67,10 +47,10 @@
 		*[name].Handlers* is a special object normally used by other loaders like [page:GLTFLoader] or [page:MTLLoader]. It provides an
 		API that allows the definition of special mappings: What loaders should be used in order to load specific files. A typical use case
 		is to overwrite the default loader for textures.<br /><br />
-	
+
 		Note: It's only possible to use *[name].Handlers* if the respective loader support the usage.
 		</p>
-	
+
 		<h3>[method:null add]( [param:Object regex], [param:Loader loader] )</h3>
 		<p>
 		[page:Object regex] — A regular expression.<br />
@@ -78,14 +58,14 @@
 		<p>
 		Registers a loader with the given regular expression.
 		</p>
-	
+
 		<h3>[method:null get]( [param:String file] )</h3>
 		<p>
 		[page:String file] — The file path.
 		<p>
 		Can be used to retrieve the registered loader for the given file path.
 		</p>
-		
+
 		<h2>Source</h2>
 
 		[link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js]

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

@@ -50,13 +50,13 @@
 		<h2>属性(Properties)</h2>
 		<p>共有属性请参见其基类[page:Material]。</p>
 
-		<h3>[property:Float clearCoat]</h3>
+		<h3>[property:Float clearcoat]</h3>
 		<p>
-			ClearCoat级别,从*0.0*到*1.0*。默认值为*0.0*。
+			Clearcoat级别,从*0.0*到*1.0*。默认值为*0.0*。
 		</p>
 
-		<h3>[property:Float clearCoatRoughness]</h3>
-		<p> clearCoat看起来的粗糙程度,从*0.0*到*1.0*。默认值为*0.0*。</p>
+		<h3>[property:Float clearcoatRoughness]</h3>
+		<p> clearcoat看起来的粗糙程度,从*0.0*到*1.0*。默认值为*0.0*。</p>
 
 		<h3>[property:Boolean isMeshPhysicalMaterial]</h3>
 		<p> 用于检查此类或派生类是否为Lambert网格材质。默认值为 *true*。<br /><br />

+ 2 - 2
docs/scenes/material-browser.html

@@ -595,8 +595,8 @@
 				folder.add( material, 'roughness', 0, 1 );
 				folder.add( material, 'metalness', 0, 1 );
 				folder.add( material, 'reflectivity', 0, 1 );
-				folder.add( material, 'clearCoat', 0, 1 ).step( 0.01 );
-				folder.add( material, 'clearCoatRoughness', 0, 1 ).step( 0.01 );
+				folder.add( material, 'clearcoat', 0, 1 ).step( 0.01 );
+				folder.add( material, 'clearcoatRoughness', 0, 1 ).step( 0.01 );
 				folder.add( material, 'flatShading' ).onChange( needsUpdate( material, geometry ) );
 				folder.add( material, 'wireframe' );
 				folder.add( material, 'wireframeLinewidth', 0, 10 );

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

@@ -225,25 +225,25 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialShininessRow );
 
-	// clearCoat
+	// clearcoat
 
-	var materialClearCoatRow = new UI.Row();
-	var materialClearCoat = new UI.Number( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
+	var materialClearcoatRow = new UI.Row();
+	var materialClearcoat = new UI.Number( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
 
-	materialClearCoatRow.add( new UI.Text( strings.getKey( 'sidebar/material/clearcoat' ) ).setWidth( '90px' ) );
-	materialClearCoatRow.add( materialClearCoat );
+	materialClearcoatRow.add( new UI.Text( strings.getKey( 'sidebar/material/clearcoat' ) ).setWidth( '90px' ) );
+	materialClearcoatRow.add( materialClearcoat );
 
-	container.add( materialClearCoatRow );
+	container.add( materialClearcoatRow );
 
-	// clearCoatRoughness
+	// clearcoatRoughness
 
-	var materialClearCoatRoughnessRow = new UI.Row();
-	var materialClearCoatRoughness = new UI.Number( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
+	var materialClearcoatRoughnessRow = new UI.Row();
+	var materialClearcoatRoughness = new UI.Number( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
 
-	materialClearCoatRoughnessRow.add( new UI.Text( strings.getKey( 'sidebar/material/clearcoatroughness' ) ).setWidth( '90px' ) );
-	materialClearCoatRoughnessRow.add( materialClearCoatRoughness );
+	materialClearcoatRoughnessRow.add( new UI.Text( strings.getKey( 'sidebar/material/clearcoatroughness' ) ).setWidth( '90px' ) );
+	materialClearcoatRoughnessRow.add( materialClearcoatRoughness );
 
-	container.add( materialClearCoatRoughnessRow );
+	container.add( materialClearcoatRoughnessRow );
 
 	// vertex colors
 
@@ -353,19 +353,19 @@ Sidebar.Material = function ( editor ) {
 
 	// clearcoat normal map
 
-	var materialClearCoatNormalMapRow = new UI.Row();
-	var materialClearCoatNormalMapEnabled = new UI.Checkbox( false ).onChange( update );
-	var materialClearCoatNormalMap = new UI.Texture().onChange( update );
-	var materialClearCoatNormalScaleX = new UI.Number( 1 ).setWidth( '30px' ).onChange( update );
-	var materialClearCoatNormalScaleY = new UI.Number( 1 ).setWidth( '30px' ).onChange( update );
+	var materialClearcoatNormalMapRow = new UI.Row();
+	var materialClearcoatNormalMapEnabled = new UI.Checkbox( false ).onChange( update );
+	var materialClearcoatNormalMap = new UI.Texture().onChange( update );
+	var materialClearcoatNormalScaleX = new UI.Number( 1 ).setWidth( '30px' ).onChange( update );
+	var materialClearcoatNormalScaleY = new UI.Number( 1 ).setWidth( '30px' ).onChange( update );
 
-	materialClearCoatNormalMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/clearcoatnormalmap' ) ).setWidth( '90px' ) );
-	materialClearCoatNormalMapRow.add( materialClearCoatNormalMapEnabled );
-	materialClearCoatNormalMapRow.add( materialClearCoatNormalMap );
-	materialClearCoatNormalMapRow.add( materialClearCoatNormalScaleX );
-	materialClearCoatNormalMapRow.add( materialClearCoatNormalScaleY );
+	materialClearcoatNormalMapRow.add( new UI.Text( strings.getKey( 'sidebar/material/clearcoatnormalmap' ) ).setWidth( '90px' ) );
+	materialClearcoatNormalMapRow.add( materialClearcoatNormalMapEnabled );
+	materialClearcoatNormalMapRow.add( materialClearcoatNormalMap );
+	materialClearcoatNormalMapRow.add( materialClearcoatNormalScaleX );
+	materialClearcoatNormalMapRow.add( materialClearcoatNormalScaleY );
 
-	container.add( materialClearCoatNormalMapRow );
+	container.add( materialClearcoatNormalMapRow );
 
 	// displacement map
 
@@ -654,15 +654,15 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			if ( material.clearCoat !== undefined && Math.abs( material.clearCoat - materialClearCoat.getValue() ) >= 0.01 ) {
+			if ( material.clearcoat !== undefined && Math.abs( material.clearcoat - materialClearcoat.getValue() ) >= 0.01 ) {
 
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'clearCoat', materialClearCoat.getValue(), currentMaterialSlot ) );
+				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'clearcoat', materialClearcoat.getValue(), currentMaterialSlot ) );
 
 			}
 
-			if ( material.clearCoatRoughness !== undefined && Math.abs( material.clearCoatRoughness - materialClearCoatRoughness.getValue() ) >= 0.01 ) {
+			if ( material.clearcoatRoughness !== undefined && Math.abs( material.clearcoatRoughness - materialClearcoatRoughness.getValue() ) >= 0.01 ) {
 
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'clearCoatRoughness', materialClearCoatRoughness.getValue(), currentMaterialSlot ) );
+				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'clearcoatRoughness', materialClearcoatRoughness.getValue(), currentMaterialSlot ) );
 
 			}
 
@@ -817,34 +817,34 @@ Sidebar.Material = function ( editor ) {
 
 			}
 
-			if ( material.clearCoatNormalMap !== undefined ) {
+			if ( material.clearcoatNormalMap !== undefined ) {
 
-				var clearCoatNormalMapEnabled = materialClearCoatNormalMapEnabled.getValue() === true;
+				var clearcoatNormalMapEnabled = materialClearcoatNormalMapEnabled.getValue() === true;
 
 				if ( objectHasUvs ) {
 
-					var clearCoatNormalMap = clearCoatNormalMapEnabled ? materialClearCoatNormalMap.getValue() : null;
+					var clearcoatNormalMap = clearcoatNormalMapEnabled ? materialClearcoatNormalMap.getValue() : null;
 
-					if ( material.clearCoatNormalMap !== clearCoatNormalMap ) {
+					if ( material.clearcoatNormalMap !== clearcoatNormalMap ) {
 
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'clearCoatNormalMap', clearCoatNormalMap, currentMaterialSlot ) );
+						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'clearcoatNormalMap', clearcoatNormalMap, currentMaterialSlot ) );
 
 					}
 
-					if ( material.clearCoatNormalScale.x !== materialClearCoatNormalScaleX.getValue() ||
-						material.clearCoatNormalScale.y !== materialClearCoatNormalScaleY.getValue() ) {
+					if ( material.clearcoatNormalScale.x !== materialClearcoatNormalScaleX.getValue() ||
+						material.clearcoatNormalScale.y !== materialClearcoatNormalScaleY.getValue() ) {
 
 						var value = [
-							materialClearCoatNormalScaleX.getValue(),
-							materialClearCoatNormalScaleY.getValue()
+							materialClearcoatNormalScaleX.getValue(),
+							materialClearcoatNormalScaleY.getValue()
 						];
-						editor.execute( new SetMaterialVectorCommand( editor, currentObject, 'clearCoatNormalScale', value, currentMaterialSlot ) );
+						editor.execute( new SetMaterialVectorCommand( editor, currentObject, 'clearcoatNormalScale', value, currentMaterialSlot ) );
 
 					}
 
 				} else {
 
-					if ( clearCoatNormalMapEnabled ) textureWarning = true;
+					if ( clearcoatNormalMapEnabled ) textureWarning = true;
 
 				}
 
@@ -1159,8 +1159,8 @@ Sidebar.Material = function ( editor ) {
 			'emissive': materialEmissiveRow,
 			'specular': materialSpecularRow,
 			'shininess': materialShininessRow,
-			'clearCoat': materialClearCoatRow,
-			'clearCoatRoughness': materialClearCoatRoughnessRow,
+			'clearcoat': materialClearcoatRow,
+			'clearcoatRoughness': materialClearcoatRoughnessRow,
 			'vertexShader': materialProgramRow,
 			'vertexColors': materialVertexColorsRow,
 			'depthPacking': materialDepthPackingRow,
@@ -1287,15 +1287,15 @@ Sidebar.Material = function ( editor ) {
 
 		}
 
-		if ( material.clearCoat !== undefined ) {
+		if ( material.clearcoat !== undefined ) {
 
-			materialClearCoat.setValue( material.clearCoat );
+			materialClearcoat.setValue( material.clearcoat );
 
 		}
 
-		if ( material.clearCoatRoughness !== undefined ) {
+		if ( material.clearcoatRoughness !== undefined ) {
 
-			materialClearCoatRoughness.setValue( material.clearCoatRoughness );
+			materialClearcoatRoughness.setValue( material.clearcoatRoughness );
 
 		}
 
@@ -1382,18 +1382,18 @@ Sidebar.Material = function ( editor ) {
 
 		}
 
-		if ( material.clearCoatNormalMap !== undefined ) {
+		if ( material.clearcoatNormalMap !== undefined ) {
 
-			materialClearCoatNormalMapEnabled.setValue( material.clearCoatNormalMap !== null );
+			materialClearcoatNormalMapEnabled.setValue( material.clearcoatNormalMap !== null );
 
-			if ( material.clearCoatNormalMap !== null || resetTextureSelectors ) {
+			if ( material.clearcoatNormalMap !== null || resetTextureSelectors ) {
 
-				materialClearCoatNormalMap.setValue( material.clearCoatNormalMap );
+				materialClearcoatNormalMap.setValue( material.clearcoatNormalMap );
 
 			}
 
-			materialClearCoatNormalScaleX.setValue( material.clearCoatNormalScale.x );
-			materialClearCoatNormalScaleY.setValue( material.clearCoatNormalScale.y );
+			materialClearcoatNormalScaleX.setValue( material.clearcoatNormalScale.x );
+			materialClearcoatNormalScaleY.setValue( material.clearcoatNormalScale.y );
 
 		}
 

+ 4 - 4
editor/js/Strings.js

@@ -224,8 +224,8 @@ var Strings = function ( config ) {
 			'sidebar/material/emissive': 'Emissive',
 			'sidebar/material/specular': 'Specular',
 			'sidebar/material/shininess': 'Shininess',
-			'sidebar/material/clearcoat': 'ClearCoat',
-			'sidebar/material/clearcoatroughness': 'ClearCoat Roughness',
+			'sidebar/material/clearcoat': 'Clearcoat',
+			'sidebar/material/clearcoatroughness': 'Clearcoat Roughness',
 			'sidebar/material/vertexcolors': 'Vertex Colors',
 			'sidebar/material/vertexcolors/no': 'No',
 			'sidebar/material/vertexcolors/face': 'Face',
@@ -236,7 +236,7 @@ var Strings = function ( config ) {
 			'sidebar/material/alphamap': 'Alpha Map',
 			'sidebar/material/bumpmap': 'Bump Map',
 			'sidebar/material/normalmap': 'Normal Map',
-			'sidebar/material/clearcoatnormalmap': 'ClearCoat Normal Map',
+			'sidebar/material/clearcoatnormalmap': 'Clearcoat Normal Map',
 			'sidebar/material/displacemap': 'Displace Map',
 			'sidebar/material/roughmap': 'Rough. Map',
 			'sidebar/material/metalmap': 'Metal. Map',
@@ -508,7 +508,7 @@ var Strings = function ( config ) {
 			'sidebar/material/alphamap': '透明贴图',
 			'sidebar/material/bumpmap': '凹凸贴图',
 			'sidebar/material/normalmap': '法线贴图',
-			'sidebar/material/clearcoatnormalmap': 'ClearCoat Normal Map',
+			'sidebar/material/clearcoatnormalmap': 'Clearcoat Normal Map',
 			'sidebar/material/displacemap': '置换贴图',
 			'sidebar/material/roughmap': '粗糙贴图',
 			'sidebar/material/metalmap': '金属贴图',

+ 1 - 0
examples/files.js

@@ -166,6 +166,7 @@ var files = {
 		"webgl_materials_parallaxmap",
 		"webgl_materials_reflectivity",
 		"webgl_materials_shaders_fresnel",
+		"webgl_materials_sheen",
 		"webgl_materials_skin",
 		"webgl_materials_standard",
 		"webgl_materials_texture_anisotropy",

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

@@ -265,7 +265,7 @@ THREE.OrthographicTrackballControls = function ( object, domElement ) {
 
 			mouseChange.copy( _panEnd ).sub( _panStart );
 
-			if ( mouseChange.lengthSq() ) {
+			if ( mouseChange.lengthSq() > EPS ) {
 
 				// Scale movement to keep clicked/dragged position under cursor
 				var scale_x = ( _this.object.right - _this.object.left ) / _this.object.zoom;

+ 8 - 3
examples/js/exporters/ColladaExporter.js

@@ -220,7 +220,9 @@ THREE.ColladaExporter.prototype = {
 						bufferGeometry.groups :
 						[ { start: 0, count: indexCount, materialIndex: 0 } ];
 
-				var gnode = `<geometry id="${ meshid }" name="${ g.name }"><mesh>`;
+
+				var gname = g.name ? ` name="${ g.name }"` : '';
+				var gnode = `<geometry id="${ meshid }"${ gname }><mesh>`;
 
 				// define the geometry node and the vertices for the geometry
 				var posName = `${ meshid }-position`;
@@ -485,7 +487,7 @@ THREE.ColladaExporter.prototype = {
 
 					(
 						m.side === THREE.DoubleSide ?
-							`<extra><technique><double_sided sid="double_sided" type="int">1</double_sided></technique></extra>` :
+							`<extra><technique profile="THREEJS"><double_sided sid="double_sided" type="int">1</double_sided></technique></extra>` :
 							''
 					) +
 
@@ -493,7 +495,10 @@ THREE.ColladaExporter.prototype = {
 
 					'</effect>';
 
-				libraryMaterials.push( `<material id="${ matid }" name="${ m.name }"><instance_effect url="#${ matid }-effect" /></material>` );
+				var materialName = m.name ? ` name="${ m.name }"` : '';
+				var materialNode = `<material id="${ matid }"${ materialName }><instance_effect url="#${ matid }-effect" /></material>`;
+
+				libraryMaterials.push( materialNode );
 				libraryEffects.push( effectnode );
 				materialMap.set( m, matid );
 

+ 42 - 3
examples/js/exporters/GLTFExporter.js

@@ -895,7 +895,7 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
-			if ( material.isShaderMaterial ) {
+			if ( material.isShaderMaterial && !material.isGLTFSpecularGlossinessMaterial ) {
 
 				console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' );
 				return null;
@@ -915,6 +915,12 @@ THREE.GLTFExporter.prototype = {
 
 				extensionsUsed[ 'KHR_materials_unlit' ] = true;
 
+			} else if ( material.isGLTFSpecularGlossinessMaterial ) {
+
+				gltfMaterial.extensions = { KHR_materials_pbrSpecularGlossiness: {} };
+
+				extensionsUsed[ 'KHR_materials_pbrSpecularGlossiness' ] = true;
+
 			} else if ( ! material.isMeshStandardMaterial ) {
 
 				console.warn( 'GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.' );
@@ -947,6 +953,23 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
+			// pbrSpecularGlossiness diffuse, specular and glossiness factor
+			if ( material.isGLTFSpecularGlossinessMaterial ) {
+				
+				if ( gltfMaterial.pbrMetallicRoughness.baseColorFactor ) {
+
+					gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.diffuseFactor = gltfMaterial.pbrMetallicRoughness.baseColorFactor;
+				  
+				}
+
+				var specularFactor = [ 1, 1, 1 ];
+				material.specular.toArray( specularFactor, 0 );
+				gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.specularFactor = specularFactor;
+
+				gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.glossinessFactor = material.glossiness;
+			
+			}
+
 			// pbrMetallicRoughness.metallicRoughnessTexture
 			if ( material.metalnessMap || material.roughnessMap ) {
 
@@ -964,12 +987,28 @@ THREE.GLTFExporter.prototype = {
 
 			}
 
-			// pbrMetallicRoughness.baseColorTexture
+			// pbrMetallicRoughness.baseColorTexture or pbrSpecularGlossiness diffuseTexture
 			if ( material.map ) {
 
 				var baseColorMapDef = { index: processTexture( material.map ) };
 				applyTextureTransform( baseColorMapDef, material.map );
+
+				if ( material.isGLTFSpecularGlossinessMaterial ) {
+
+					gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.diffuseTexture = baseColorMapDef;
+
+				}
+
 				gltfMaterial.pbrMetallicRoughness.baseColorTexture = baseColorMapDef;
+				
+			}
+
+			// pbrSpecularGlossiness specular map
+			if ( material.isGLTFSpecularGlossinessMaterial && material.specularMap ) {
+
+				var specularMapDef = { index: processTexture( material.specularMap ) };
+				applyTextureTransform( specularMapDef, material.specularMap );
+				gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.specularGlossinessTexture = specularMapDef;
 
 			}
 
@@ -1004,7 +1043,7 @@ THREE.GLTFExporter.prototype = {
 
 				var normalMapDef = { index: processTexture( material.normalMap ) };
 
-				if ( material.normalScale.x !== - 1 ) {
+				if ( material.normalScale && material.normalScale.x !== - 1 ) {
 
 					if ( material.normalScale.x !== material.normalScale.y ) {
 

+ 2 - 2
examples/js/loaders/LWOLoader.js

@@ -2593,11 +2593,11 @@ MaterialParser.prototype = {
 
 		if ( attributes.Clearcoat && attributes.Clearcoat.value > 0 ) {
 
-			params.clearCoat = attributes.Clearcoat.value;
+			params.clearcoat = attributes.Clearcoat.value;
 
 			if ( attributes[ 'Clearcoat Gloss' ] ) {
 
-				params.clearCoatRoughness = 0.5 * ( 1 - attributes[ 'Clearcoat Gloss' ].value );
+				params.clearcoatRoughness = 0.5 * ( 1 - attributes[ 'Clearcoat Gloss' ].value );
 
 			}
 

+ 79 - 25
examples/js/loaders/STLLoader.js

@@ -28,6 +28,31 @@
  *    material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors });
  *  } else { .... }
  *  var mesh = new THREE.Mesh( geometry, material );
+ *
+ * For ASCII STLs containing multiple solids, each solid is assigned to a different group.
+ * Groups can be used to assign a different color by defining an array of materials with the same length of
+ * geometry.groups and passing it to the Mesh constructor:
+ *
+ * var mesh = new THREE.Mesh( geometry, material );
+ *
+ * For example:
+ *
+ *  var materials = [];
+ *  var nGeometryGroups = geometry.groups.length;
+ *
+ *  var colorMap = ...; // Some logic to index colors.
+ *
+ *  for (var i = 0; i < nGeometryGroups; i++) {
+ *
+ *		var material = new THREE.MeshPhongMaterial({
+ *			color: colorMap[i],
+ *			wireframe: false
+ *		});
+ *
+ *  }
+ *
+ *  materials.push(material);
+ *  var mesh = new THREE.Mesh(geometry, materials);
  */
 
 
@@ -107,7 +132,7 @@ THREE.STLLoader.prototype = {
 
 				// If "solid" text is matched to the current offset, declare it to be an ASCII STL.
 
-				if ( matchDataViewAt ( solid, reader, off ) ) return false;
+				if ( matchDataViewAt( solid, reader, off ) ) return false;
 
 			}
 
@@ -201,7 +226,7 @@ THREE.STLLoader.prototype = {
 
 					var vertexstart = start + i * 12;
 					var componentIdx = ( face * 3 * 3 ) + ( ( i - 1 ) * 3 );
-					
+
 					vertices[ componentIdx ] = reader.getFloat32( vertexstart, true );
 					vertices[ componentIdx + 1 ] = reader.getFloat32( vertexstart + 4, true );
 					vertices[ componentIdx + 2 ] = reader.getFloat32( vertexstart + 8, true );
@@ -240,6 +265,7 @@ THREE.STLLoader.prototype = {
 		function parseASCII( data ) {
 
 			var geometry = new THREE.BufferGeometry();
+			var patternSolid = /solid([\s\S]*?)endsolid/g;
 			var patternFace = /facet([\s\S]*?)endfacet/g;
 			var faceCounter = 0;
 
@@ -254,53 +280,80 @@ THREE.STLLoader.prototype = {
 
 			var result;
 
-			while ( ( result = patternFace.exec( data ) ) !== null ) {
+			var groupVertexes = [];
+			var groupCount = 0;
+			var startVertex = 0;
+			var endVertex = 0;
 
-				var vertexCountPerFace = 0;
-				var normalCountPerFace = 0;
+			while ( ( result = patternSolid.exec( data ) ) !== null ) {
 
-				var text = result[ 0 ];
+				startVertex = endVertex;
 
-				while ( ( result = patternNormal.exec( text ) ) !== null ) {
+				var solid = result[ 0 ];
 
-					normal.x = parseFloat( result[ 1 ] );
-					normal.y = parseFloat( result[ 2 ] );
-					normal.z = parseFloat( result[ 3 ] );
-					normalCountPerFace ++;
+				while ( ( result = patternFace.exec( solid ) ) !== null ) {
 
-				}
+					var vertexCountPerFace = 0;
+					var normalCountPerFace = 0;
 
-				while ( ( result = patternVertex.exec( text ) ) !== null ) {
+					var text = result[ 0 ];
 
-					vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
-					normals.push( normal.x, normal.y, normal.z );
-					vertexCountPerFace ++;
+					while ( ( result = patternNormal.exec( text ) ) !== null ) {
 
-				}
+						normal.x = parseFloat( result[ 1 ] );
+						normal.y = parseFloat( result[ 2 ] );
+						normal.z = parseFloat( result[ 3 ] );
+						normalCountPerFace ++;
 
-				// every face have to own ONE valid normal
+					}
 
-				if ( normalCountPerFace !== 1 ) {
+					while ( ( result = patternVertex.exec( text ) ) !== null ) {
 
-					console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
+						vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
+						normals.push( normal.x, normal.y, normal.z );
+						vertexCountPerFace ++;
+						endVertex ++;
 
-				}
+					}
 
-				// each face have to own THREE valid vertices
+					// every face have to own ONE valid normal
 
-				if ( vertexCountPerFace !== 3 ) {
+					if ( normalCountPerFace !== 1 ) {
 
-					console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
+						console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
+
+					}
+
+					// each face have to own THREE valid vertices
+
+					if ( vertexCountPerFace !== 3 ) {
+
+						console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
+
+					}
+
+					faceCounter ++;
 
 				}
 
-				faceCounter ++;
+				groupVertexes.push( { startVertex: startVertex, endVertex: endVertex } );
+				groupCount ++;
 
 			}
 
 			geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
 			geometry.addAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
 
+			if ( groupCount > 0 ) {
+
+				for ( var i = 0; i < groupVertexes.length; i ++ ) {
+
+					geometry.addGroup( groupVertexes[ i ].startVertex, groupVertexes[ i ].endVertex, i );
+
+				}
+
+			}
+
 			return geometry;
 
 		}
@@ -327,6 +380,7 @@ THREE.STLLoader.prototype = {
 					array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian
 
 				}
+
 				return array_buffer.buffer || array_buffer;
 
 			} else {

+ 265 - 1
examples/js/loaders/deprecated/LegacyJSONLoader.js

@@ -85,6 +85,270 @@ THREE.LegacyJSONLoader = ( function () {
 
 		parse: ( function () {
 
+			var _BlendingMode = {
+				NoBlending: THREE.NoBlending,
+				NormalBlending: THREE.NormalBlending,
+				AdditiveBlending: THREE.AdditiveBlending,
+				SubtractiveBlending: THREE.SubtractiveBlending,
+				MultiplyBlending: THREE.MultiplyBlending,
+				CustomBlending: THREE.CustomBlending
+			};
+
+			var _color = new THREE.Color();
+			var _textureLoader = new THREE.TextureLoader();
+			var _materialLoader = new THREE.MaterialLoader();
+
+			function initMaterials( materials, texturePath, crossOrigin ) {
+
+				var array = [];
+
+				for ( var i = 0; i < materials.length; ++ i ) {
+
+					array[ i ] = createMaterial( materials[ i ], texturePath, crossOrigin );
+
+				}
+
+				return array;
+
+			}
+
+			function createMaterial( m, texturePath, crossOrigin ) {
+
+				// convert from old material format
+
+				var textures = {};
+
+				//
+
+				var json = {
+					uuid: THREE.Math.generateUUID(),
+					type: 'MeshLambertMaterial'
+				};
+
+				for ( var name in m ) {
+
+					var value = m[ name ];
+
+					switch ( name ) {
+
+						case 'DbgColor':
+						case 'DbgIndex':
+						case 'opticalDensity':
+						case 'illumination':
+							break;
+						case 'DbgName':
+							json.name = value;
+							break;
+						case 'blending':
+							json.blending = _BlendingMode[ value ];
+							break;
+						case 'colorAmbient':
+						case 'mapAmbient':
+							console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' );
+							break;
+						case 'colorDiffuse':
+							json.color = _color.fromArray( value ).getHex();
+							break;
+						case 'colorSpecular':
+							json.specular = _color.fromArray( value ).getHex();
+							break;
+						case 'colorEmissive':
+							json.emissive = _color.fromArray( value ).getHex();
+							break;
+						case 'specularCoef':
+							json.shininess = value;
+							break;
+						case 'shading':
+							if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';
+							if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';
+							if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial';
+							break;
+						case 'mapDiffuse':
+							json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapDiffuseRepeat':
+						case 'mapDiffuseOffset':
+						case 'mapDiffuseWrap':
+						case 'mapDiffuseAnisotropy':
+							break;
+						case 'mapEmissive':
+							json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapEmissiveRepeat':
+						case 'mapEmissiveOffset':
+						case 'mapEmissiveWrap':
+						case 'mapEmissiveAnisotropy':
+							break;
+						case 'mapLight':
+							json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapLightRepeat':
+						case 'mapLightOffset':
+						case 'mapLightWrap':
+						case 'mapLightAnisotropy':
+							break;
+						case 'mapAO':
+							json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapAORepeat':
+						case 'mapAOOffset':
+						case 'mapAOWrap':
+						case 'mapAOAnisotropy':
+							break;
+						case 'mapBump':
+							json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapBumpScale':
+							json.bumpScale = value;
+							break;
+						case 'mapBumpRepeat':
+						case 'mapBumpOffset':
+						case 'mapBumpWrap':
+						case 'mapBumpAnisotropy':
+							break;
+						case 'mapNormal':
+							json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapNormalFactor':
+							json.normalScale = value;
+							break;
+						case 'mapNormalRepeat':
+						case 'mapNormalOffset':
+						case 'mapNormalWrap':
+						case 'mapNormalAnisotropy':
+							break;
+						case 'mapSpecular':
+							json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapSpecularRepeat':
+						case 'mapSpecularOffset':
+						case 'mapSpecularWrap':
+						case 'mapSpecularAnisotropy':
+							break;
+						case 'mapMetalness':
+							json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapMetalnessRepeat':
+						case 'mapMetalnessOffset':
+						case 'mapMetalnessWrap':
+						case 'mapMetalnessAnisotropy':
+							break;
+						case 'mapRoughness':
+							json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapRoughnessRepeat':
+						case 'mapRoughnessOffset':
+						case 'mapRoughnessWrap':
+						case 'mapRoughnessAnisotropy':
+							break;
+						case 'mapAlpha':
+							json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapAlphaRepeat':
+						case 'mapAlphaOffset':
+						case 'mapAlphaWrap':
+						case 'mapAlphaAnisotropy':
+							break;
+						case 'flipSided':
+							json.side = THREE.BackSide;
+							break;
+						case 'doubleSided':
+							json.side = THREE.DoubleSide;
+							break;
+						case 'transparency':
+							console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );
+							json.opacity = value;
+							break;
+						case 'depthTest':
+						case 'depthWrite':
+						case 'colorWrite':
+						case 'opacity':
+						case 'reflectivity':
+						case 'transparent':
+						case 'visible':
+						case 'wireframe':
+							json[ name ] = value;
+							break;
+						case 'vertexColors':
+							if ( value === true ) json.vertexColors = THREE.VertexColors;
+							if ( value === 'face' ) json.vertexColors = THREE.FaceColors;
+							break;
+						default:
+							console.error( 'THREE.Loader.createMaterial: Unsupported', name, value );
+							break;
+
+					}
+
+				}
+
+				if ( json.type === 'MeshBasicMaterial' ) delete json.emissive;
+				if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;
+
+				if ( json.opacity < 1 ) json.transparent = true;
+
+				_materialLoader.setTextures( textures );
+
+				return _materialLoader.parse( json );
+
+			}
+
+			function loadTexture( path, repeat, offset, wrap, anisotropy, textures, texturePath, crossOrigin ) {
+
+				var fullPath = texturePath + path;
+				var loader = THREE.Loader.Handlers.get( fullPath );
+
+				var texture;
+
+				if ( loader !== null ) {
+
+					texture = loader.load( fullPath );
+
+				} else {
+
+					_textureLoader.setCrossOrigin( crossOrigin );
+					texture = _textureLoader.load( fullPath );
+
+				}
+
+				if ( repeat !== undefined ) {
+
+					texture.repeat.fromArray( repeat );
+
+					if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping;
+					if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping;
+
+				}
+
+				if ( offset !== undefined ) {
+
+					texture.offset.fromArray( offset );
+
+				}
+
+				if ( wrap !== undefined ) {
+
+					if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping;
+					if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping;
+
+					if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping;
+					if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping;
+
+				}
+
+				if ( anisotropy !== undefined ) {
+
+					texture.anisotropy = anisotropy;
+
+				}
+
+				var uuid = THREE.Math.generateUUID();
+
+				textures[ uuid ] = texture;
+
+				return uuid;
+
+			}
+
 			function parseModel( json, geometry ) {
 
 				function isBitSet( value, position ) {
@@ -561,7 +825,7 @@ THREE.LegacyJSONLoader = ( function () {
 
 				} else {
 
-					var materials = THREE.Loader.prototype.initMaterials( json.materials, this.resourcePath || path, this.crossOrigin );
+					var materials = initMaterials( json.materials, this.resourcePath || path, this.crossOrigin );
 
 					return { geometry: geometry, materials: materials };
 

+ 1 - 1
examples/js/shaders/TranslucentShader.js

@@ -187,7 +187,7 @@ THREE.TranslucentShader = {
 		"	#if defined( RE_IndirectSpecular )",
 
 		"		vec3 radiance = vec3( 0.0 );",
-		"		vec3 clearCoatRadiance = vec3( 0.0 );",
+		"		vec3 clearcoatRadiance = vec3( 0.0 );",
 
 		"	#endif",
 		THREE.ShaderChunk[ "lights_fragment_end" ],

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

@@ -272,7 +272,7 @@ var OrthographicTrackballControls = function ( object, domElement ) {
 
 			mouseChange.copy( _panEnd ).sub( _panStart );
 
-			if ( mouseChange.lengthSq() ) {
+			if ( mouseChange.lengthSq() > EPS ) {
 
 				// Scale movement to keep clicked/dragged position under cursor
 				var scale_x = ( _this.object.right - _this.object.left ) / _this.object.zoom;

+ 8 - 3
examples/jsm/exporters/ColladaExporter.js

@@ -231,7 +231,9 @@ ColladaExporter.prototype = {
 						bufferGeometry.groups :
 						[ { start: 0, count: indexCount, materialIndex: 0 } ];
 
-				var gnode = `<geometry id="${ meshid }" name="${ g.name }"><mesh>`;
+
+				var gname = g.name ? ` name="${ g.name }"` : '';
+				var gnode = `<geometry id="${ meshid }"${ gname }><mesh>`;
 
 				// define the geometry node and the vertices for the geometry
 				var posName = `${ meshid }-position`;
@@ -496,7 +498,7 @@ ColladaExporter.prototype = {
 
 					(
 						m.side === DoubleSide ?
-							`<extra><technique><double_sided sid="double_sided" type="int">1</double_sided></technique></extra>` :
+							`<extra><technique profile="THREEJS"><double_sided sid="double_sided" type="int">1</double_sided></technique></extra>` :
 							''
 					) +
 
@@ -504,7 +506,10 @@ ColladaExporter.prototype = {
 
 					'</effect>';
 
-				libraryMaterials.push( `<material id="${ matid }" name="${ m.name }"><instance_effect url="#${ matid }-effect" /></material>` );
+				var materialName = m.name ? ` name="${ m.name }"` : '';
+				var materialNode = `<material id="${ matid }"${ materialName }><instance_effect url="#${ matid }-effect" /></material>`;
+
+				libraryMaterials.push( materialNode );
 				libraryEffects.push( effectnode );
 				materialMap.set( m, matid );
 

+ 42 - 3
examples/jsm/exporters/GLTFExporter.js

@@ -919,7 +919,7 @@ GLTFExporter.prototype = {
 
 			}
 
-			if ( material.isShaderMaterial ) {
+			if ( material.isShaderMaterial && !material.isGLTFSpecularGlossinessMaterial ) {
 
 				console.warn( 'GLTFExporter: THREE.ShaderMaterial not supported.' );
 				return null;
@@ -939,6 +939,12 @@ GLTFExporter.prototype = {
 
 				extensionsUsed[ 'KHR_materials_unlit' ] = true;
 
+			} else if ( material.isGLTFSpecularGlossinessMaterial ) {
+
+				gltfMaterial.extensions = { KHR_materials_pbrSpecularGlossiness: {} };
+
+				extensionsUsed[ 'KHR_materials_pbrSpecularGlossiness' ] = true;
+
 			} else if ( ! material.isMeshStandardMaterial ) {
 
 				console.warn( 'GLTFExporter: Use MeshStandardMaterial or MeshBasicMaterial for best results.' );
@@ -971,6 +977,23 @@ GLTFExporter.prototype = {
 
 			}
 
+			// pbrSpecularGlossiness diffuse, specular and glossiness factor
+			if ( material.isGLTFSpecularGlossinessMaterial ) {
+				
+				if ( gltfMaterial.pbrMetallicRoughness.baseColorFactor ) {
+
+					gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.diffuseFactor = gltfMaterial.pbrMetallicRoughness.baseColorFactor;
+				  
+				}
+
+				var specularFactor = [ 1, 1, 1 ];
+				material.specular.toArray( specularFactor, 0 );
+				gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.specularFactor = specularFactor;
+
+				gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.glossinessFactor = material.glossiness;
+			
+			}
+
 			// pbrMetallicRoughness.metallicRoughnessTexture
 			if ( material.metalnessMap || material.roughnessMap ) {
 
@@ -988,12 +1011,28 @@ GLTFExporter.prototype = {
 
 			}
 
-			// pbrMetallicRoughness.baseColorTexture
+			// pbrMetallicRoughness.baseColorTexture or pbrSpecularGlossiness diffuseTexture
 			if ( material.map ) {
 
 				var baseColorMapDef = { index: processTexture( material.map ) };
 				applyTextureTransform( baseColorMapDef, material.map );
+
+				if ( material.isGLTFSpecularGlossinessMaterial ) {
+
+					gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.diffuseTexture = baseColorMapDef;
+
+				}
+
 				gltfMaterial.pbrMetallicRoughness.baseColorTexture = baseColorMapDef;
+				
+			}
+
+			// pbrSpecularGlossiness specular map
+			if ( material.isGLTFSpecularGlossinessMaterial && material.specularMap ) {
+
+				var specularMapDef = { index: processTexture( material.specularMap ) };
+				applyTextureTransform( specularMapDef, material.specularMap );
+				gltfMaterial.extensions.KHR_materials_pbrSpecularGlossiness.specularGlossinessTexture = specularMapDef;
 
 			}
 
@@ -1028,7 +1067,7 @@ GLTFExporter.prototype = {
 
 				var normalMapDef = { index: processTexture( material.normalMap ) };
 
-				if ( material.normalScale.x !== - 1 ) {
+				if ( material.normalScale && material.normalScale.x !== - 1 ) {
 
 					if ( material.normalScale.x !== material.normalScale.y ) {
 

+ 2 - 2
examples/jsm/loaders/LWOLoader.js

@@ -2622,11 +2622,11 @@ MaterialParser.prototype = {
 
 		if ( attributes.Clearcoat && attributes.Clearcoat.value > 0 ) {
 
-			params.clearCoat = attributes.Clearcoat.value;
+			params.clearcoat = attributes.Clearcoat.value;
 
 			if ( attributes[ 'Clearcoat Gloss' ] ) {
 
-				params.clearCoatRoughness = 0.5 * ( 1 - attributes[ 'Clearcoat Gloss' ].value );
+				params.clearcoatRoughness = 0.5 * ( 1 - attributes[ 'Clearcoat Gloss' ].value );
 
 			}
 

+ 79 - 25
examples/jsm/loaders/STLLoader.js

@@ -28,6 +28,31 @@
  *    material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors });
  *  } else { .... }
  *  var mesh = new THREE.Mesh( geometry, material );
+ *
+ * For ASCII STLs containing multiple solids, each solid is assigned to a different group.
+ * Groups can be used to assign a different color by defining an array of materials with the same length of
+ * geometry.groups and passing it to the Mesh constructor:
+ *
+ * var mesh = new THREE.Mesh( geometry, material );
+ *
+ * For example:
+ *
+ *  var materials = [];
+ *  var nGeometryGroups = geometry.groups.length;
+ *
+ *  var colorMap = ...; // Some logic to index colors.
+ *
+ *  for (var i = 0; i < nGeometryGroups; i++) {
+ *
+ *		var material = new THREE.MeshPhongMaterial({
+ *			color: colorMap[i],
+ *			wireframe: false
+ *		});
+ *
+ *  }
+ *
+ *  materials.push(material);
+ *  var mesh = new THREE.Mesh(geometry, materials);
  */
 
 import {
@@ -117,7 +142,7 @@ STLLoader.prototype = {
 
 				// If "solid" text is matched to the current offset, declare it to be an ASCII STL.
 
-				if ( matchDataViewAt ( solid, reader, off ) ) return false;
+				if ( matchDataViewAt( solid, reader, off ) ) return false;
 
 			}
 
@@ -211,7 +236,7 @@ STLLoader.prototype = {
 
 					var vertexstart = start + i * 12;
 					var componentIdx = ( face * 3 * 3 ) + ( ( i - 1 ) * 3 );
-					
+
 					vertices[ componentIdx ] = reader.getFloat32( vertexstart, true );
 					vertices[ componentIdx + 1 ] = reader.getFloat32( vertexstart + 4, true );
 					vertices[ componentIdx + 2 ] = reader.getFloat32( vertexstart + 8, true );
@@ -250,6 +275,7 @@ STLLoader.prototype = {
 		function parseASCII( data ) {
 
 			var geometry = new BufferGeometry();
+			var patternSolid = /solid([\s\S]*?)endsolid/g;
 			var patternFace = /facet([\s\S]*?)endfacet/g;
 			var faceCounter = 0;
 
@@ -264,53 +290,80 @@ STLLoader.prototype = {
 
 			var result;
 
-			while ( ( result = patternFace.exec( data ) ) !== null ) {
+			var groupVertexes = [];
+			var groupCount = 0;
+			var startVertex = 0;
+			var endVertex = 0;
 
-				var vertexCountPerFace = 0;
-				var normalCountPerFace = 0;
+			while ( ( result = patternSolid.exec( data ) ) !== null ) {
 
-				var text = result[ 0 ];
+				startVertex = endVertex;
 
-				while ( ( result = patternNormal.exec( text ) ) !== null ) {
+				var solid = result[ 0 ];
 
-					normal.x = parseFloat( result[ 1 ] );
-					normal.y = parseFloat( result[ 2 ] );
-					normal.z = parseFloat( result[ 3 ] );
-					normalCountPerFace ++;
+				while ( ( result = patternFace.exec( solid ) ) !== null ) {
 
-				}
+					var vertexCountPerFace = 0;
+					var normalCountPerFace = 0;
 
-				while ( ( result = patternVertex.exec( text ) ) !== null ) {
+					var text = result[ 0 ];
 
-					vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
-					normals.push( normal.x, normal.y, normal.z );
-					vertexCountPerFace ++;
+					while ( ( result = patternNormal.exec( text ) ) !== null ) {
 
-				}
+						normal.x = parseFloat( result[ 1 ] );
+						normal.y = parseFloat( result[ 2 ] );
+						normal.z = parseFloat( result[ 3 ] );
+						normalCountPerFace ++;
 
-				// every face have to own ONE valid normal
+					}
 
-				if ( normalCountPerFace !== 1 ) {
+					while ( ( result = patternVertex.exec( text ) ) !== null ) {
 
-					console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
+						vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
+						normals.push( normal.x, normal.y, normal.z );
+						vertexCountPerFace ++;
+						endVertex ++;
 
-				}
+					}
 
-				// each face have to own THREE valid vertices
+					// every face have to own ONE valid normal
 
-				if ( vertexCountPerFace !== 3 ) {
+					if ( normalCountPerFace !== 1 ) {
 
-					console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
+						console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
+
+					}
+
+					// each face have to own THREE valid vertices
+
+					if ( vertexCountPerFace !== 3 ) {
+
+						console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
+
+					}
+
+					faceCounter ++;
 
 				}
 
-				faceCounter ++;
+				groupVertexes.push( { startVertex: startVertex, endVertex: endVertex } );
+				groupCount ++;
 
 			}
 
 			geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
 			geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
 
+			if ( groupCount > 0 ) {
+
+				for ( var i = 0; i < groupVertexes.length; i ++ ) {
+
+					geometry.addGroup( groupVertexes[ i ].startVertex, groupVertexes[ i ].endVertex, i );
+
+				}
+
+			}
+
 			return geometry;
 
 		}
@@ -337,6 +390,7 @@ STLLoader.prototype = {
 					array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian
 
 				}
+
 				return array_buffer.buffer || array_buffer;
 
 			} else {

+ 281 - 2
examples/jsm/loaders/deprecated/LegacyJSONLoader.js

@@ -4,17 +4,32 @@
  */
 
 import {
+	AdditiveBlending,
 	AnimationClip,
+	BackSide,
 	Color,
+	CustomBlending,
 	DefaultLoadingManager,
+	DoubleSide,
 	Face3,
+	FaceColors,
 	FileLoader,
 	Geometry,
 	Loader,
 	LoaderUtils,
+	MaterialLoader,
+	Math as _Math,
+	MirroredRepeatWrapping,
+	MultiplyBlending,
+	NoBlending,
+	NormalBlending,
+	RepeatWrapping,
+	SubtractiveBlending,
+	TextureLoader,
 	Vector2,
 	Vector3,
-	Vector4
+	Vector4,
+	VertexColors
 } from "../../../../build/three.module.js";
 
 var LegacyJSONLoader = ( function () {
@@ -99,6 +114,270 @@ var LegacyJSONLoader = ( function () {
 
 		parse: ( function () {
 
+			var _BlendingMode = {
+				NoBlending: NoBlending,
+				NormalBlending: NormalBlending,
+				AdditiveBlending: AdditiveBlending,
+				SubtractiveBlending: SubtractiveBlending,
+				MultiplyBlending: MultiplyBlending,
+				CustomBlending: CustomBlending
+			};
+
+			var _color = new Color();
+			var _textureLoader = new TextureLoader();
+			var _materialLoader = new MaterialLoader();
+
+			function initMaterials( materials, texturePath, crossOrigin ) {
+
+				var array = [];
+
+				for ( var i = 0; i < materials.length; ++ i ) {
+
+					array[ i ] = createMaterial( materials[ i ], texturePath, crossOrigin );
+
+				}
+
+				return array;
+
+			}
+
+			function createMaterial( m, texturePath, crossOrigin ) {
+
+				// convert from old material format
+
+				var textures = {};
+
+				//
+
+				var json = {
+					uuid: _Math.generateUUID(),
+					type: 'MeshLambertMaterial'
+				};
+
+				for ( var name in m ) {
+
+					var value = m[ name ];
+
+					switch ( name ) {
+
+						case 'DbgColor':
+						case 'DbgIndex':
+						case 'opticalDensity':
+						case 'illumination':
+							break;
+						case 'DbgName':
+							json.name = value;
+							break;
+						case 'blending':
+							json.blending = _BlendingMode[ value ];
+							break;
+						case 'colorAmbient':
+						case 'mapAmbient':
+							console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' );
+							break;
+						case 'colorDiffuse':
+							json.color = _color.fromArray( value ).getHex();
+							break;
+						case 'colorSpecular':
+							json.specular = _color.fromArray( value ).getHex();
+							break;
+						case 'colorEmissive':
+							json.emissive = _color.fromArray( value ).getHex();
+							break;
+						case 'specularCoef':
+							json.shininess = value;
+							break;
+						case 'shading':
+							if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';
+							if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';
+							if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial';
+							break;
+						case 'mapDiffuse':
+							json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapDiffuseRepeat':
+						case 'mapDiffuseOffset':
+						case 'mapDiffuseWrap':
+						case 'mapDiffuseAnisotropy':
+							break;
+						case 'mapEmissive':
+							json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapEmissiveRepeat':
+						case 'mapEmissiveOffset':
+						case 'mapEmissiveWrap':
+						case 'mapEmissiveAnisotropy':
+							break;
+						case 'mapLight':
+							json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapLightRepeat':
+						case 'mapLightOffset':
+						case 'mapLightWrap':
+						case 'mapLightAnisotropy':
+							break;
+						case 'mapAO':
+							json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapAORepeat':
+						case 'mapAOOffset':
+						case 'mapAOWrap':
+						case 'mapAOAnisotropy':
+							break;
+						case 'mapBump':
+							json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapBumpScale':
+							json.bumpScale = value;
+							break;
+						case 'mapBumpRepeat':
+						case 'mapBumpOffset':
+						case 'mapBumpWrap':
+						case 'mapBumpAnisotropy':
+							break;
+						case 'mapNormal':
+							json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapNormalFactor':
+							json.normalScale = value;
+							break;
+						case 'mapNormalRepeat':
+						case 'mapNormalOffset':
+						case 'mapNormalWrap':
+						case 'mapNormalAnisotropy':
+							break;
+						case 'mapSpecular':
+							json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapSpecularRepeat':
+						case 'mapSpecularOffset':
+						case 'mapSpecularWrap':
+						case 'mapSpecularAnisotropy':
+							break;
+						case 'mapMetalness':
+							json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapMetalnessRepeat':
+						case 'mapMetalnessOffset':
+						case 'mapMetalnessWrap':
+						case 'mapMetalnessAnisotropy':
+							break;
+						case 'mapRoughness':
+							json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapRoughnessRepeat':
+						case 'mapRoughnessOffset':
+						case 'mapRoughnessWrap':
+						case 'mapRoughnessAnisotropy':
+							break;
+						case 'mapAlpha':
+							json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy, textures, texturePath, crossOrigin );
+							break;
+						case 'mapAlphaRepeat':
+						case 'mapAlphaOffset':
+						case 'mapAlphaWrap':
+						case 'mapAlphaAnisotropy':
+							break;
+						case 'flipSided':
+							json.side = BackSide;
+							break;
+						case 'doubleSided':
+							json.side = DoubleSide;
+							break;
+						case 'transparency':
+							console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );
+							json.opacity = value;
+							break;
+						case 'depthTest':
+						case 'depthWrite':
+						case 'colorWrite':
+						case 'opacity':
+						case 'reflectivity':
+						case 'transparent':
+						case 'visible':
+						case 'wireframe':
+							json[ name ] = value;
+							break;
+						case 'vertexColors':
+							if ( value === true ) json.vertexColors = VertexColors;
+							if ( value === 'face' ) json.vertexColors = FaceColors;
+							break;
+						default:
+							console.error( 'THREE.Loader.createMaterial: Unsupported', name, value );
+							break;
+
+					}
+
+				}
+
+				if ( json.type === 'MeshBasicMaterial' ) delete json.emissive;
+				if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;
+
+				if ( json.opacity < 1 ) json.transparent = true;
+
+				_materialLoader.setTextures( textures );
+
+				return _materialLoader.parse( json );
+
+			}
+
+			function loadTexture( path, repeat, offset, wrap, anisotropy, textures, texturePath, crossOrigin ) {
+
+				var fullPath = texturePath + path;
+				var loader = Loader.Handlers.get( fullPath );
+
+				var texture;
+
+				if ( loader !== null ) {
+
+					texture = loader.load( fullPath );
+
+				} else {
+
+					_textureLoader.setCrossOrigin( crossOrigin );
+					texture = _textureLoader.load( fullPath );
+
+				}
+
+				if ( repeat !== undefined ) {
+
+					texture.repeat.fromArray( repeat );
+
+					if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping;
+					if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping;
+
+				}
+
+				if ( offset !== undefined ) {
+
+					texture.offset.fromArray( offset );
+
+				}
+
+				if ( wrap !== undefined ) {
+
+					if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping;
+					if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping;
+
+					if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping;
+					if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping;
+
+				}
+
+				if ( anisotropy !== undefined ) {
+
+					texture.anisotropy = anisotropy;
+
+				}
+
+				var uuid = _Math.generateUUID();
+
+				textures[ uuid ] = texture;
+
+				return uuid;
+
+			}
+
 			function parseModel( json, geometry ) {
 
 				function isBitSet( value, position ) {
@@ -575,7 +854,7 @@ var LegacyJSONLoader = ( function () {
 
 				} else {
 
-					var materials = Loader.prototype.initMaterials( json.materials, this.resourcePath || path, this.crossOrigin );
+					var materials = initMaterials( json.materials, this.resourcePath || path, this.crossOrigin );
 
 					return { geometry: geometry, materials: materials };
 

+ 2 - 2
examples/jsm/loaders/sea3d/SEA3DLoader.js

@@ -2715,8 +2715,8 @@ SEA3D.prototype.materialTechnique =
 	techniques[ SEA3DSDK.Material.CLEAR_COAT ] =
 	function ( mat, tech ) {
 
-		mat.clearCoat = tech.strength;
-		mat.clearCoatRoughness = tech.roughness;
+		mat.clearcoat = tech.strength;
+		mat.clearcoatRoughness = tech.roughness;
 
 	};
 

+ 3 - 2
examples/jsm/nodes/accessors/NormalNode.js

@@ -36,7 +36,8 @@ NormalNode.prototype.generate = function ( builder, output ) {
 
 		case NormalNode.LOCAL:
 
-			result = 'normal';
+			if ( builder.isShader( 'vertex' ) ) result = 'objectNormal';
+			else result = 'geometryNormal';
 
 			break;
 
@@ -86,7 +87,7 @@ NormalNode.prototype.toJSON = function ( meta ) {
 
 };
 
-NodeLib.addKeyword( 'normal', function () {
+NodeLib.addKeyword( 'viewNormal', function () {
 
 	return new NormalNode();
 

+ 52 - 7
examples/jsm/nodes/accessors/ReflectNode.js

@@ -3,10 +3,12 @@
  */
 
 import { TempNode } from '../core/TempNode.js';
+import { PositionNode } from './PositionNode.js';
+import { NormalNode } from './NormalNode.js';
 
 function ReflectNode( scope ) {
 
-	TempNode.call( this, 'v3', { unique: true } );
+	TempNode.call( this, 'v3' );
 
 	this.scope = scope || ReflectNode.CUBE;
 
@@ -20,6 +22,12 @@ ReflectNode.prototype = Object.create( TempNode.prototype );
 ReflectNode.prototype.constructor = ReflectNode;
 ReflectNode.prototype.nodeType = "Reflect";
 
+ReflectNode.prototype.getUnique = function ( builder ) {
+
+	return !builder.context.viewNormal;
+
+};
+
 ReflectNode.prototype.getType = function ( /* builder */ ) {
 
 	switch ( this.scope ) {
@@ -36,6 +44,8 @@ ReflectNode.prototype.getType = function ( /* builder */ ) {
 
 ReflectNode.prototype.generate = function ( builder, output ) {
 
+	var isUnique = this.getUnique( builder );
+
 	if ( builder.isShader( 'fragment' ) ) {
 
 		var result;
@@ -44,9 +54,24 @@ ReflectNode.prototype.generate = function ( builder, output ) {
 
 			case ReflectNode.VECTOR:
 
-				builder.addNodeCode( 'vec3 reflectVec = inverseTransformDirection( reflect( -normalize( vViewPosition ), normal ), viewMatrix );' );
+				var viewNormalNode = builder.context.viewNormal || new NormalNode();
+
+				var viewNormal = viewNormalNode.build( builder, 'v3' );
+				var viewPosition = new PositionNode( PositionNode.VIEW ).build( builder, 'v3' );
+
+				var code = `inverseTransformDirection( reflect( -normalize( ${viewPosition} ), ${viewNormal} ), viewMatrix )`;
+
+				if ( isUnique ) {
+
+					builder.addNodeCode( `vec3 reflectVec = ${code};` );
+
+					result = 'reflectVec';
+
+				} else {
 
-				result = 'reflectVec';
+					result = code;
+
+				}
 
 				break;
 
@@ -54,9 +79,19 @@ ReflectNode.prototype.generate = function ( builder, output ) {
 
 				var reflectVec = new ReflectNode( ReflectNode.VECTOR ).build( builder, 'v3' );
 
-				builder.addNodeCode( 'vec3 reflectCubeVec = vec3( -1.0 * ' + reflectVec + '.x, ' + reflectVec + '.yz );' );
+				var code = 'vec3( -' + reflectVec + '.x, ' + reflectVec + '.yz )';
+
+				if ( isUnique ) {
+
+					builder.addNodeCode( `vec3 reflectCubeVec = ${code};` );
+
+					result = 'reflectCubeVec';
+
+				} else {
+
+					result = code;
 
-				result = 'reflectCubeVec';
+				}
 
 				break;
 
@@ -64,9 +99,19 @@ ReflectNode.prototype.generate = function ( builder, output ) {
 
 				var reflectVec = new ReflectNode( ReflectNode.VECTOR ).build( builder, 'v3' );
 
-				builder.addNodeCode( 'vec2 reflectSphereVec = normalize( ( viewMatrix * vec4( ' + reflectVec + ', 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) ).xy * 0.5 + 0.5;' );
+				var code = 'normalize( ( viewMatrix * vec4( ' + reflectVec + ', 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) ).xy * 0.5 + 0.5';
+
+				if ( isUnique ) {
+
+					builder.addNodeCode( `vec2 reflectSphereVec = ${code};` );
+
+					result = 'reflectSphereVec';
+
+				} else {
+
+					result = code;
 
-				result = 'reflectSphereVec';
+				}
 
 				break;
 

+ 2 - 2
examples/jsm/nodes/bsdfs/BlinnShininessExponentNode.js

@@ -16,9 +16,9 @@ BlinnShininessExponentNode.prototype.nodeType = "BlinnShininessExponent";
 
 BlinnShininessExponentNode.prototype.generate = function ( builder, output ) {
 
-	if ( builder.isCache( 'clearCoat' ) ) {
+	if ( builder.isCache( 'clearcoat' ) ) {
 
-		return builder.format( 'Material_ClearCoat_BlinnShininessExponent( material )', this.type, output );
+		return builder.format( 'Material_Clearcoat_BlinnShininessExponent( material )', this.type, output );
 
 	} else {
 

+ 5 - 3
examples/jsm/nodes/materials/StandardNodeMaterial.js

@@ -25,8 +25,9 @@ NodeUtils.addShortcuts( StandardNodeMaterial.prototype, 'fragment', [
 	'roughness',
 	'metalness',
 	'reflectivity',
-	'clearCoat',
-	'clearCoatRoughness',
+	'clearcoat',
+	'clearcoatRoughness',
+	'clearcoatNormal',
 	'normal',
 	'emissive',
 	'ambient',
@@ -35,7 +36,8 @@ NodeUtils.addShortcuts( StandardNodeMaterial.prototype, 'fragment', [
 	'ao',
 	'environment',
 	'mask',
-	'position'
+	'position',
+	'sheen'
 ] );
 
 export { StandardNodeMaterial };

+ 62 - 26
examples/jsm/nodes/materials/nodes/StandardNode.js

@@ -8,6 +8,7 @@ import {
 } from '../../../../../build/three.module.js';
 
 import { Node } from '../../core/Node.js';
+import { ExpressionNode } from '../../core/ExpressionNode.js';
 import { ColorNode } from '../../inputs/ColorNode.js';
 import { FloatNode } from '../../inputs/FloatNode.js';
 import { RoughnessToBlinnExponentNode } from '../../bsdfs/RoughnessToBlinnExponentNode.js';
@@ -32,7 +33,7 @@ StandardNode.prototype.build = function ( builder ) {
 
 	var code;
 
-	builder.define( this.clearCoat || this.clearCoatRoughness ? 'PHYSICAL' : 'STANDARD' );
+	builder.define( this.clearcoat || this.clearcoatRoughness || this.clearcoatNormal ? 'PHYSICAL' : 'STANDARD' );
 
 	builder.requires.lights = true;
 
@@ -122,6 +123,7 @@ StandardNode.prototype.build = function ( builder ) {
 
 		var contextEnvironment = {
 			bias: RoughnessToBlinnExponentNode,
+			viewNormal: new ExpressionNode('normal', 'v3'),
 			gamma: true
 		};
 
@@ -129,7 +131,13 @@ StandardNode.prototype.build = function ( builder ) {
 			gamma: true
 		};
 
-		var useClearCoat = ! builder.isDefined( 'STANDARD' );
+		var contextClearcoatEnvironment = {
+			bias: RoughnessToBlinnExponentNode,
+			viewNormal: new ExpressionNode('clearcoatNormal', 'v3'),
+			gamma: true
+		};
+
+		var useClearcoat = ! builder.isDefined( 'STANDARD' );
 
 		// analyze all nodes to reuse generate codes
 
@@ -143,8 +151,9 @@ StandardNode.prototype.build = function ( builder ) {
 
 		if ( this.normal ) this.normal.analyze( builder );
 
-		if ( this.clearCoat ) this.clearCoat.analyze( builder );
-		if ( this.clearCoatRoughness ) this.clearCoatRoughness.analyze( builder );
+		if ( this.clearcoat ) this.clearcoat.analyze( builder );
+		if ( this.clearcoatRoughness ) this.clearcoatRoughness.analyze( builder );
+		if ( this.clearcoatNormal ) this.clearcoatNormal.analyze( builder );
 
 		if ( this.reflectivity ) this.reflectivity.analyze( builder );
 
@@ -160,16 +169,18 @@ StandardNode.prototype.build = function ( builder ) {
 			// isolate environment from others inputs ( see TextureNode, CubeTextureNode )
 			// environment.analyze will detect if there is a need of calculate irradiance
 
-			this.environment.analyze( builder, { cache: 'radiance', context: contextEnvironment, slot: 'radiance' } ); 
+			this.environment.analyze( builder, { cache: 'radiance', context: contextEnvironment, slot: 'radiance' } );
 
 			if ( builder.requires.irradiance ) {
 
-				this.environment.analyze( builder, { cache: 'irradiance', context: contextEnvironment, slot: 'irradiance' } ); 
+				this.environment.analyze( builder, { cache: 'irradiance', context: contextEnvironment, slot: 'irradiance' } );
 
 			}
 
 		}
 
+		if ( this.sheen ) this.sheen.analyze( builder );
+
 		// build code
 
 		var mask = this.mask ? this.mask.flow( builder, 'b' ) : undefined;
@@ -182,8 +193,9 @@ StandardNode.prototype.build = function ( builder ) {
 
 		var normal = this.normal ? this.normal.flow( builder, 'v3' ) : undefined;
 
-		var clearCoat = this.clearCoat ? this.clearCoat.flow( builder, 'f' ) : undefined;
-		var clearCoatRoughness = this.clearCoatRoughness ? this.clearCoatRoughness.flow( builder, 'f' ) : undefined;
+		var clearcoat = this.clearcoat ? this.clearcoat.flow( builder, 'f' ) : undefined;
+		var clearcoatRoughness = this.clearcoatRoughness ? this.clearcoatRoughness.flow( builder, 'f' ) : undefined;
+		var clearcoatNormal = this.clearcoatNormal ? this.clearcoatNormal.flow( builder, 'v3' ) : undefined;
 
 		var reflectivity = this.reflectivity ? this.reflectivity.flow( builder, 'f' ) : undefined;
 
@@ -210,7 +222,9 @@ StandardNode.prototype.build = function ( builder ) {
 
 		}
 
-		var clearCoatEnv = useClearCoat && environment ? this.environment.flow( builder, 'c', { cache: 'clearCoat', context: contextEnvironment, slot: 'environment' } ) : undefined;
+		var clearcoatEnv = useClearcoat && environment ? this.environment.flow( builder, 'c', { cache: 'clearcoat', context: contextClearcoatEnvironment, slot: 'environment' } ) : undefined;
+
+		var sheen = this.sheen ? this.sheen.flow( builder, 'c' ) : undefined;
 
 		builder.requires.transparent = alpha !== undefined;
 
@@ -238,6 +252,7 @@ StandardNode.prototype.build = function ( builder ) {
 
 			// add before: prevent undeclared normal
 			"	#include <normal_fragment_begin>",
+			"	#include <clearcoat_normal_fragment_begin>",
 
 			// add before: prevent undeclared material
 			"	PhysicalMaterial material;",
@@ -289,6 +304,15 @@ StandardNode.prototype.build = function ( builder ) {
 
 		}
 
+		if ( clearcoatNormal ) {
+
+			output.push(
+				clearcoatNormal.code,
+				'clearcoatNormal = ' + clearcoatNormal.result + ';'
+			);
+
+		}
+
 		// optimization for now
 
 		output.push(
@@ -296,29 +320,35 @@ StandardNode.prototype.build = function ( builder ) {
 			'material.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );'
 		);
 
-		if ( clearCoat ) {
+		if ( clearcoat ) {
 
 			output.push(
-				clearCoat.code,
-				'material.clearCoat = saturate( ' + clearCoat.result + ' );'
+				clearcoat.code,
+				'material.clearcoat = saturate( ' + clearcoat.result + ' );'
 			);
 
-		} else if ( useClearCoat ) {
+		} else if ( useClearcoat ) {
 
-			output.push( 'material.clearCoat = 0.0;' );
+			output.push( 'material.clearcoat = 0.0;' );
 
 		}
 
-		if ( clearCoatRoughness ) {
+		if ( clearcoatRoughness ) {
 
 			output.push(
-				clearCoatRoughness.code,
-				'material.clearCoatRoughness = clamp( ' + clearCoatRoughness.result + ', 0.04, 1.0 );'
+				clearcoatRoughness.code,
+				'material.clearcoatRoughness = clamp( ' + clearcoatRoughness.result + ', 0.04, 1.0 );'
 			);
 
-		} else if ( useClearCoat ) {
+		} else if ( useClearcoat ) {
 
-			output.push( 'material.clearCoatRoughness = 0.0;' );
+			output.push( 'material.clearcoatRoughness = 0.0;' );
+
+		}
+
+		if ( sheen ) {
+
+			output.push( 'material.sheenColor = ' + sheen.result + ';' );
 
 		}
 
@@ -408,11 +438,11 @@ StandardNode.prototype.build = function ( builder ) {
 
 			}
 
-			if ( clearCoatEnv ) {
+			if ( clearcoatEnv ) {
 
 				output.push(
-					clearCoatEnv.code,
-					"clearCoatRadiance += " + clearCoatEnv.result + ";"
+					clearcoatEnv.code,
+					"clearcoatRadiance += " + clearcoatEnv.result + ";"
 				);
 
 			}
@@ -479,8 +509,9 @@ StandardNode.prototype.copy = function ( source ) {
 
 	if ( source.normal ) this.normal = source.normal;
 
-	if ( source.clearCoat ) this.clearCoat = source.clearCoat;
-	if ( source.clearCoatRoughness ) this.clearCoatRoughness = source.clearCoatRoughness;
+	if ( source.clearcoat ) this.clearcoat = source.clearcoat;
+	if ( source.clearcoatRoughness ) this.clearcoatRoughness = source.clearcoatRoughness;
+	if ( source.clearcoatNormal ) this.clearcoatNormal = source.clearcoatNormal;
 
 	if ( source.reflectivity ) this.reflectivity = source.reflectivity;
 
@@ -494,6 +525,8 @@ StandardNode.prototype.copy = function ( source ) {
 
 	if ( source.environment ) this.environment = source.environment;
 
+	if ( source.sheen ) this.sheen = source.sheen;
+
 	return this;
 
 };
@@ -522,8 +555,9 @@ StandardNode.prototype.toJSON = function ( meta ) {
 
 		if ( this.normal ) data.normal = this.normal.toJSON( meta ).uuid;
 
-		if ( this.clearCoat ) data.clearCoat = this.clearCoat.toJSON( meta ).uuid;
-		if ( this.clearCoatRoughness ) data.clearCoatRoughness = this.clearCoatRoughness.toJSON( meta ).uuid;
+		if ( this.clearcoat ) data.clearcoat = this.clearcoat.toJSON( meta ).uuid;
+		if ( this.clearcoatRoughness ) data.clearcoatRoughness = this.clearcoatRoughness.toJSON( meta ).uuid;
+		if ( this.clearcoatNormal ) data.clearcoatNormal = this.clearcoatNormal.toJSON( meta ).uuid;
 
 		if ( this.reflectivity ) data.reflectivity = this.reflectivity.toJSON( meta ).uuid;
 
@@ -537,6 +571,8 @@ StandardNode.prototype.toJSON = function ( meta ) {
 
 		if ( this.environment ) data.environment = this.environment.toJSON( meta ).uuid;
 
+		if ( this.sheen ) data.sheen = this.sheen.toJSON( meta ).uuid;
+
 	}
 
 	return data;

+ 1 - 1
examples/jsm/shaders/TranslucentShader.js

@@ -194,7 +194,7 @@ var TranslucentShader = {
 		"	#if defined( RE_IndirectSpecular )",
 
 		"		vec3 radiance = vec3( 0.0 );",
-		"		vec3 clearCoatRadiance = vec3( 0.0 );",
+		"		vec3 clearcoatRadiance = vec3( 0.0 );",
 
 		"	#endif",
 		ShaderChunk[ "lights_fragment_end" ],

+ 1 - 1
examples/misc_controls_transform.html

@@ -83,7 +83,7 @@
 
 						case 17: // Ctrl
 							control.setTranslationSnap( 100 );
-							control.setRotationSnap( Math.degToRad( 15 ) );
+							control.setRotationSnap( THREE.Math.degToRad( 15 ) );
 							break;
 
 						case 87: // W

BIN
examples/models/fbx/cloth.fbx


+ 9 - 9
examples/webgl_materials_clearcoat_normalmap.html

@@ -87,12 +87,12 @@
 
 						var normalMap2 = textureLoader.load( "textures/water/Water_1_M_Normal.jpg" );
 
-						var clearCoatNormaMap = textureLoader.load( "textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png" );
+						var clearcoatNormaMap = textureLoader.load( "textures/pbr/Scratched_gold/Scratched_gold_01_1K_Normal.png" );
 
 						//
 
 						var material = new THREE.MeshPhysicalMaterial( {
-							clearCoat: 1.0,
+							clearcoat: 1.0,
 							envMap: hdrCubeRenderTarget.texture,
 							map: diffuse
 						} );
@@ -105,7 +105,7 @@
 						// normalmap
 
 						var material = new THREE.MeshPhysicalMaterial( {
-							clearCoat: 1.0,
+							clearcoat: 1.0,
 							envMap: hdrCubeRenderTarget.texture,
 							map: diffuse,
 							normalMap: normalMap
@@ -118,12 +118,12 @@
 						// clearcoat
 
 						var material = new THREE.MeshPhysicalMaterial( {
-							clearCoat: 1.0,
+							clearcoat: 1.0,
 							metalness: 0.0,
 							color: 0xff0000,
 							envMap: hdrCubeRenderTarget.texture,
-							clearCoatNormalMap: clearCoatNormaMap,
-							clearCoatNormalScale: new THREE.Vector2( 2.0, 2.0 )
+							clearcoatNormalMap: clearcoatNormaMap,
+							clearcoatNormalScale: new THREE.Vector2( 2.0, 2.0 )
 						} );
 						var mesh = new THREE.Mesh( geometry, material );
 						mesh.position.x = - 100;
@@ -133,14 +133,14 @@
 						// clearcoat + normalmap
 
 						var material = new THREE.MeshPhysicalMaterial( {
-							clearCoat: 1.0,
+							clearcoat: 1.0,
 							metalness: 1.0,
 							color: 0xff0000,
 							envMap: hdrCubeRenderTarget.texture,
 							normalMap: normalMap2,
 							normalScale: new THREE.Vector2( 0.15, 0.15 ),
-							clearCoatNormalMap: clearCoatNormaMap,
-							clearCoatNormalScale: new THREE.Vector2( 2.0, 2.0 )
+							clearcoatNormalMap: clearcoatNormaMap,
+							clearcoatNormalScale: new THREE.Vector2( 2.0, 2.0 )
 						} );
 						var mesh = new THREE.Mesh( geometry, material );
 						mesh.position.x = 100;

+ 6 - 3
examples/webgl_materials_cubemap_mipmaps.html

@@ -28,12 +28,13 @@
 			init();
 			animate();
 
-			//load custmized cube texture
+			//load customized cube texture
 			async function loadCubeTextureWithMipmaps() {
 
 				var path = 'textures/cube/angus/';
 				var format = '.jpg';
 				var mipmaps = [];
+				var maxLevel = 8;
 
 				async function loadCubeTexture( urls ) {
 
@@ -53,7 +54,7 @@
 				// load mipmaps
 				var pendings = [];
 
-				for ( var level = 0; level < 9; ++ level ) {
+				for ( var level = 0; level <= maxLevel; ++ level ) {
 
 					var urls = [];
 
@@ -63,9 +64,11 @@
 
 					}
 
+					let mipmapLevel = level;
+
 					pendings.push( loadCubeTexture( urls ).then( function ( cubeTexture ) {
 
-						mipmaps.push( cubeTexture );
+						mipmaps[ mipmapLevel ] = cubeTexture;
 
 					} ) );
 

+ 34 - 28
examples/webgl_materials_nodes.html

@@ -740,8 +740,9 @@
 						//mtl.roughness = // roughness (float)
 						//mtl.metalness = // metalness (float)
 						//mtl.reflectivity = // reflectivity (float)
-						//mtl.clearCoat = // clearCoat (float)
-						//mtl.clearCoatRoughness = // clearCoatRoughness (float)
+						//mtl.clearcoat = // clearcoat (float)
+						//mtl.clearcoatRoughness = // clearcoatRoughness (float)
+						//mtl.clearcoatNormal = // clearcoatNormal (vec3)
 						//mtl.normal = // normal (vec3)
 						//mtl.emissive = // emissive color (vec3)
 						//mtl.ambient = // ambient color (vec3)
@@ -754,6 +755,7 @@
 						var mask = new Nodes.SwitchNode( new Nodes.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
 
 						var normalScale = new Nodes.FloatNode( .3 );
+						var clearcoatNormalScale = new Nodes.FloatNode( .1 );
 
 						var roughnessA = new Nodes.FloatNode( .5 );
 						var metalnessA = new Nodes.FloatNode( .5 );
@@ -762,8 +764,8 @@
 						var metalnessB = new Nodes.FloatNode( 1 );
 
 						var reflectivity = new Nodes.FloatNode( 0 );
-						var clearCoat = new Nodes.FloatNode( 1 );
-						var clearCoatRoughness = new Nodes.FloatNode( 1 );
+						var clearcoat = new Nodes.FloatNode( 1 );
+						var clearcoatRoughness = new Nodes.FloatNode( 1 );
 
 						var roughness = new Nodes.MathNode(
 							roughnessA,
@@ -785,12 +787,20 @@
 							Nodes.OperatorNode.MUL
 						);
 
+						var clearcoatNormalMask = new Nodes.OperatorNode(
+							mask,
+							clearcoatNormalScale,
+							Nodes.OperatorNode.MUL
+						);
+
 						mtl.color = new Nodes.ColorNode( 0xEEEEEE );
 						mtl.roughness = roughness;
 						mtl.metalness = metalness;
 						mtl.reflectivity = reflectivity;
-						mtl.clearCoat = clearCoat;
-						mtl.clearCoatRoughness = clearCoatRoughness;
+						mtl.clearcoat = clearcoat;
+						mtl.clearcoatRoughness = clearcoatRoughness;
+						mtl.clearcoatNormal = new Nodes.NormalMapNode( new Nodes.TextureNode( getTexture( "grassNormal" ) ) );
+						mtl.clearcoatNormal.scale = clearcoatNormalMask;
 						mtl.environment = new Nodes.CubeTextureNode( cubemap );
 						mtl.normal = new Nodes.NormalMapNode( new Nodes.TextureNode( getTexture( "grassNormal" ) ) );
 						mtl.normal.scale = normalMask;
@@ -809,15 +819,21 @@
 
 						}, false, 0, 1 );
 
-						addGui( 'clearCoat', clearCoat.value, function ( val ) {
+						addGui( 'clearcoat', clearcoat.value, function ( val ) {
 
-							clearCoat.value = val;
+							clearcoat.value = val;
 
 						}, false, 0, 1 );
 
-						addGui( 'clearCoatRoughness', clearCoatRoughness.value, function ( val ) {
+						addGui( 'clearcoatRoughness', clearcoatRoughness.value, function ( val ) {
 
-							clearCoatRoughness.value = val;
+							clearcoatRoughness.value = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'clearcoatNormalScale', clearcoatNormalScale.value, function ( val ) {
+
+							clearcoatNormalScale.value = val;
 
 						}, false, 0, 1 );
 
@@ -2220,8 +2236,8 @@
 							mtl.metalness = new Nodes.FloatNode( 0 );
 							mtl.roughness = new Nodes.FloatNode( 1 );
 							mtl.reflectivity = new Nodes.FloatNode( 0 );
-							mtl.clearCoat = new Nodes.FloatNode( .2 );
-							mtl.clearCoatRoughness = new Nodes.FloatNode( .3 );
+							mtl.clearcoat = new Nodes.FloatNode( .2 );
+							mtl.clearcoatRoughness = new Nodes.FloatNode( .3 );
 							mtl.environment = new Nodes.CubeTextureNode( cubemap );
 
 						} else {
@@ -2586,12 +2602,14 @@
 
 							var nodeMaterial = new Nodes.StandardNodeMaterial();
 							nodeMaterial.environment = new Nodes.CubeTextureNode( cubemap, node );
-							nodeMaterial.roughness = new Nodes.FloatNode(0);
-							nodeMaterial.metalness = new Nodes.FloatNode(1);
+							nodeMaterial.roughness.value = .5;
+							nodeMaterial.metalness.value = 1;
 
 							var standardMaterial = new THREE.MeshStandardMaterial( {
+								color: nodeMaterial.color.value,
+								side: defaultSide,
 								envMap: cubemap,
-								roughness: 0,
+								roughness: nodeMaterial.roughness.value,
 								metalness: 1
 							} );
 
@@ -2599,18 +2617,6 @@
 
 							// GUI
 
-							addGui( 'scope', {
-								vector: Nodes.ReflectNode.VECTOR,
-								cube: Nodes.ReflectNode.CUBE,
-								sphere: Nodes.ReflectNode.SPHERE
-							}, function ( val ) {
-
-								node.scope = val;
-
-								nodeMaterial.needsUpdate = true;
-
-							} );
-
 							addGui( 'node', true, function ( val ) {
 
 								mtl = val ? nodeMaterial : standardMaterial;
@@ -2618,7 +2624,7 @@
 
 							} );
 
-							addGui( 'roughness', 0, function ( val ) {
+							addGui( 'roughness', nodeMaterial.roughness.value, function ( val ) {
 
 								nodeMaterial.roughness.value = val;
 								standardMaterial.roughness = val;

+ 203 - 0
examples/webgl_materials_sheen.html

@@ -0,0 +1,203 @@
+<html lang="en">
+	<head>
+		<title>Sheen demo (material property)</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 {
+				color: #333;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">Sheen demo by <a href="https://github.com/DanielSturk">DanielSturk</a></div>
+		<div id="container"></div>
+
+		<script src="js/libs/ammo.js"></script>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import * as Nodes from './jsm/nodes/Nodes.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 { FBXLoader } from './jsm/loaders/FBXLoader.js';
+
+			// Graphics variables
+			var camera, controls, scene, renderer, stats;
+			var directionalLight;
+			var mesh, sphere, material, nodeMaterial;
+
+			var params = {
+				nodeMaterial: true,
+				color: new THREE.Color( 255, 0, 127 ),
+				sheenBRDF: true,
+				sheen: new THREE.Color( 10, 10, 10 ), // corresponds to .04 reflectance
+				roughness: .9,
+				exposure: 2,
+			};
+
+			// model
+			new FBXLoader().load( 'models/fbx/cloth.fbx', function ( loadedModel ) {
+
+				mesh = loadedModel.children[0];
+
+				init();
+
+			} );
+
+			function init( ) {
+
+				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.2, 2000 );
+
+				scene = new THREE.Scene();
+				scene.background = new THREE.Color( 0xbfd1e5 );
+
+				mesh.scale.multiplyScalar( .5 );
+				scene.add( mesh );
+
+				//
+
+				material = new THREE.MeshPhysicalMaterial();
+				material.side = THREE.DoubleSide;
+				material.metalness = 0;
+
+				//
+
+				nodeMaterial = new Nodes.StandardNodeMaterial();
+				nodeMaterial.side = THREE.DoubleSide;
+				nodeMaterial.metalness = new Nodes.FloatNode( 0 );
+				nodeMaterial.roughness = new Nodes.FloatNode();
+				nodeMaterial.color = new Nodes.ColorNode( params.color.clone() );
+
+				//
+
+				sphere = new THREE.Mesh(
+					new THREE.SphereBufferGeometry( 1, 100, 100 ),
+					material
+				);
+				scene.add(sphere);
+
+				camera.position.set( - 12, 7, 4 );
+
+				var container = document.getElementById( 'container' );
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.shadowMap.enabled = true;
+				container.appendChild( renderer.domElement );
+
+				controls = new OrbitControls( camera, renderer.domElement );
+				controls.target.set( 0, 2, 0 );
+				controls.update();
+
+				directionalLight = new THREE.DirectionalLight( 0xffffff, .5 );
+				directionalLight.position.set( 0, 10, 0 );
+				directionalLight.castShadow = true;
+				directionalLight.add(
+					new THREE.Mesh(
+						new THREE.SphereBufferGeometry( .5 ),
+						new THREE.MeshBasicMaterial( { color: 0xffffff } )
+					)
+				);
+
+				scene.add( directionalLight );
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.dom );
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+				var gui = new GUI();
+
+				function onUpdate() {
+
+					mesh.material = sphere.material = params.nodeMaterial
+					? nodeMaterial
+					: material;
+
+					material.sheen = params.sheenBRDF
+					? new THREE.Color()
+					: null;
+
+					material.needsUpdate = true;
+
+					nodeMaterial.sheen = params.sheenBRDF
+					? new Nodes.ColorNode( material.sheen )
+					: undefined;
+
+					nodeMaterial.needsCompile = true;
+
+				}
+
+				gui.add( params, 'nodeMaterial' ).onChange( onUpdate );
+				gui.addColor( params, 'color' );
+				gui.add( params, 'sheenBRDF' ).onChange( onUpdate );
+				gui.addColor( params, 'sheen' );
+				gui.add( params, 'roughness', 0, 1 );
+				gui.add( params, 'exposure', 0, 3 );
+				gui.open();
+
+				onUpdate();
+
+				animate();
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				//
+
+				material.color.copy( params.color ).multiplyScalar( 1 / 255 );
+				material.roughness = params.roughness;
+
+				//
+
+				nodeMaterial.color.value.copy( material.color );
+				nodeMaterial.roughness.value = params.roughness;
+
+				//
+
+				if ( params.sheenBRDF ) {
+
+					material.sheen.copy( params.sheen ).multiplyScalar( 1 / 255 );
+
+				}
+
+				//
+
+				renderer.toneMappingExposure = params.exposure;
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 6 - 6
examples/webgl_materials_variations_physical.html

@@ -96,8 +96,8 @@
 										color: diffuseColor,
 										metalness: 0,
 										roughness: 0.5,
-										clearCoat: 1.0 - alpha,
-										clearCoatRoughness: 1.0 - beta,
+										clearcoat: 1.0 - alpha,
+										clearcoatRoughness: 1.0 - beta,
 										reflectivity: 1.0 - gamma,
 										envMap: ( index % 2 ) == 1 ? hdrCubeRenderTarget.texture : null
 									} );
@@ -148,11 +148,11 @@
 
 				}
 
-				addLabel( "+clearCoat", new THREE.Vector3( - 350, 0, 0 ) );
-				addLabel( "-clearCoat", new THREE.Vector3( 350, 0, 0 ) );
+				addLabel( "+clearcoat", new THREE.Vector3( - 350, 0, 0 ) );
+				addLabel( "-clearcoat", new THREE.Vector3( 350, 0, 0 ) );
 
-				addLabel( "+clearCoatRoughness", new THREE.Vector3( 0, - 300, 0 ) );
-				addLabel( "-clearCoatRoughness", new THREE.Vector3( 0, 300, 0 ) );
+				addLabel( "+clearcoatRoughness", new THREE.Vector3( 0, - 300, 0 ) );
+				addLabel( "-clearcoatRoughness", new THREE.Vector3( 0, 300, 0 ) );
 
 				addLabel( "+reflectivity", new THREE.Vector3( 0, 0, - 300 ) );
 				addLabel( "-reflectivity", new THREE.Vector3( 0, 0, 300 ) );

+ 73 - 62
package-lock.json

@@ -114,9 +114,9 @@
       }
     },
     "acorn": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
-      "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz",
+      "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==",
       "dev": true
     },
     "acorn-dynamic-import": {
@@ -126,9 +126,9 @@
       "dev": true
     },
     "acorn-jsx": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
-      "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz",
+      "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==",
       "dev": true
     },
     "ajv": {
@@ -389,20 +389,20 @@
       "dev": true
     },
     "concurrently": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.1.tgz",
-      "integrity": "sha512-48+FE5RJ0qc8azwKv4keVQWlni1hZeSjcWr8shBelOBtBHcKj1aJFM9lHRiSc1x7lq416pkvsqfBMhSRja+Lhw==",
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-4.1.2.tgz",
+      "integrity": "sha512-Kim9SFrNr2jd8/0yNYqDTFALzUX1tvimmwFWxmp/D4mRI+kbqIIwE2RkBDrxS2ic25O1UgQMI5AtBqdtX3ynYg==",
       "dev": true,
       "requires": {
-        "chalk": "^2.4.1",
-        "date-fns": "^1.23.0",
-        "lodash": "^4.17.10",
+        "chalk": "^2.4.2",
+        "date-fns": "^1.30.1",
+        "lodash": "^4.17.15",
         "read-pkg": "^4.0.1",
-        "rxjs": "^6.3.3",
+        "rxjs": "^6.5.2",
         "spawn-command": "^0.0.2-1",
         "supports-color": "^4.5.0",
-        "tree-kill": "^1.1.0",
-        "yargs": "^12.0.1"
+        "tree-kill": "^1.2.1",
+        "yargs": "^12.0.5"
       }
     },
     "core-util-is": {
@@ -558,9 +558,9 @@
       "dev": true
     },
     "eslint": {
-      "version": "6.1.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.1.0.tgz",
-      "integrity": "sha512-QhrbdRD7ofuV09IuE2ySWBz0FyXCq0rriLTZXZqaWSI79CVtHVRdkFuFTViiqzZhkCgfOh9USpriuGN2gIpZDQ==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.2.1.tgz",
+      "integrity": "sha512-ES7BzEzr0Q6m5TK9i+/iTpKjclXitOdDK4vT07OqbkBT2/VcN/gO9EL1C4HlK3TAOXYv2ItcmbVR9jO1MR0fJg==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
@@ -570,9 +570,9 @@
         "debug": "^4.0.1",
         "doctrine": "^3.0.0",
         "eslint-scope": "^5.0.0",
-        "eslint-utils": "^1.3.1",
-        "eslint-visitor-keys": "^1.0.0",
-        "espree": "^6.0.0",
+        "eslint-utils": "^1.4.2",
+        "eslint-visitor-keys": "^1.1.0",
+        "espree": "^6.1.0",
         "esquery": "^1.0.1",
         "esutils": "^2.0.2",
         "file-entry-cache": "^5.0.1",
@@ -618,6 +618,12 @@
             "estraverse": "^4.1.1"
           }
         },
+        "eslint-visitor-keys": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+          "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+          "dev": true
+        },
         "semver": {
           "version": "6.3.0",
           "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -661,10 +667,13 @@
       }
     },
     "eslint-utils": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
-      "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
-      "dev": true
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz",
+      "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.0.0"
+      }
     },
     "eslint-visitor-keys": {
       "version": "1.0.0",
@@ -673,14 +682,22 @@
       "dev": true
     },
     "espree": {
-      "version": "6.0.0",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-6.0.0.tgz",
-      "integrity": "sha512-lJvCS6YbCn3ImT3yKkPe0+tJ+mH6ljhGNjHQH9mRtiO6gjhVAOhVXW1yjnwqGwTkK3bGbye+hb00nFNmu0l/1Q==",
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.0.tgz",
+      "integrity": "sha512-boA7CHRLlVWUSg3iL5Kmlt/xT3Q+sXnKoRYYzj1YeM10A76TEJBbotV5pKbnK42hEUIr121zTv+QLRM5LsCPXQ==",
       "dev": true,
       "requires": {
-        "acorn": "^6.0.7",
+        "acorn": "^7.0.0",
         "acorn-jsx": "^5.0.0",
-        "eslint-visitor-keys": "^1.0.0"
+        "eslint-visitor-keys": "^1.1.0"
+      },
+      "dependencies": {
+        "eslint-visitor-keys": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+          "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+          "dev": true
+        }
       }
     },
     "esprima": {
@@ -975,9 +992,9 @@
       "dev": true
     },
     "hosted-git-info": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
-      "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+      "version": "2.8.4",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz",
+      "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==",
       "dev": true
     },
     "htmlparser2": {
@@ -1101,12 +1118,6 @@
           "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
           "dev": true
         },
-        "lodash": {
-          "version": "4.17.15",
-          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-          "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
-          "dev": true
-        },
         "string-width": {
           "version": "4.1.0",
           "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz",
@@ -1262,9 +1273,9 @@
       }
     },
     "lodash": {
-      "version": "4.17.14",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.14.tgz",
-      "integrity": "sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw==",
+      "version": "4.17.15",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
       "dev": true
     },
     "lodash.unescape": {
@@ -1470,7 +1481,7 @@
     },
     "os-tmpdir": {
       "version": "1.0.2",
-      "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
       "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
       "dev": true
     },
@@ -1493,9 +1504,9 @@
       "dev": true
     },
     "p-limit": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
-      "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
+      "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
       "dev": true,
       "requires": {
         "p-try": "^2.0.0"
@@ -1543,7 +1554,7 @@
     },
     "path-is-absolute": {
       "version": "1.0.1",
-      "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
       "dev": true
     },
@@ -1760,9 +1771,9 @@
       "dev": true
     },
     "resolve": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
-      "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
+      "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
       "dev": true,
       "requires": {
         "path-parse": "^1.0.6"
@@ -1862,9 +1873,9 @@
       "dev": true
     },
     "semver": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
-      "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
       "dev": true
     },
     "set-blocking": {
@@ -1942,7 +1953,7 @@
     "spdx-expression-parse": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-      "integrity": "sha1-meEZt6XaAOBUkcn6M4t5BII7QdA=",
+      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
       "dev": true,
       "requires": {
         "spdx-exceptions": "^2.1.0",
@@ -1950,9 +1961,9 @@
       }
     },
     "spdx-license-ids": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz",
-      "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
       "dev": true
     },
     "sprintf-js": {
@@ -2072,7 +2083,7 @@
     },
     "through": {
       "version": "2.3.8",
-      "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
       "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
       "dev": true
     },
@@ -2122,9 +2133,9 @@
       "dev": true
     },
     "typescript": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
-      "integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz",
+      "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
       "dev": true
     },
     "unicode-canonical-property-names-ecmascript": {
@@ -2247,7 +2258,7 @@
     },
     "wrap-ansi": {
       "version": "2.1.0",
-      "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
       "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
       "dev": true,
       "requires": {

+ 4 - 4
package.json

@@ -69,8 +69,8 @@
   "devDependencies": {
     "@typescript-eslint/eslint-plugin": "^2.0.0",
     "@typescript-eslint/parser": "^2.0.0",
-    "concurrently": "^4.1.1",
-    "eslint": "^6.1.0",
+    "concurrently": "^4.1.2",
+    "eslint": "^6.2.1",
     "eslint-config-mdcs": "^4.2.3",
     "eslint-plugin-html": "^6.0.0",
     "failonlyreporter": "^1.0.0",
@@ -78,8 +78,8 @@
     "http-server": "^0.11.1",
     "qunit": "^2.9.2",
     "rollup": "^1.19.4",
-    "rollup-plugin-buble": "^0.19.8",
-    "typescript": "^3.5.2"
+    "typescript": "^3.5.3",
+		"rollup-plugin-buble": "^0.19.8"
   },
   "jspm": {
     "files": [

+ 0 - 11
src/loaders/Loader.d.ts

@@ -44,17 +44,6 @@ export class Loader {
 	 */
 	crossOrigin: string;
 
-	/**
-	 * @deprecated Use THREE.LoaderUtils.extractUrlBase() instead.
-	 */
-	extractUrlBase( url: string ): string;
-	initMaterials( materials: Material[], texturePath: string ): Material[];
-	createMaterial(
-		m: Material,
-		texturePath: string,
-		crossOrigin?: string
-	): boolean;
-
 	static Handlers: LoaderHandler;
 
 }

+ 1 - 286
src/loaders/Loader.js

@@ -1,42 +1,7 @@
-import {
-	NoBlending,
-	NormalBlending,
-	AdditiveBlending,
-	SubtractiveBlending,
-	MultiplyBlending,
-	CustomBlending,
-
-	FaceColors,
-	VertexColors,
-
-	DoubleSide,
-	BackSide,
-
-	MirroredRepeatWrapping,
-	RepeatWrapping
-} from '../constants.js';
-import { _Math } from '../math/Math.js';
-import { MaterialLoader } from './MaterialLoader.js';
-import { TextureLoader } from './TextureLoader.js';
-import { Color } from '../math/Color.js';
-
 /**
  * @author alteredq / http://alteredqualia.com/
  */
 
-var _BlendingMode = {
-	NoBlending: NoBlending,
-	NormalBlending: NormalBlending,
-	AdditiveBlending: AdditiveBlending,
-	SubtractiveBlending: SubtractiveBlending,
-	MultiplyBlending: MultiplyBlending,
-	CustomBlending: CustomBlending
-};
-
-var _color = new Color();
-var _textureLoader = new TextureLoader();
-var _materialLoader = new MaterialLoader();
-
 function Loader() {}
 
 Loader.Handlers = {
@@ -80,259 +45,9 @@ Object.assign( Loader.prototype, {
 
 	onLoadProgress: function () {},
 
-	onLoadComplete: function () {},
-
-	initMaterials: function ( materials, texturePath, crossOrigin ) {
-
-		var array = [];
-
-		for ( var i = 0; i < materials.length; ++ i ) {
-
-			array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );
-
-		}
-
-		return array;
-
-	},
-
-	createMaterial: function ( m, texturePath, crossOrigin ) {
-
-		// convert from old material format
-
-		var textures = {};
-
-		//
-
-		var json = {
-			uuid: _Math.generateUUID(),
-			type: 'MeshLambertMaterial'
-		};
-
-		for ( var name in m ) {
-
-			var value = m[ name ];
-
-			switch ( name ) {
-
-				case 'DbgColor':
-				case 'DbgIndex':
-				case 'opticalDensity':
-				case 'illumination':
-					break;
-				case 'DbgName':
-					json.name = value;
-					break;
-				case 'blending':
-					json.blending = _BlendingMode[ value ];
-					break;
-				case 'colorAmbient':
-				case 'mapAmbient':
-					console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' );
-					break;
-				case 'colorDiffuse':
-					json.color = _color.fromArray( value ).getHex();
-					break;
-				case 'colorSpecular':
-					json.specular = _color.fromArray( value ).getHex();
-					break;
-				case 'colorEmissive':
-					json.emissive = _color.fromArray( value ).getHex();
-					break;
-				case 'specularCoef':
-					json.shininess = value;
-					break;
-				case 'shading':
-					if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';
-					if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';
-					if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial';
-					break;
-				case 'mapDiffuse':
-					json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapDiffuseRepeat':
-				case 'mapDiffuseOffset':
-				case 'mapDiffuseWrap':
-				case 'mapDiffuseAnisotropy':
-					break;
-				case 'mapEmissive':
-					json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapEmissiveRepeat':
-				case 'mapEmissiveOffset':
-				case 'mapEmissiveWrap':
-				case 'mapEmissiveAnisotropy':
-					break;
-				case 'mapLight':
-					json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapLightRepeat':
-				case 'mapLightOffset':
-				case 'mapLightWrap':
-				case 'mapLightAnisotropy':
-					break;
-				case 'mapAO':
-					json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapAORepeat':
-				case 'mapAOOffset':
-				case 'mapAOWrap':
-				case 'mapAOAnisotropy':
-					break;
-				case 'mapBump':
-					json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapBumpScale':
-					json.bumpScale = value;
-					break;
-				case 'mapBumpRepeat':
-				case 'mapBumpOffset':
-				case 'mapBumpWrap':
-				case 'mapBumpAnisotropy':
-					break;
-				case 'mapNormal':
-					json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapNormalFactor':
-					json.normalScale = value;
-					break;
-				case 'mapNormalRepeat':
-				case 'mapNormalOffset':
-				case 'mapNormalWrap':
-				case 'mapNormalAnisotropy':
-					break;
-				case 'mapSpecular':
-					json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapSpecularRepeat':
-				case 'mapSpecularOffset':
-				case 'mapSpecularWrap':
-				case 'mapSpecularAnisotropy':
-					break;
-				case 'mapMetalness':
-					json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapMetalnessRepeat':
-				case 'mapMetalnessOffset':
-				case 'mapMetalnessWrap':
-				case 'mapMetalnessAnisotropy':
-					break;
-				case 'mapRoughness':
-					json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapRoughnessRepeat':
-				case 'mapRoughnessOffset':
-				case 'mapRoughnessWrap':
-				case 'mapRoughnessAnisotropy':
-					break;
-				case 'mapAlpha':
-					json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy, textures, texturePath, crossOrigin );
-					break;
-				case 'mapAlphaRepeat':
-				case 'mapAlphaOffset':
-				case 'mapAlphaWrap':
-				case 'mapAlphaAnisotropy':
-					break;
-				case 'flipSided':
-					json.side = BackSide;
-					break;
-				case 'doubleSided':
-					json.side = DoubleSide;
-					break;
-				case 'transparency':
-					console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );
-					json.opacity = value;
-					break;
-				case 'depthTest':
-				case 'depthWrite':
-				case 'colorWrite':
-				case 'opacity':
-				case 'reflectivity':
-				case 'transparent':
-				case 'visible':
-				case 'wireframe':
-					json[ name ] = value;
-					break;
-				case 'vertexColors':
-					if ( value === true ) json.vertexColors = VertexColors;
-					if ( value === 'face' ) json.vertexColors = FaceColors;
-					break;
-				default:
-					console.error( 'THREE.Loader.createMaterial: Unsupported', name, value );
-					break;
-
-			}
-
-		}
-
-		if ( json.type === 'MeshBasicMaterial' ) delete json.emissive;
-		if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;
-
-		if ( json.opacity < 1 ) json.transparent = true;
-
-		_materialLoader.setTextures( textures );
-
-		return _materialLoader.parse( json );
-
-	}
+	onLoadComplete: function () {}
 
 } );
 
-function loadTexture( path, repeat, offset, wrap, anisotropy, textures, texturePath, crossOrigin ) {
-
-	var fullPath = texturePath + path;
-	var loader = Loader.Handlers.get( fullPath );
-
-	var texture;
-
-	if ( loader !== null ) {
-
-		texture = loader.load( fullPath );
-
-	} else {
-
-		_textureLoader.setCrossOrigin( crossOrigin );
-		texture = _textureLoader.load( fullPath );
-
-	}
-
-	if ( repeat !== undefined ) {
-
-		texture.repeat.fromArray( repeat );
-
-		if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping;
-		if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping;
-
-	}
-
-	if ( offset !== undefined ) {
-
-		texture.offset.fromArray( offset );
-
-	}
-
-	if ( wrap !== undefined ) {
-
-		if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping;
-		if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping;
-
-		if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping;
-		if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping;
-
-	}
-
-	if ( anisotropy !== undefined ) {
-
-		texture.anisotropy = anisotropy;
-
-	}
-
-	var uuid = _Math.generateUUID();
-
-	textures[ uuid ] = texture;
-
-	return uuid;
-
-}
 
 export { Loader };

+ 4 - 4
src/loaders/MaterialLoader.js

@@ -61,8 +61,8 @@ Object.assign( MaterialLoader.prototype, {
 		if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );
 		if ( json.specular !== undefined ) material.specular.setHex( json.specular );
 		if ( json.shininess !== undefined ) material.shininess = json.shininess;
-		if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat;
-		if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness;
+		if ( json.clearcoat !== undefined ) material.clearcoat = json.clearcoat;
+		if ( json.clearcoatRoughness !== undefined ) material.clearcoatRoughness = json.clearcoatRoughness;
 		if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;
 		if ( json.fog !== undefined ) material.fog = json.fog;
 		if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;
@@ -229,8 +229,8 @@ Object.assign( MaterialLoader.prototype, {
 
 		if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );
 
-		if ( json.clearCoatNormalMap !== undefined ) material.clearCoatNormalMap = getTexture( json.clearCoatNormalMap );
-		if ( json.clearCoatNormalScale !== undefined ) material.clearCoatNormalScale = new Vector2().fromArray( json.clearCoatNormalScale );
+		if ( json.clearcoatNormalMap !== undefined ) material.clearcoatNormalMap = getTexture( json.clearcoatNormalMap );
+		if ( json.clearcoatNormalScale !== undefined ) material.clearcoatNormalScale = new Vector2().fromArray( json.clearcoatNormalScale );
 
 		return material;
 

+ 5 - 5
src/materials/Material.js

@@ -172,13 +172,13 @@ Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 
 		if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();
 		if ( this.shininess !== undefined ) data.shininess = this.shininess;
-		if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat;
-		if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness;
+		if ( this.clearcoat !== undefined ) data.clearcoat = this.clearcoat;
+		if ( this.clearcoatRoughness !== undefined ) data.clearcoatRoughness = this.clearcoatRoughness;
 
-		if ( this.clearCoatNormalMap && this.clearCoatNormalMap.isTexture ) {
+		if ( this.clearcoatNormalMap && this.clearcoatNormalMap.isTexture ) {
 
-			data.clearCoatNormalMap = this.clearCoatNormalMap.toJSON( meta ).uuid;
-			data.clearCoatNormalScale = this.clearCoatNormalScale.toArray();
+			data.clearcoatNormalMap = this.clearcoatNormalMap.toJSON( meta ).uuid;
+			data.clearcoatNormalScale = this.clearcoatNormalScale.toArray();
 
 		}
 

+ 12 - 9
src/materials/MeshPhysicalMaterial.d.ts

@@ -4,17 +4,18 @@ import {
 	MeshStandardMaterialParameters,
 	MeshStandardMaterial,
 } from './MeshStandardMaterial';
+import { Color } from './../math/Color';
 
 export interface MeshPhysicalMaterialParameters
 	extends MeshStandardMaterialParameters {
-
 	reflectivity?: number;
-	clearCoat?: number;
-	clearCoatRoughness?: number;
+	clearcoat?: number;
+	clearcoatRoughness?: number;
 
-	clearCoatNormalScale?: Vector2;
-	clearCoatNormalMap?: Texture;
+	sheen?: Color;
 
+	clearcoatNormalScale?: Vector2;
+	clearcoatNormalMap?: Texture;
 }
 
 export class MeshPhysicalMaterial extends MeshStandardMaterial {
@@ -23,10 +24,12 @@ export class MeshPhysicalMaterial extends MeshStandardMaterial {
 
 	defines: any;
 	reflectivity: number;
-	clearCoat: number;
-	clearCoatRoughness: number;
+	clearcoat: number;
+	clearcoatRoughness: number;
+
+	sheen: Color | null;
 
-	clearCoatNormalScale: Vector2;
-	clearCoatNormalMap: Texture | null;
+	clearcoatNormalScale: Vector2;
+	clearcoatNormalMap: Texture | null;
 
 }

+ 20 - 12
src/materials/MeshPhysicalMaterial.js

@@ -1,16 +1,19 @@
 import { Vector2 } from '../math/Vector2.js';
 import { MeshStandardMaterial } from './MeshStandardMaterial.js';
+import { Color } from '../math/Color.js';
 
 /**
  * @author WestLangley / http://github.com/WestLangley
  *
  * parameters = {
  *  reflectivity: <float>
- *  clearCoat: <float>
- *  clearCoatRoughness: <float>
+ *  clearcoat: <float>
+ *  clearcoatRoughness: <float>
  *
- *  clearCoatNormalScale: <Vector2>,
- *  clearCoatNormalMap: new THREE.Texture( <Image> ),
+ *  sheen: <Color>
+ *
+ *  clearcoatNormalScale: <Vector2>,
+ *  clearcoatNormalMap: new THREE.Texture( <Image> ),
  * }
  */
 
@@ -24,11 +27,13 @@ function MeshPhysicalMaterial( parameters ) {
 
 	this.reflectivity = 0.5; // maps to F0 = 0.04
 
-	this.clearCoat = 0.0;
-	this.clearCoatRoughness = 0.0;
+	this.clearcoat = 0.0;
+	this.clearcoatRoughness = 0.0;
+
+	this.sheen = null; // null will disable sheen bsdf
 
-	this.clearCoatNormalScale = new Vector2( 1, 1 );
-	this.clearCoatNormalMap = null;
+	this.clearcoatNormalScale = new Vector2( 1, 1 );
+	this.clearcoatNormalMap = null;
 
 	this.setValues( parameters );
 
@@ -47,11 +52,14 @@ MeshPhysicalMaterial.prototype.copy = function ( source ) {
 
 	this.reflectivity = source.reflectivity;
 
-	this.clearCoat = source.clearCoat;
-	this.clearCoatRoughness = source.clearCoatRoughness;
+	this.clearcoat = source.clearcoat;
+	this.clearcoatRoughness = source.clearcoatRoughness;
+
+	if ( source.sheen ) this.sheen = ( this.sheen || new Color() ).copy( source.sheen );
+	else this.sheen = null;
 
-	this.clearCoatNormalMap = source.clearCoatNormalMap;
-	this.clearCoatNormalScale.copy( source.clearCoatNormalScale );
+	this.clearcoatNormalMap = source.clearcoatNormalMap;
+	this.clearcoatNormalScale.copy( source.clearcoatNormalScale );
 
 	return this;
 

+ 0 - 2
src/materials/MeshStandardMaterial.d.ts

@@ -29,7 +29,6 @@ export interface MeshStandardMaterialParameters extends MaterialParameters {
 	alphaMap?: Texture;
 	envMap?: Texture;
 	envMapIntensity?: number;
-	energyPreservation: boolean;
 	refractionRatio?: number;
 	wireframe?: boolean;
 	wireframeLinewidth?: number;
@@ -67,7 +66,6 @@ export class MeshStandardMaterial extends Material {
 	alphaMap: Texture | null;
 	envMap: Texture | null;
 	envMapIntensity: number;
-	energyPreservation: boolean;
 	refractionRatio: number;
 	wireframe: boolean;
 	wireframeLinewidth: number;

+ 7 - 6
src/renderers/WebGLRenderer.js

@@ -2269,17 +2269,18 @@ function WebGLRenderer( parameters ) {
 
 		uniforms.reflectivity.value = material.reflectivity; // also part of uniforms common
 
-		uniforms.clearCoat.value = material.clearCoat;
-		uniforms.clearCoatRoughness.value = material.clearCoatRoughness;
+		uniforms.clearcoat.value = material.clearcoat;
+		uniforms.clearcoatRoughness.value = material.clearcoatRoughness;
+		if ( material.sheen ) uniforms.sheen.value.copy( material.sheen );
 
-		if ( material.clearCoatNormalMap ) {
+		if ( material.clearcoatNormalMap ) {
 
-			uniforms.clearCoatNormalScale.value.copy( material.clearCoatNormalScale );
-			uniforms.clearCoatNormalMap.value = material.clearCoatNormalMap;
+			uniforms.clearcoatNormalScale.value.copy( material.clearcoatNormalScale );
+			uniforms.clearcoatNormalMap.value = material.clearcoatNormalMap;
 
 			if ( material.side === BackSide ) {
 
-				uniforms.clearCoatNormalScale.value.negate();
+				uniforms.clearcoatNormalScale.value.negate();
 
 			}
 

+ 31 - 0
src/renderers/shaders/ShaderChunk/bsdfs.glsl.js

@@ -336,4 +336,35 @@ float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {
 float BlinnExponentToGGXRoughness( const in float blinnExponent ) {
 	return sqrt( 2.0 / ( blinnExponent + 2.0 ) );
 }
+
+#if defined( USE_SHEEN )
+
+// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L94
+float D_Charlie(float roughness, float NoH) {
+	// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"
+	float invAlpha  = 1.0 / roughness;
+	float cos2h = NoH * NoH;
+	float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16
+	return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
+}
+
+// https://github.com/google/filament/blob/master/shaders/src/brdf.fs#L136
+float V_Neubelt(float NoV, float NoL) {
+	// Neubelt and Pettineo 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"
+	return saturate(1.0 / (4.0 * (NoL + NoV - NoL * NoV)));
+}
+
+vec3 BRDF_Specular_Sheen( const in float roughness, const in vec3 L, const in GeometricContext geometry, vec3 specularColor ) {
+
+	vec3 N = geometry.normal;
+	vec3 V = geometry.viewDir;
+
+	vec3 H = normalize( V + L );
+	float dotNH = saturate( dot( N, H ) );
+
+	return specularColor * D_Charlie( roughness, dotNH ) * V_Neubelt( dot(N, V), dot(N, L) );
+
+}
+
+#endif
 `;

+ 2 - 2
src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_begin.glsl.js

@@ -1,7 +1,7 @@
 export default /* glsl */`
-#ifdef USE_CLEARCOAT_NORMALMAP
+#ifdef PHYSICAL
 
-  vec3 clearCoatNormal = geometryNormal;
+	vec3 clearcoatNormal = geometryNormal;
 
 #endif
 `;

+ 4 - 4
src/renderers/shaders/ShaderChunk/clearcoat_normal_fragment_maps.glsl.js

@@ -3,14 +3,14 @@ export default /* glsl */`
 
 	#ifdef USE_TANGENT
 
-		mat3 vTBN = mat3( tangent, bitangent, clearCoatNormal );
+		mat3 vTBN = mat3( tangent, bitangent, clearcoatNormal );
 		vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;
-		mapN.xy = clearCoatNormalScale * mapN.xy;
-		clearCoatNormal = normalize( vTBN * mapN );
+		mapN.xy = clearcoatNormalScale * mapN.xy;
+		clearcoatNormal = normalize( vTBN * mapN );
 
 	#else
 
-		clearCoatNormal = perturbNormal2Arb( - vViewPosition, clearCoatNormal, clearCoatNormalScale, clearCoatNormalMap );
+		clearcoatNormal = perturbNormal2Arb( - vViewPosition, clearcoatNormal, clearcoatNormalScale, clearcoatNormalMap );
 
 	#endif
 

+ 2 - 2
src/renderers/shaders/ShaderChunk/clearcoat_normalmap_pars_fragment.glsl.js

@@ -1,8 +1,8 @@
 export default /* glsl */`
 #ifdef USE_CLEARCOAT_NORMALMAP
 
-	uniform sampler2D clearCoatNormalMap;
-	uniform vec2 clearCoatNormalScale;
+	uniform sampler2D clearcoatNormalMap;
+	uniform vec2 clearcoatNormalScale;
 
 #endif
 `;

+ 4 - 2
src/renderers/shaders/ShaderChunk/common.glsl.js

@@ -8,7 +8,7 @@ export default /* glsl */`
 #define EPSILON 1e-6
 
 #define saturate(a) clamp( a, 0.0, 1.0 )
-#define whiteCompliment(a) ( 1.0 - saturate( a ) )
+#define whiteComplement(a) ( 1.0 - saturate( a ) )
 
 float pow2( const in float x ) { return x*x; }
 float pow3( const in float x ) { return x*x*x; }
@@ -39,7 +39,9 @@ struct GeometricContext {
 	vec3 position;
 	vec3 normal;
 	vec3 viewDir;
-	vec3 clearCoatNormal;
+#ifdef PHYSICAL
+	vec3 clearcoatNormal;
+#endif
 };
 
 vec3 transformDirection( in vec3 dir, in mat4 matrix ) {

+ 3 - 7
src/renderers/shaders/ShaderChunk/lights_fragment_begin.glsl.js

@@ -20,13 +20,9 @@ geometry.position = - vViewPosition;
 geometry.normal = normal;
 geometry.viewDir = normalize( vViewPosition );
 
-#ifdef USE_CLEARCOAT_NORMALMAP
+#ifdef PHYSICAL
 
-	geometry.clearCoatNormal = clearCoatNormal;
-
-#else
-
-	geometry.clearCoatNormal = geometryNormal;
+	geometry.clearcoatNormal = clearcoatNormal;
 
 #endif
 
@@ -131,7 +127,7 @@ IncidentLight directLight;
 #if defined( RE_IndirectSpecular )
 
 	vec3 radiance = vec3( 0.0 );
-	vec3 clearCoatRadiance = vec3( 0.0 );
+	vec3 clearcoatRadiance = vec3( 0.0 );
 
 #endif
 `;

+ 1 - 1
src/renderers/shaders/ShaderChunk/lights_fragment_end.glsl.js

@@ -7,7 +7,7 @@ export default /* glsl */`
 
 #if defined( RE_IndirectSpecular )
 
-	RE_IndirectSpecular( radiance, irradiance, clearCoatRadiance, geometry, material, reflectedLight );
+	RE_IndirectSpecular( radiance, irradiance, clearcoatRadiance, geometry, material, reflectedLight );
 
 #endif
 `;

+ 2 - 2
src/renderers/shaders/ShaderChunk/lights_fragment_maps.glsl.js

@@ -27,9 +27,9 @@ export default /* glsl */`
 
 	radiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry.viewDir, geometry.normal, Material_BlinnShininessExponent( material ), maxMipLevel );
 
-	#ifndef STANDARD
+	#ifdef PHYSICAL
 
-		clearCoatRadiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry.viewDir, geometry.clearCoatNormal, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );
+		clearcoatRadiance += getLightProbeIndirectRadiance( /*specularLightProbe,*/ geometry.viewDir, geometry.clearcoatNormal, Material_Clearcoat_BlinnShininessExponent( material ), maxMipLevel );
 
 	#endif
 

+ 5 - 2
src/renderers/shaders/ShaderChunk/lights_physical_fragment.glsl.js

@@ -6,7 +6,10 @@ material.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );
 	material.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );
 #else
 	material.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );
-	material.clearCoat = saturate( clearCoat ); // Burley clearcoat model
-	material.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );
+	material.clearcoat = saturate( clearcoat ); // Burley clearcoat model
+	material.clearcoatRoughness = clamp( clearcoatRoughness, 0.04, 1.0 );
+#endif
+#ifdef USE_SHEEN
+	material.sheenColor = sheen;
 #endif
 `;

+ 33 - 21
src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

@@ -5,9 +5,13 @@ struct PhysicalMaterial {
 	float	specularRoughness;
 	vec3	specularColor;
 
-	#ifndef STANDARD
-		float clearCoat;
-		float clearCoatRoughness;
+	#ifdef PHYSICAL
+		float clearcoat;
+		float clearcoatRoughness;
+	#endif
+
+	#ifdef USE_SHEEN
+		vec3 sheenColor;
 	#endif
 
 };
@@ -16,7 +20,7 @@ struct PhysicalMaterial {
 #define DEFAULT_SPECULAR_COEFFICIENT 0.04
 
 // Clear coat directional hemishperical reflectance (this approximation should be improved)
-float clearCoatDHRApprox( const in float roughness, const in float dotNL ) {
+float clearcoatDHRApprox( const in float roughness, const in float dotNL ) {
 
 	return DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );
 
@@ -76,9 +80,9 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 	#endif
 
-	#ifndef STANDARD
+	#ifdef PHYSICAL
 
-		float ccDotNL = saturate( dot( geometry.clearCoatNormal, directLight.direction ) );
+		float ccDotNL = saturate( dot( geometry.clearcoatNormal, directLight.direction ) );
 
 		vec3 ccIrradiance = ccDotNL * directLight.color;
 
@@ -88,20 +92,28 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in GeometricC
 
 		#endif
 
-		float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, ccDotNL );
+		float clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );
 
-		reflectedLight.directSpecular += ccIrradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearCoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
+		reflectedLight.directSpecular += ccIrradiance * material.clearcoat * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );
 
 	#else
 
-		float clearCoatDHR = 0.0;
+		float clearcoatDHR = 0.0;
 
 	#endif
 
-	reflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness );
-
-	reflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
+	#ifdef USE_SHEEN
+		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_Sheen(
+			material.specularRoughness,
+			directLight.direction,
+			geometry,
+			material.sheenColor
+		);
+	#else
+		reflectedLight.directSpecular += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry.viewDir, geometry.normal, material.specularColor, material.specularRoughness);
+	#endif
 
+	reflectedLight.directDiffuse += ( 1.0 - clearcoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );
 }
 
 void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
@@ -109,24 +121,24 @@ void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricCo
 
 }
 
-void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
+void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
 
-	#ifndef STANDARD
+	#ifdef PHYSICAL
 
-		float ccDotNV = saturate( dot( geometry.clearCoatNormal, geometry.viewDir ) );
+		float ccDotNV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );
 
-		reflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearCoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );
+		reflectedLight.indirectSpecular += clearcoatRadiance * material.clearcoat * BRDF_Specular_GGX_Environment( geometry.viewDir, geometry.clearcoatNormal, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearcoatRoughness );
 
 		float ccDotNL = ccDotNV;
-		float clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, ccDotNL );
+		float clearcoatDHR = material.clearcoat * clearcoatDHRApprox( material.clearcoatRoughness, ccDotNL );
 
 	#else
 
-		float clearCoatDHR = 0.0;
+		float clearcoatDHR = 0.0;
 
 	#endif
 
-	float clearCoatInv = 1.0 - clearCoatDHR;
+	float clearcoatInv = 1.0 - clearcoatDHR;
 
 	// Both indirect specular and diffuse light accumulate here
 	// if energy preservation enabled, and PMREM provided.
@@ -139,7 +151,7 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia
 
 	vec3 diffuse = material.diffuseColor * ( 1.0 - ( singleScattering + multiScattering ) );
 
-	reflectedLight.indirectSpecular += clearCoatInv * radiance * singleScattering;
+	reflectedLight.indirectSpecular += clearcoatInv * radiance * singleScattering;
 	reflectedLight.indirectDiffuse += multiScattering * cosineWeightedIrradiance;
 	reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance;
 
@@ -151,7 +163,7 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia
 #define RE_IndirectSpecular		RE_IndirectSpecular_Physical
 
 #define Material_BlinnShininessExponent( material )   GGXRoughnessToBlinnExponent( material.specularRoughness )
-#define Material_ClearCoat_BlinnShininessExponent( material )   GGXRoughnessToBlinnExponent( material.clearCoatRoughness )
+#define Material_Clearcoat_BlinnShininessExponent( material )   GGXRoughnessToBlinnExponent( material.clearcoatRoughness )
 
 // ref: https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
 float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {

+ 1 - 1
src/renderers/shaders/ShaderChunk/normal_fragment_begin.glsl.js

@@ -33,7 +33,7 @@ export default /* glsl */`
 
 #endif
 
-// non perturbed normal for clearcoat
+// non perturbed normal for clearcoat among others
 
 vec3 geometryNormal = normal;
 

+ 5 - 4
src/renderers/shaders/ShaderLib.js

@@ -273,10 +273,11 @@ ShaderLib.physical = {
 	uniforms: mergeUniforms( [
 		ShaderLib.standard.uniforms,
 		{
-			clearCoat: { value: 0 },
-			clearCoatRoughness: { value: 0 },
-			clearCoatNormalScale: { value: new Vector2( 1, 1 ) },
-			clearCoatNormalMap: { value: null },
+			clearcoat: { value: 0 },
+			clearcoatRoughness: { value: 0 },
+			sheen: { value: new Color( 0x000000 ) },
+			clearcoatNormalScale: { value: new Vector2( 1, 1 ) },
+			clearcoatNormalMap: { value: null },
 		}
 	] ),
 

+ 7 - 3
src/renderers/shaders/ShaderLib/meshphysical_frag.glsl.js

@@ -7,9 +7,13 @@ uniform float roughness;
 uniform float metalness;
 uniform float opacity;
 
-#ifndef STANDARD
-	uniform float clearCoat;
-	uniform float clearCoatRoughness;
+#ifdef PHYSICAL
+	uniform float clearcoat;
+	uniform float clearcoatRoughness;
+#endif
+
+#ifdef USE_SHEEN
+	uniform vec3 sheen;
 #endif
 
 varying vec3 vViewPosition;

+ 4 - 2
src/renderers/webgl/WebGLBufferRenderer.js

@@ -22,15 +22,17 @@ function WebGLBufferRenderer( gl, extensions, info, capabilities ) {
 
 	function renderInstances( geometry, start, count ) {
 
-		var extension;
+		var extension, methodName;
 
 		if ( capabilities.isWebGL2 ) {
 
 			extension = gl;
+			methodName = 'drawArraysInstanced';
 
 		} else {
 
 			extension = extensions.get( 'ANGLE_instanced_arrays' );
+			methodName = 'drawArraysInstancedANGLE';
 
 			if ( extension === null ) {
 
@@ -41,7 +43,7 @@ function WebGLBufferRenderer( gl, extensions, info, capabilities ) {
 
 		}
 
-		extension[ capabilities.isWebGL2 ? 'drawArraysInstanced' : 'drawArraysInstancedANGLE' ]( mode, start, count, geometry.maxInstancedCount );
+		extension[ methodName ]( mode, start, count, geometry.maxInstancedCount );
 
 		info.update( count, mode, geometry.maxInstancedCount );
 

+ 5 - 3
src/renderers/webgl/WebGLIndexedBufferRenderer.js

@@ -31,15 +31,17 @@ function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) {
 
 	function renderInstances( geometry, start, count ) {
 
-		var extension;
+		var extension, methodName;
 
 		if ( capabilities.isWebGL2 ) {
 
 			extension = gl;
+			methodName = 'drawElementsInstanced';
 
 		} else {
 
-			var extension = extensions.get( 'ANGLE_instanced_arrays' );
+			extension = extensions.get( 'ANGLE_instanced_arrays' );
+			methodName = 'drawElementsInstancedANGLE';
 
 			if ( extension === null ) {
 
@@ -50,7 +52,7 @@ function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) {
 
 		}
 
-		extension[ capabilities.isWebGL2 ? 'drawElementsInstanced' : 'drawElementsInstancedANGLE' ]( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );
+		extension[ methodName ]( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );
 
 		info.update( count, mode, geometry.maxInstancedCount );
 

+ 5 - 3
src/renderers/webgl/WebGLProgram.js

@@ -120,7 +120,7 @@ function generateExtensions( extensions, parameters, rendererExtensions ) {
 	extensions = extensions || {};
 
 	var chunks = [
-		( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || ( parameters.normalMap && ! parameters.objectSpaceNormalMap ) || parameters.clearCoatNormalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
+		( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || ( parameters.normalMap && ! parameters.objectSpaceNormalMap ) || parameters.clearcoatNormalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',
 		( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',
 		( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',
 		( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : ''
@@ -391,7 +391,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
 			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
-			parameters.clearCoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
+			parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
 			parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
@@ -509,12 +509,14 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters,
 			parameters.bumpMap ? '#define USE_BUMPMAP' : '',
 			parameters.normalMap ? '#define USE_NORMALMAP' : '',
 			( parameters.normalMap && parameters.objectSpaceNormalMap ) ? '#define OBJECTSPACE_NORMALMAP' : '',
-			parameters.clearCoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
+			parameters.clearcoatNormalMap ? '#define USE_CLEARCOAT_NORMALMAP' : '',
 			parameters.specularMap ? '#define USE_SPECULARMAP' : '',
 			parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',
 			parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',
 			parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
 
+			parameters.sheen ? '#define USE_SHEEN' : '',
+
 			parameters.vertexTangents ? '#define USE_TANGENT' : '',
 			parameters.vertexColors ? '#define USE_COLOR' : '',
 			parameters.vertexUvs ? '#define USE_UV' : '',

+ 7 - 4
src/renderers/webgl/WebGLPrograms.js

@@ -29,7 +29,7 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 	var parameterNames = [
 		"precision", "supportsVertexTextures", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding",
-		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "clearCoatNormalMap", "displacementMap", "specularMap",
+		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "clearcoatNormalMap", "displacementMap", "specularMap",
 		"roughnessMap", "metalnessMap", "gradientMap",
 		"alphaMap", "combine", "vertexColors", "vertexTangents", "fog", "useFog", "fogExp2",
 		"flatShading", "sizeAttenuation", "logarithmicDepthBuffer", "skinning",
@@ -37,7 +37,8 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 		"maxMorphTargets", "maxMorphNormals", "premultipliedAlpha",
 		"numDirLights", "numPointLights", "numSpotLights", "numHemiLights", "numRectAreaLights",
 		"shadowMapEnabled", "shadowMapType", "toneMapping", 'physicallyCorrectLights',
-		"alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering"
+		"alphaTest", "doubleSided", "flipSided", "numClippingPlanes", "numClipIntersection", "depthPacking", "dithering",
+		"sheen"
 	];
 
 
@@ -153,7 +154,7 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 			bumpMap: !! material.bumpMap,
 			normalMap: !! material.normalMap,
 			objectSpaceNormalMap: material.normalMapType === ObjectSpaceNormalMap,
-			clearCoatNormalMap: !! material.clearCoatNormalMap,
+			clearcoatNormalMap: !! material.clearcoatNormalMap,
 			displacementMap: !! material.displacementMap,
 			roughnessMap: !! material.roughnessMap,
 			metalnessMap: !! material.metalnessMap,
@@ -162,11 +163,13 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 			gradientMap: !! material.gradientMap,
 
+			sheen: !! material.sheen,
+
 			combine: material.combine,
 
 			vertexTangents: ( material.normalMap && material.vertexTangents ),
 			vertexColors: material.vertexColors,
-			vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearCoatNormalMap,
+			vertexUvs: !! material.map || !! material.bumpMap || !! material.normalMap || !! material.specularMap || !! material.alphaMap || !! material.emissiveMap || !! material.roughnessMap || !! material.metalnessMap || !! material.clearcoatNormalMap,
 
 			fog: !! fog,
 			useFog: material.fog,

+ 66 - 60
src/renderers/webgl/WebGLTextures.js

@@ -357,104 +357,110 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 	function setTextureCube( texture, slot ) {
 
+		if ( texture.image.length !== 6 ) return;
+
 		var textureProperties = properties.get( texture );
 
-		if ( texture.image.length === 6 ) {
+		if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
 
-			if ( texture.version > 0 && textureProperties.__version !== texture.version ) {
+			initTexture( textureProperties, texture );
 
-				initTexture( textureProperties, texture );
+			state.activeTexture( _gl.TEXTURE0 + slot );
+			state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
 
-				state.activeTexture( _gl.TEXTURE0 + slot );
-				state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
+			_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
 
-				_gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );
+			var isCompressed = ( texture && texture.isCompressedTexture );
+			var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );
 
-				var isCompressed = ( texture && texture.isCompressedTexture );
-				var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );
+			var cubeImage = [];
 
-				var cubeImage = [];
+			for ( var i = 0; i < 6; i ++ ) {
 
-				for ( var i = 0; i < 6; i ++ ) {
+				if ( ! isCompressed && ! isDataTexture ) {
 
-					if ( ! isCompressed && ! isDataTexture ) {
+					cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, capabilities.maxCubemapSize );
 
-						cubeImage[ i ] = resizeImage( texture.image[ i ], false, true, capabilities.maxCubemapSize );
+				} else {
 
-					} else {
+					cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
 
-						cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];
+				}
 
-					}
+			}
 
-				}
+			var image = cubeImage[ 0 ],
+				supportsMips = isPowerOfTwo( image ) || capabilities.isWebGL2,
+				glFormat = utils.convert( texture.format ),
+				glType = utils.convert( texture.type ),
+				glInternalFormat = getInternalFormat( glFormat, glType );
 
-				var image = cubeImage[ 0 ],
-					supportsMips = isPowerOfTwo( image ) || capabilities.isWebGL2,
-					glFormat = utils.convert( texture.format ),
-					glType = utils.convert( texture.type ),
-					glInternalFormat = getInternalFormat( glFormat, glType );
+			setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips );
 
-				setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, supportsMips );
+			var mipmaps;
 
-				var mipmaps = texture.mipmaps;
+			if ( isCompressed ) {
 
 				for ( var i = 0; i < 6; i ++ ) {
 
-					if ( ! isCompressed ) {
+					mipmaps = cubeImage[ i ].mipmaps;
+
+					for ( var j = 0; j < mipmaps.length; j ++ ) {
 
-						if ( isDataTexture ) {
+						var mipmap = mipmaps[ j ];
 
-							state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
+						if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {
 
-							for ( var j = 0; j < mipmaps.length; ++ j ) {
+							if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
 
-								var mipmap = mipmaps[ j ];
-								var image = mipmap.image[ i ].image;
+								state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );
 
-								state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, image.width, image.height, 0, glFormat, glType, image.data );
+							} else {
+
+								console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );
 
 							}
 
 						} else {
 
-							state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] );
+							state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
 
-							for ( var j = 0; j < mipmaps.length; ++ j ) {
+						}
 
-								var mipmap = mipmaps[ j ];
+					}
 
-								state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] );
+				}
 
-							}
+				textureProperties.__maxMipLevel = mipmaps.length - 1;
 
-						}
+			} else {
 
-					} else {
+				mipmaps = texture.mipmaps;
 
-						var mipmaps = cubeImage[ i ].mipmaps;
+				for ( var i = 0; i < 6; i ++ ) {
 
-						for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {
+					if ( isDataTexture ) {
 
-							var mipmap = mipmaps[ j ];
+						state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );
 
-							if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {
+						for ( var j = 0; j < mipmaps.length; j ++ ) {
 
-								if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {
+							var mipmap = mipmaps[ j ];
+							var mipmapImage = mipmap.image[ i ].image;
 
-									state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, mipmap.data );
+							state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, mipmapImage.width, mipmapImage.height, 0, glFormat, glType, mipmapImage.data );
 
-								} else {
+						}
 
-									console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );
+					} else {
 
-								}
+						state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, glFormat, glType, cubeImage[ i ] );
 
-							} else {
+						for ( var j = 0; j < mipmaps.length; j ++ ) {
 
-								state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glInternalFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );
+							var mipmap = mipmaps[ j ];
 
-							}
+							state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j + 1, glInternalFormat, glFormat, glType, mipmap.image[ i ] );
 
 						}
 
@@ -462,25 +468,25 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 				}
 
-				textureProperties.__maxMipLevel = isCompressed ? mipmaps.length - 1 : mipmaps.length;
+				textureProperties.__maxMipLevel = mipmaps.length;
 
-				if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
+			}
 
-					// We assume images for cube map have the same size.
-					generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height );
+			if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
 
-				}
+				// We assume images for cube map have the same size.
+				generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height );
 
-				textureProperties.__version = texture.version;
+			}
 
-				if ( texture.onUpdate ) texture.onUpdate( texture );
+			textureProperties.__version = texture.version;
 
-			} else {
+			if ( texture.onUpdate ) texture.onUpdate( texture );
 
-				state.activeTexture( _gl.TEXTURE0 + slot );
-				state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
+		} else {
 
-			}
+			state.activeTexture( _gl.TEXTURE0 + slot );
+			state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );
 
 		}
 

+ 0 - 13
test/unit/src/loaders/Loader.tests.js

@@ -29,19 +29,6 @@ export default QUnit.module( 'Loaders', () => {
 
 		} );
 
-		// PUBLIC STUFF
-		QUnit.todo( "initMaterials", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
-		QUnit.todo( "createMaterial", ( assert ) => {
-
-			assert.ok( false, "everything's gonna be alright" );
-
-		} );
-
 	} );
 
 } );

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