2
0
Эх сурвалжийг харах

Editor: Refactored SidebarMaterial (#22194)

* Editor: Added SidebarMaterialColorProperty.

* Editor: Added SidebarMaterialNumberProperty.

* Editor: Added SidebarMaterialBooleanProperty.

* Editor: Added SidebarMaterialMapProperty.

* Editor: Added *Scale support to SidebarMaterialMapProperty.

* Editor: Cleaned up envMap/reflectivity code.

* Editor: String keys clean up.

* Editor: Clean up.

* Editor: Removed epsilon check in SidebarMaterialNumberProperty.

* Editor: Improved intensity code in SidebarMaterialColorProperty.

* Editor: Added Intensity support to SidebarMaterialMapProperty.

* Editor: Removed unused code from SidebarMaterial.

* Editor: Cleaned up size property in SidebarMaterial.

* Editor: Clean up.

* Editor: Added SidebarMaterialConstantProperty.

* Editor: Removed constants translations.

* Editor: Added SidebarMaterialProgram.

* Editor: Fixed reflectivity property.

* Editor: Removed unused code.

* Editor: Fixed material size.

* Editor: Clean up.

* Editor: Improved SidebarMaterialMapProperty.

* Editor: Clean up.

* UI: Removed redundant method

* Editor: Reordered material properties
Mr.doob 4 жил өмнө
parent
commit
f952a0e4f9

+ 63 - 0
editor/js/Sidebar.Material.BooleanProperty.js

@@ -0,0 +1,63 @@
+import { UICheckbox, UIRow, UIText } from './libs/ui.js';
+import { SetMaterialValueCommand } from './commands/SetMaterialValueCommand.js';
+
+function SidebarMaterialBooleanProperty( editor, property, name ) {
+
+	const signals = editor.signals;
+
+	const container = new UIRow();
+	container.add( new UIText( name ).setWidth( '90px' ) );
+
+	const boolean = new UICheckbox().setLeft( '100px' ).onChange( onChange );
+	container.add( boolean );
+
+	let object = null;
+	let material = null;
+
+	function onChange() {
+
+		if ( material[ property ] !== boolean.getValue() ) {
+
+			editor.execute( new SetMaterialValueCommand( editor, object, property, boolean.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+
+		}
+
+	}
+
+	function update() {
+
+		if ( object === null ) return;
+		if ( object.material === undefined ) return;
+
+		material = object.material;
+
+		if ( property in material ) {
+
+			boolean.setValue( material[ property ] );
+			container.setDisplay( '' );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	}
+
+	//
+
+	signals.objectSelected.add( function ( selected ) {
+
+		object = selected;
+
+		update();
+
+	} );
+
+	signals.materialChanged.add( update );
+
+	return container;
+
+}
+
+export { SidebarMaterialBooleanProperty };

+ 90 - 0
editor/js/Sidebar.Material.ColorProperty.js

@@ -0,0 +1,90 @@
+import { UIColor, UINumber, UIRow, UIText } from './libs/ui.js';
+import { SetMaterialColorCommand } from './commands/SetMaterialColorCommand.js';
+import { SetMaterialValueCommand } from './commands/SetMaterialValueCommand.js';
+
+function SidebarMaterialColorProperty( editor, property, name ) {
+
+	const signals = editor.signals;
+
+	const container = new UIRow();
+	container.add( new UIText( name ).setWidth( '90px' ) );
+
+	const color = new UIColor().onInput( onChange );
+	container.add( color );
+
+	let intensity;
+
+	if ( property === 'emissive' ) {
+
+		intensity = new UINumber().setWidth( '30px' ).onChange( onChange );
+		container.add( intensity );
+
+	}
+
+	let object = null;
+	let material = null;
+
+	function onChange() {
+
+		if ( material[ property ].getHex() !== color.getHexValue() ) {
+
+			editor.execute( new SetMaterialColorCommand( editor, object, property, color.getHexValue(), 0 /* TODO: currentMaterialSlot */ ) );
+
+		}
+
+		if ( intensity !== undefined ) {
+
+			if ( material[ `${ property }Intensity` ] !== intensity.getValue() ) {
+
+				editor.execute( new SetMaterialValueCommand( editor, object, `${ property }Intensity`, intensity.getValue(), /* TODO: currentMaterialSlot*/ 0 ) );
+
+			}
+
+		}
+
+	}
+
+	function update() {
+
+		if ( object === null ) return;
+		if ( object.material === undefined ) return;
+
+		material = object.material;
+
+		if ( property in material ) {
+
+			color.setHexValue( material[ property ].getHexString() );
+
+			if ( intensity !== undefined ) {
+
+				intensity.setValue( material[ `${ property }Intensity` ] );
+
+			}
+
+			container.setDisplay( '' );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	}
+
+	//
+
+	signals.objectSelected.add( function ( selected ) {
+
+		object = selected;
+
+		update();
+
+	} );
+
+	signals.materialChanged.add( update );
+
+	return container;
+
+}
+
+export { SidebarMaterialColorProperty };

+ 65 - 0
editor/js/Sidebar.Material.ConstantProperty.js

@@ -0,0 +1,65 @@
+import { UIRow, UISelect, UIText } from './libs/ui.js';
+import { SetMaterialValueCommand } from './commands/SetMaterialValueCommand.js';
+
+function SidebarMaterialConstantProperty( editor, property, name, options ) {
+
+	const signals = editor.signals;
+
+	const container = new UIRow();
+	container.add( new UIText( name ).setWidth( '90px' ) );
+
+	const constant = new UISelect().setOptions( options ).onChange( onChange );
+	container.add( constant );
+
+	let object = null;
+	let material = null;
+
+	function onChange() {
+
+		const value = parseInt( constant.getValue() );
+
+		if ( material[ property ] !== value ) {
+
+			editor.execute( new SetMaterialValueCommand( editor, object, property, value, 0 /* TODO: currentMaterialSlot */ ) );
+
+		}
+
+	}
+
+	function update() {
+
+		if ( object === null ) return;
+		if ( object.material === undefined ) return;
+
+		material = object.material;
+
+		if ( property in material ) {
+
+			constant.setValue( material[ property ] );
+			container.setDisplay( '' );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	}
+
+	//
+
+	signals.objectSelected.add( function ( selected ) {
+
+		object = selected;
+
+		update();
+
+	} );
+
+	signals.materialChanged.add( update );
+
+	return container;
+
+}
+
+export { SidebarMaterialConstantProperty };

+ 193 - 0
editor/js/Sidebar.Material.MapProperty.js

@@ -0,0 +1,193 @@
+import * as THREE from '../../build/three.module.js';
+
+import { UICheckbox, UINumber, UIRow, UIText } from './libs/ui.js';
+import { UITexture } from './libs/ui.three.js';
+import { SetMaterialMapCommand } from './commands/SetMaterialMapCommand.js';
+import { SetMaterialValueCommand } from './commands/SetMaterialValueCommand.js';
+import { SetMaterialVectorCommand } from './commands/SetMaterialVectorCommand.js';
+
+function SidebarMaterialMapProperty( editor, property, name ) {
+
+	const signals = editor.signals;
+
+	const container = new UIRow();
+	container.add( new UIText( name ).setWidth( '90px' ) );
+
+	const enabled = new UICheckbox( false ).setMarginRight( '8px' ).onChange( onChange );
+	container.add( enabled );
+
+	const map = new UITexture().onChange( onMapChange );
+	container.add( map );
+
+	const mapType = property.replace( 'Map', '' );
+
+	let intensity;
+
+	if ( property === 'aoMap' ) {
+
+		intensity = new UINumber().setWidth( '30px' ).onChange( onIntensityChange );
+		container.add( intensity );
+
+	}
+
+	let scale;
+
+	if ( property === 'bumpMap' || property === 'displacementMap' ) {
+
+		scale = new UINumber().setWidth( '30px' ).onChange( onScaleChange );
+		container.add( scale );
+
+	}
+
+	let scaleX, scaleY;
+
+	if ( property === 'normalMap' || property === 'clearcoatNormalMap' ) {
+
+		scaleX = new UINumber().setWidth( '30px' ).onChange( onScaleXYChange );
+		container.add( scaleX );
+
+		scaleY = new UINumber().setWidth( '30px' ).onChange( onScaleXYChange );
+		container.add( scaleY );
+
+	}
+
+	let object = null;
+	let material = null;
+
+	function onChange() {
+
+		const newMap = enabled.getValue() ? map.getValue() : null;
+
+		if ( material[ 'property' ] !== newMap ) {
+
+			const geometry = object.geometry;
+
+			if ( newMap !== null && geometry.isBufferGeometry && geometry.attributes.uv === undefined ) {
+
+				console.warn( 'Geometry doesn\'t have uvs:', geometry );
+
+			}
+
+			editor.execute( new SetMaterialMapCommand( editor, object, property, newMap, 0 /* TODO: currentMaterialSlot */ ) );
+
+		}
+
+	}
+
+	function onMapChange( texture ) {
+
+		if ( texture !== null ) {
+
+			if ( texture.isDataTexture !== true && texture.encoding !== THREE.sRGBEncoding ) {
+
+				texture.encoding = THREE.sRGBEncoding;
+				material.needsUpdate = true;
+
+			}
+
+		}
+
+		enabled.setDisabled( false );
+
+		onChange();
+
+	}
+
+	function onIntensityChange() {
+
+		if ( material[ `${ property }Intensity` ] !== intensity.getValue() ) {
+
+			editor.execute( new SetMaterialValueCommand( editor, object, `${ property }Intensity`, intensity.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+
+		}
+
+	}
+
+	function onScaleChange() {
+
+		if ( material[ `${ mapType }Scale` ] !== scale.getValue() ) {
+
+			editor.execute( new SetMaterialValueCommand( editor, object, `${ mapType }Scale`, scale.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+
+		}
+
+	}
+
+	function onScaleXYChange() {
+
+		const value = [ scaleX.getValue(), scaleY.getValue() ];
+
+		if ( material[ `${ mapType }Scale` ].x !== value[ 0 ] || material[ `${ mapType }Scale` ].y !== value[ 1 ] ) {
+
+			editor.execute( new SetMaterialVectorCommand( editor, object, `${ mapType }Scale`, value, 0 /* TODOL currentMaterialSlot */ ) );
+
+		}
+
+	}
+
+	function update() {
+
+		if ( object === null ) return;
+		if ( object.material === undefined ) return;
+
+		material = object.material;
+
+		if ( property in material ) {
+
+			if ( material[ property ] !== null ) {
+
+				map.setValue( material[ property ] );
+
+			}
+
+			enabled.setValue( material[ property ] !== null );
+			enabled.setDisabled( map.getValue() === null );
+
+			if ( intensity !== undefined ) {
+
+				intensity.setValue( material[ `${ property }Intensity` ] );
+
+			}
+
+			if ( scale !== undefined ) {
+
+				scale.setValue( material[ `${ mapType }Scale` ] );
+
+			}
+
+			if ( scaleX !== undefined ) {
+
+				scaleX.setValue( material[ `${ mapType }Scale` ].x );
+				scaleY.setValue( material[ `${ mapType }Scale` ].y );
+
+			}
+
+			container.setDisplay( '' );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	}
+
+	//
+
+	signals.objectSelected.add( function ( selected ) {
+
+		object = selected;
+
+		map.setValue( null );
+
+		update();
+
+	} );
+
+	signals.materialChanged.add( update );
+
+	return container;
+
+}
+
+export { SidebarMaterialMapProperty };

+ 63 - 0
editor/js/Sidebar.Material.NumberProperty.js

@@ -0,0 +1,63 @@
+import { UINumber, UIRow, UIText } from './libs/ui.js';
+import { SetMaterialValueCommand } from './commands/SetMaterialValueCommand.js';
+
+function SidebarMaterialNumberProperty( editor, property, name, range = [ - Infinity, Infinity ] ) {
+
+	const signals = editor.signals;
+
+	const container = new UIRow();
+	container.add( new UIText( name ).setWidth( '90px' ) );
+
+	const number = new UINumber().setWidth( '60px' ).setRange( range[ 0 ], range[ 1 ] ).onChange( onChange );
+	container.add( number );
+
+	let object = null;
+	let material = null;
+
+	function onChange() {
+
+		if ( material[ property ] !== number.getValue() ) {
+
+			editor.execute( new SetMaterialValueCommand( editor, object, property, number.getValue(), 0 /* TODO: currentMaterialSlot */ ) );
+
+		}
+
+	}
+
+	function update() {
+
+		if ( object === null ) return;
+		if ( object.material === undefined ) return;
+
+		material = object.material;
+
+		if ( property in material ) {
+
+			number.setValue( material[ property ] );
+			container.setDisplay( '' );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	}
+
+	//
+
+	signals.objectSelected.add( function ( selected ) {
+
+		object = selected;
+
+		update();
+
+	} );
+
+	signals.materialChanged.add( update );
+
+	return container;
+
+}
+
+export { SidebarMaterialNumberProperty };

+ 76 - 0
editor/js/Sidebar.Material.Program.js

@@ -0,0 +1,76 @@
+import { UIButton, UIRow, UIText } from './libs/ui.js';
+
+function SidebarMaterialProgram( editor, property ) {
+
+	const signals = editor.signals;
+	const strings = editor.strings;
+
+	let object = null;
+	let material = null;
+
+	const container = new UIRow();
+	container.add( new UIText( strings.getKey( 'sidebar/material/program' ) ).setWidth( '90px' ) );
+
+	const programInfo = new UIButton( strings.getKey( 'sidebar/material/info' ) );
+	programInfo.setMarginRight( '4px' );
+	programInfo.onClick( function () {
+
+		signals.editScript.dispatch( object, 'programInfo' );
+
+	} );
+	container.add( programInfo );
+
+	const programVertex = new UIButton( strings.getKey( 'sidebar/material/vertex' ) );
+	programVertex.setMarginRight( '4px' );
+	programVertex.onClick( function () {
+
+		signals.editScript.dispatch( object, 'vertexShader' );
+
+	} );
+	container.add( programVertex );
+
+	const programFragment = new UIButton( strings.getKey( 'sidebar/material/fragment' ) );
+	programFragment.setMarginRight( '4px' );
+	programFragment.onClick( function () {
+
+		signals.editScript.dispatch( object, 'fragmentShader' );
+
+	} );
+	container.add( programFragment );
+
+	function update() {
+
+		if ( object === null ) return;
+		if ( object.material === undefined ) return;
+
+		material = object.material;
+
+		if ( property in material ) {
+
+			container.setDisplay( '' );
+
+		} else {
+
+			container.setDisplay( 'none' );
+
+		}
+
+	}
+
+	//
+
+	signals.objectSelected.add( function ( selected ) {
+
+		object = selected;
+
+		update();
+
+	} );
+
+	signals.materialChanged.add( update );
+
+	return container;
+
+}
+
+export { SidebarMaterialProgram };

+ 246 - 1499
editor/js/Sidebar.Material.js

@@ -1,57 +1,38 @@
 import * as THREE from '../../build/three.module.js';
 
-import { UIButton, UICheckbox, UIColor, UIInput, UINumber, UIPanel, UIRow, UISelect, UIText } from './libs/ui.js';
-import { UITexture } from './libs/ui.three.js';
+import { UIButton, UIInput, UIPanel, UIRow, UISelect, UIText } from './libs/ui.js';
 
 import { SetMaterialCommand } from './commands/SetMaterialCommand.js';
-import { SetMaterialColorCommand } from './commands/SetMaterialColorCommand.js';
-import { SetMaterialMapCommand } from './commands/SetMaterialMapCommand.js';
 import { SetMaterialValueCommand } from './commands/SetMaterialValueCommand.js';
-import { SetMaterialVectorCommand } from './commands/SetMaterialVectorCommand.js';
 
-var materialClasses = {
-	'LineBasicMaterial': THREE.LineBasicMaterial,
-	'LineDashedMaterial': THREE.LineDashedMaterial,
-	'MeshBasicMaterial': THREE.MeshBasicMaterial,
-	'MeshDepthMaterial': THREE.MeshDepthMaterial,
-	'MeshNormalMaterial': THREE.MeshNormalMaterial,
-	'MeshLambertMaterial': THREE.MeshLambertMaterial,
-	'MeshMatcapMaterial': THREE.MeshMatcapMaterial,
-	'MeshPhongMaterial': THREE.MeshPhongMaterial,
-	'MeshToonMaterial': THREE.MeshToonMaterial,
-	'MeshStandardMaterial': THREE.MeshStandardMaterial,
-	'MeshPhysicalMaterial': THREE.MeshPhysicalMaterial,
-	'RawShaderMaterial': THREE.RawShaderMaterial,
-	'ShaderMaterial': THREE.ShaderMaterial,
-	'ShadowMaterial': THREE.ShadowMaterial,
-	'SpriteMaterial': THREE.SpriteMaterial,
-	'PointsMaterial': THREE.PointsMaterial
-};
+import { SidebarMaterialBooleanProperty } from './Sidebar.Material.BooleanProperty.js';
+import { SidebarMaterialColorProperty } from './Sidebar.Material.ColorProperty.js';
+import { SidebarMaterialConstantProperty } from './Sidebar.Material.ConstantProperty.js';
+import { SidebarMaterialMapProperty } from './Sidebar.Material.MapProperty.js';
+import { SidebarMaterialNumberProperty } from './Sidebar.Material.NumberProperty.js';
+import { SidebarMaterialProgram } from './Sidebar.Material.Program.js';
 
 function SidebarMaterial( editor ) {
 
-	var strings = editor.strings;
-
-	var signals = editor.signals;
+	const signals = editor.signals;
+	const strings = editor.strings;
 
-	var currentObject;
+	let currentObject;
 
-	var currentMaterialSlot = 0;
+	let currentMaterialSlot = 0;
 
-	var epsilon = 0.01 - Number.EPSILON;
-
-	var container = new UIPanel();
+	const container = new UIPanel();
 	container.setBorderTop( '0' );
 	container.setDisplay( 'none' );
 	container.setPaddingTop( '20px' );
 
 	// Current material slot
 
-	var materialSlotRow = new UIRow();
+	const materialSlotRow = new UIRow();
 
 	materialSlotRow.add( new UIText( strings.getKey( 'sidebar/material/slot' ) ).setWidth( '90px' ) );
 
-	var materialSlotSelect = new UISelect().setWidth( '170px' ).setFontSize( '12px' ).onChange( update );
+	const materialSlotSelect = new UISelect().setWidth( '170px' ).setFontSize( '12px' ).onChange( update );
 	materialSlotSelect.setOptions( { 0: '' } ).setValue( 0 );
 	materialSlotRow.add( materialSlotSelect );
 
@@ -59,8 +40,8 @@ function SidebarMaterial( editor ) {
 
 	// type
 
-	var materialClassRow = new UIRow();
-	var materialClass = new UISelect().setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
+	const materialClassRow = new UIRow();
+	const materialClass = new UISelect().setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
 
 	materialClassRow.add( new UIText( strings.getKey( 'sidebar/material/type' ) ).setWidth( '90px' ) );
 	materialClassRow.add( materialClass );
@@ -69,9 +50,10 @@ function SidebarMaterial( editor ) {
 
 	// uuid
 
-	var materialUUIDRow = new UIRow();
-	var materialUUID = new UIInput().setWidth( '102px' ).setFontSize( '12px' ).setDisabled( true );
-	var materialUUIDRenew = new UIButton( strings.getKey( 'sidebar/material/new' ) ).setMarginLeft( '7px' ).onClick( function () {
+	const materialUUIDRow = new UIRow();
+	const materialUUID = new UIInput().setWidth( '102px' ).setFontSize( '12px' ).setDisabled( true );
+	const materialUUIDRenew = new UIButton( strings.getKey( 'sidebar/material/new' ) ).setMarginLeft( '7px' );
+	materialUUIDRenew.onClick( function () {
 
 		materialUUID.setValue( THREE.MathUtils.generateUUID() );
 		update();
@@ -86,8 +68,8 @@ function SidebarMaterial( editor ) {
 
 	// name
 
-	var materialNameRow = new UIRow();
-	var materialName = new UIInput().setWidth( '150px' ).setFontSize( '12px' ).onChange( function () {
+	const materialNameRow = new UIRow();
+	const materialName = new UIInput().setWidth( '150px' ).setFontSize( '12px' ).onChange( function () {
 
 		editor.execute( new SetMaterialValueCommand( editor, editor.selected, 'name', materialName.getValue(), currentMaterialSlot ) );
 
@@ -100,511 +82,230 @@ function SidebarMaterial( editor ) {
 
 	// program
 
-	var materialProgramRow = new UIRow();
-	materialProgramRow.add( new UIText( strings.getKey( 'sidebar/material/program' ) ).setWidth( '90px' ) );
-
-	var materialProgramInfo = new UIButton( strings.getKey( 'sidebar/material/info' ) );
-	materialProgramInfo.setMarginRight( '4px' );
-	materialProgramInfo.onClick( function () {
-
-		signals.editScript.dispatch( currentObject, 'programInfo' );
-
-	} );
-	materialProgramRow.add( materialProgramInfo );
-
-	var materialProgramVertex = new UIButton( strings.getKey( 'sidebar/material/vertex' ) );
-	materialProgramVertex.setMarginRight( '4px' );
-	materialProgramVertex.onClick( function () {
+	const materialProgram = new SidebarMaterialProgram( editor, 'vertexShader' );
+	container.add( materialProgram );
 
-		signals.editScript.dispatch( currentObject, 'vertexShader' );
+	// color
 
-	} );
-	materialProgramRow.add( materialProgramVertex );
+	const materialColor = new SidebarMaterialColorProperty( editor, 'color', strings.getKey( 'sidebar/material/color' ) );
+	container.add( materialColor );
 
-	var materialProgramFragment = new UIButton( strings.getKey( 'sidebar/material/fragment' ) );
-	materialProgramFragment.setMarginRight( '4px' );
-	materialProgramFragment.onClick( function () {
+	// specular
 
-		signals.editScript.dispatch( currentObject, 'fragmentShader' );
+	const materialSpecular = new SidebarMaterialColorProperty( editor, 'specular', strings.getKey( 'sidebar/material/specular' ) );
+	container.add( materialSpecular );
 
-	} );
-	materialProgramRow.add( materialProgramFragment );
+	// shininess
 
-	container.add( materialProgramRow );
+	const materialShininess = new SidebarMaterialNumberProperty( editor, 'shininess', strings.getKey( 'sidebar/material/shininess' ) );
+	container.add( materialShininess );
 
-	// color
+	// emissive
 
-	var materialColorRow = new UIRow();
-	var materialColor = new UIColor().onInput( update );
+	const materialEmissive = new SidebarMaterialColorProperty( editor, 'emissive', strings.getKey( 'sidebar/material/emissive' ) );
+	container.add( materialEmissive );
 
-	materialColorRow.add( new UIText( strings.getKey( 'sidebar/material/color' ) ).setWidth( '90px' ) );
-	materialColorRow.add( materialColor );
+	// reflectivity
 
-	container.add( materialColorRow );
+	const materialReflectivity = new SidebarMaterialNumberProperty( editor, 'reflectivity', strings.getKey( 'sidebar/material/reflectivity' ) );
+	container.add( materialReflectivity );
 
 	// roughness
 
-	var materialRoughnessRow = new UIRow();
-	var materialRoughness = new UINumber( 0.5 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
-
-	materialRoughnessRow.add( new UIText( strings.getKey( 'sidebar/material/roughness' ) ).setWidth( '90px' ) );
-	materialRoughnessRow.add( materialRoughness );
-
-	container.add( materialRoughnessRow );
+	const materialRoughness = new SidebarMaterialNumberProperty( editor, 'roughness', strings.getKey( 'sidebar/material/roughness' ), [ 0, 1 ] );
+	container.add( materialRoughness );
 
 	// metalness
 
-	var materialMetalnessRow = new UIRow();
-	var materialMetalness = new UINumber( 0.5 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
-
-	materialMetalnessRow.add( new UIText( strings.getKey( 'sidebar/material/metalness' ) ).setWidth( '90px' ) );
-	materialMetalnessRow.add( materialMetalness );
-
-	container.add( materialMetalnessRow );
-
-	/*
-	// sheen
-
-	var materialSheenRow = new UIRow();
-	var materialSheenEnabled = new UICheckbox( false ).onChange( update );
-	var materialSheen = new UIColor().setHexValue( 0x000000 ).onInput( update );
-
-	materialSheenRow.add( new UIText( strings.getKey( 'sidebar/material/sheen' ) ).setWidth( '90px' ) )
-	materialSheenRow.add( materialSheenEnabled );
-	materialSheenRow.add( materialSheen );
-
-	container.add( materialSheenRow );
-	*/
+	const materialMetalness = new SidebarMaterialNumberProperty( editor, 'metalness', strings.getKey( 'sidebar/material/metalness' ), [ 0, 1 ] );
+	container.add( materialMetalness );
 
 	// transmission
 
-	var materialTransmissionRow = new UIRow();
-	var materialTransmission = new UINumber( 1 ).setWidth( '30px' ).setRange( 0, 1 ).onChange( update );
-
-	materialTransmissionRow.add( new UIText( strings.getKey( 'sidebar/material/transmission' ) ).setWidth( '90px' ) );
-	materialTransmissionRow.add( materialTransmission );
-
-	container.add( materialTransmissionRow );
-
-	// emissive
-
-	var materialEmissiveRow = new UIRow();
-	var materialEmissive = new UIColor().setHexValue( 0x000000 ).onInput( update );
-	var materialEmissiveIntensity = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-
-	materialEmissiveRow.add( new UIText( strings.getKey( 'sidebar/material/emissive' ) ).setWidth( '90px' ) );
-	materialEmissiveRow.add( materialEmissive );
-	materialEmissiveRow.add( materialEmissiveIntensity );
-
-	container.add( materialEmissiveRow );
-
-	// specular
-
-	var materialSpecularRow = new UIRow();
-	var materialSpecular = new UIColor().setHexValue( 0x111111 ).onInput( update );
-
-	materialSpecularRow.add( new UIText( strings.getKey( 'sidebar/material/specular' ) ).setWidth( '90px' ) );
-	materialSpecularRow.add( materialSpecular );
-
-	container.add( materialSpecularRow );
-
-	// shininess
-
-	var materialShininessRow = new UIRow();
-	var materialShininess = new UINumber( 30 ).onChange( update );
-
-	materialShininessRow.add( new UIText( strings.getKey( 'sidebar/material/shininess' ) ).setWidth( '90px' ) );
-	materialShininessRow.add( materialShininess );
-
-	container.add( materialShininessRow );
+	const materialTransmission = new SidebarMaterialNumberProperty( editor, 'transmission', strings.getKey( 'sidebar/material/transmission' ), [ 0, 1 ] );
+	container.add( materialTransmission );
 
 	// clearcoat
 
-	var materialClearcoatRow = new UIRow();
-	var materialClearcoat = new UINumber( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
-
-	materialClearcoatRow.add( new UIText( strings.getKey( 'sidebar/material/clearcoat' ) ).setWidth( '90px' ) );
-	materialClearcoatRow.add( materialClearcoat );
-
-	container.add( materialClearcoatRow );
+	const materialClearcoat = new SidebarMaterialNumberProperty( editor, 'clearcoat', strings.getKey( 'sidebar/material/clearcoat' ), [ 0, 1 ] );
+	container.add( materialClearcoat );
 
 	// clearcoatRoughness
 
-	var materialClearcoatRoughnessRow = new UIRow();
-	var materialClearcoatRoughness = new UINumber( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
-
-	materialClearcoatRoughnessRow.add( new UIText( strings.getKey( 'sidebar/material/clearcoatroughness' ) ).setWidth( '90px' ) );
-	materialClearcoatRoughnessRow.add( materialClearcoatRoughness );
-
-	container.add( materialClearcoatRoughnessRow );
+	const materialClearcoatRoughness = new SidebarMaterialNumberProperty( editor, 'clearcoatRoughness', strings.getKey( 'sidebar/material/clearcoatroughness' ), [ 0, 1 ] );
+	container.add( materialClearcoatRoughness );
 
 	// vertex colors
 
-	var materialVertexColorsRow = new UIRow();
-	var materialVertexColors = new UICheckbox( false ).onChange( update );
-
-	materialVertexColorsRow.add( new UIText( strings.getKey( 'sidebar/material/vertexcolors' ) ).setWidth( '90px' ) );
-	materialVertexColorsRow.add( materialVertexColors );
-
-	container.add( materialVertexColorsRow );
+	const materialVertexColors = new SidebarMaterialBooleanProperty( editor, 'vertexColors', strings.getKey( 'sidebar/material/vertexcolors' ) );
+	container.add( materialVertexColors );
 
 	// depth packing
 
-	var materialDepthPackingRow = new UIRow();
-	var materialDepthPacking = new UISelect().setOptions( {
-		[ THREE.BasicDepthPacking ]: 'BasicDepthPacking',
-		[ THREE.RGBADepthPacking ]: 'RGBADepthPacking'
-	} );
-	materialDepthPacking.onChange( update );
-
-	materialDepthPackingRow.add( new UIText( strings.getKey( 'sidebar/material/depthPacking' ) ).setWidth( '90px' ) );
-	materialDepthPackingRow.add( materialDepthPacking );
+	const materialDepthPackingOptions = {
+		[ THREE.BasicDepthPacking ]: 'Basic',
+		[ THREE.RGBADepthPacking ]: 'RGBA'
+	};
 
-	container.add( materialDepthPackingRow );
+	const materialDepthPacking = new SidebarMaterialConstantProperty( editor, 'depthPacking', strings.getKey( 'sidebar/material/depthPacking' ), materialDepthPackingOptions );
+	container.add( materialDepthPacking );
 
 	// map
 
-	var materialMapRow = new UIRow();
-	var materialMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialMap = new UITexture().onChange( updateMaterial );
+	const materialMap = new SidebarMaterialMapProperty( editor, 'map', strings.getKey( 'sidebar/material/map' ) );
+	container.add( materialMap );
 
-	materialMapRow.add( new UIText( strings.getKey( 'sidebar/material/map' ) ).setWidth( '90px' ) );
-	materialMapRow.add( materialMapEnabled );
-	materialMapRow.add( materialMap );
+	// specular map
 
-	container.add( materialMapRow );
+	const materialSpecularMap = new SidebarMaterialMapProperty( editor, 'specularMap', strings.getKey( 'sidebar/material/specularmap' ) );
+	container.add( materialSpecularMap );
 
-	// matcap map
+	// emissive map
 
-	var materialMatcapMapRow = new UIRow();
-	var materialMatcapMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialMatcapMap = new UITexture().onChange( update );
+	const materialEmissiveMap = new SidebarMaterialMapProperty( editor, 'emissiveMap', strings.getKey( 'sidebar/material/emissivemap' ) );
+	container.add( materialEmissiveMap );
 
-	materialMatcapMapRow.add( new UIText( strings.getKey( 'sidebar/material/matcap' ) ).setWidth( '90px' ) );
-	materialMatcapMapRow.add( materialMatcapMapEnabled );
-	materialMatcapMapRow.add( materialMatcapMap );
+	// matcap map
 
-	container.add( materialMatcapMapRow );
+	const materialMatcapMap = new SidebarMaterialMapProperty( editor, 'matcap', strings.getKey( 'sidebar/material/matcap' ) );
+	container.add( materialMatcapMap );
 
 	// alpha map
 
-	var materialAlphaMapRow = new UIRow();
-	var materialAlphaMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialAlphaMap = new UITexture().onChange( update );
-
-	materialAlphaMapRow.add( new UIText( strings.getKey( 'sidebar/material/alphamap' ) ).setWidth( '90px' ) );
-	materialAlphaMapRow.add( materialAlphaMapEnabled );
-	materialAlphaMapRow.add( materialAlphaMap );
-
-	container.add( materialAlphaMapRow );
+	const materialAlphaMap = new SidebarMaterialMapProperty( editor, 'alphaMap', strings.getKey( 'sidebar/material/alphamap' ) );
+	container.add( materialAlphaMap );
 
 	// bump map
 
-	var materialBumpMapRow = new UIRow();
-	var materialBumpMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialBumpMap = new UITexture().onChange( update );
-	var materialBumpScale = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-
-	materialBumpMapRow.add( new UIText( strings.getKey( 'sidebar/material/bumpmap' ) ).setWidth( '90px' ) );
-	materialBumpMapRow.add( materialBumpMapEnabled );
-	materialBumpMapRow.add( materialBumpMap );
-	materialBumpMapRow.add( materialBumpScale );
-
-	container.add( materialBumpMapRow );
+	const materialBumpMap = new SidebarMaterialMapProperty( editor, 'bumpMap', strings.getKey( 'sidebar/material/bumpmap' ) );
+	container.add( materialBumpMap );
 
 	// normal map
 
-	var materialNormalMapRow = new UIRow();
-	var materialNormalMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialNormalMap = new UITexture().onChange( update );
-	var materialNormalScaleX = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-	var materialNormalScaleY = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-
-	materialNormalMapRow.add( new UIText( strings.getKey( 'sidebar/material/normalmap' ) ).setWidth( '90px' ) );
-	materialNormalMapRow.add( materialNormalMapEnabled );
-	materialNormalMapRow.add( materialNormalMap );
-	materialNormalMapRow.add( materialNormalScaleX );
-	materialNormalMapRow.add( materialNormalScaleY );
-
-	container.add( materialNormalMapRow );
+	const materialNormalMap = new SidebarMaterialMapProperty( editor, 'normalMap', strings.getKey( 'sidebar/material/normalmap' ) );
+	container.add( materialNormalMap );
 
 	// clearcoat normal map
 
-	var materialClearcoatNormalMapRow = new UIRow();
-	var materialClearcoatNormalMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialClearcoatNormalMap = new UITexture().onChange( update );
-	var materialClearcoatNormalScaleX = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-	var materialClearcoatNormalScaleY = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-
-	materialClearcoatNormalMapRow.add( new UIText( strings.getKey( 'sidebar/material/clearcoatnormalmap' ) ).setWidth( '90px' ) );
-	materialClearcoatNormalMapRow.add( materialClearcoatNormalMapEnabled );
-	materialClearcoatNormalMapRow.add( materialClearcoatNormalMap );
-	materialClearcoatNormalMapRow.add( materialClearcoatNormalScaleX );
-	materialClearcoatNormalMapRow.add( materialClearcoatNormalScaleY );
-
-	container.add( materialClearcoatNormalMapRow );
+	const materialClearcoatNormalMap = new SidebarMaterialMapProperty( editor, 'clearcoatNormalMap', strings.getKey( 'sidebar/material/clearcoatnormalmap' ) );
+	container.add( materialClearcoatNormalMap );
 
 	// displacement map
 
-	var materialDisplacementMapRow = new UIRow();
-	var materialDisplacementMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialDisplacementMap = new UITexture().onChange( update );
-	var materialDisplacementScale = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-
-	materialDisplacementMapRow.add( new UIText( strings.getKey( 'sidebar/material/displacemap' ) ).setWidth( '90px' ) );
-	materialDisplacementMapRow.add( materialDisplacementMapEnabled );
-	materialDisplacementMapRow.add( materialDisplacementMap );
-	materialDisplacementMapRow.add( materialDisplacementScale );
-
-	container.add( materialDisplacementMapRow );
+	const materialDisplacementMap = new SidebarMaterialMapProperty( editor, 'displacementMap', strings.getKey( 'sidebar/material/displacementmap' ) );
+	container.add( materialDisplacementMap );
 
 	// roughness map
 
-	var materialRoughnessMapRow = new UIRow();
-	var materialRoughnessMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialRoughnessMap = new UITexture().onChange( update );
-
-	materialRoughnessMapRow.add( new UIText( strings.getKey( 'sidebar/material/roughmap' ) ).setWidth( '90px' ) );
-	materialRoughnessMapRow.add( materialRoughnessMapEnabled );
-	materialRoughnessMapRow.add( materialRoughnessMap );
-
-	container.add( materialRoughnessMapRow );
+	const materialRoughnessMap = new SidebarMaterialMapProperty( editor, 'roughnessMap', strings.getKey( 'sidebar/material/roughnessmap' ) );
+	container.add( materialRoughnessMap );
 
 	// metalness map
 
-	var materialMetalnessMapRow = new UIRow();
-	var materialMetalnessMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialMetalnessMap = new UITexture().onChange( update );
-
-	materialMetalnessMapRow.add( new UIText( strings.getKey( 'sidebar/material/metalmap' ) ).setWidth( '90px' ) );
-	materialMetalnessMapRow.add( materialMetalnessMapEnabled );
-	materialMetalnessMapRow.add( materialMetalnessMap );
-
-	container.add( materialMetalnessMapRow );
-
-	// specular map
-
-	var materialSpecularMapRow = new UIRow();
-	var materialSpecularMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialSpecularMap = new UITexture().onChange( update );
-
-	materialSpecularMapRow.add( new UIText( strings.getKey( 'sidebar/material/specularmap' ) ).setWidth( '90px' ) );
-	materialSpecularMapRow.add( materialSpecularMapEnabled );
-	materialSpecularMapRow.add( materialSpecularMap );
-
-	container.add( materialSpecularMapRow );
+	const materialMetalnessMap = new SidebarMaterialMapProperty( editor, 'metalnessMap', strings.getKey( 'sidebar/material/metalnessmap' ) );
+	container.add( materialMetalnessMap );
 
 	// env map
 
-	var materialEnvMapRow = new UIRow();
-	var materialEnvMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialEnvMap = new UITexture( THREE.EquirectangularReflectionMapping ).onChange( updateMaterial );
-	var materialReflectivity = new UINumber( 1 ).setWidth( '30px' ).onChange( update );
-
-	materialEnvMapRow.add( new UIText( strings.getKey( 'sidebar/material/envmap' ) ).setWidth( '90px' ) );
-	materialEnvMapRow.add( materialEnvMapEnabled );
-	materialEnvMapRow.add( materialEnvMap );
-	materialEnvMapRow.add( materialReflectivity );
-
-	container.add( materialEnvMapRow );
+	const materialEnvMap = new SidebarMaterialMapProperty( editor, 'envMap', strings.getKey( 'sidebar/material/envmap' ) );
+	container.add( materialEnvMap );
 
 	// light map
 
-	var materialLightMapRow = new UIRow();
-	var materialLightMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialLightMap = new UITexture().onChange( update );
-
-	materialLightMapRow.add( new UIText( strings.getKey( 'sidebar/material/lightmap' ) ).setWidth( '90px' ) );
-	materialLightMapRow.add( materialLightMapEnabled );
-	materialLightMapRow.add( materialLightMap );
-
-	container.add( materialLightMapRow );
+	const materialLightMap = new SidebarMaterialMapProperty( editor, 'lightMap', strings.getKey( 'sidebar/material/lightmap' ) );
+	container.add( materialLightMap );
 
 	// ambient occlusion map
 
-	var materialAOMapRow = new UIRow();
-	var materialAOMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialAOMap = new UITexture().onChange( update );
-	var materialAOScale = new UINumber( 1 ).setRange( 0, 1 ).setWidth( '30px' ).onChange( update );
-
-	materialAOMapRow.add( new UIText( strings.getKey( 'sidebar/material/aomap' ) ).setWidth( '90px' ) );
-	materialAOMapRow.add( materialAOMapEnabled );
-	materialAOMapRow.add( materialAOMap );
-	materialAOMapRow.add( materialAOScale );
-
-	container.add( materialAOMapRow );
-
-	// emissive map
-
-	var materialEmissiveMapRow = new UIRow();
-	var materialEmissiveMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialEmissiveMap = new UITexture().onChange( updateMaterial );
-
-	materialEmissiveMapRow.add( new UIText( strings.getKey( 'sidebar/material/emissivemap' ) ).setWidth( '90px' ) );
-	materialEmissiveMapRow.add( materialEmissiveMapEnabled );
-	materialEmissiveMapRow.add( materialEmissiveMap );
-
-	container.add( materialEmissiveMapRow );
+	const materialAOMap = new SidebarMaterialMapProperty( editor, 'aoMap', strings.getKey( 'sidebar/material/aomap' ) );
+	container.add( materialAOMap );
 
 	// gradient map
 
-	var materialGradientMapRow = new UIRow();
-	var materialGradientMapEnabled = new UICheckbox( false ).onChange( update );
-	var materialGradientMap = new UITexture().onChange( update );
-
-	materialGradientMapRow.add( new UIText( strings.getKey( 'sidebar/material/gradientmap' ) ).setWidth( '90px' ) );
-	materialGradientMapRow.add( materialGradientMapEnabled );
-	materialGradientMapRow.add( materialGradientMap );
-
-	container.add( materialGradientMapRow );
+	const materialGradientMap = new SidebarMaterialMapProperty( editor, 'gradientMap', strings.getKey( 'sidebar/material/gradientmap' ) );
+	container.add( materialGradientMap );
 
 	// side
 
-	var materialSideRow = new UIRow();
-	var materialSide = new UISelect().setOptions( {
-
-		0: strings.getKey( 'sidebar/material/side/front' ),
-		1: strings.getKey( 'sidebar/material/side/back' ),
-		2: strings.getKey( 'sidebar/material/side/double' )
-
-	} ).setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
-
-	materialSideRow.add( new UIText( strings.getKey( 'sidebar/material/side' ) ).setWidth( '90px' ) );
-	materialSideRow.add( materialSide );
+	const materialSideOptions = {
+		0: 'Front',
+		1: 'Back',
+		2: 'Double'
+	};
 
-	container.add( materialSideRow );
+	const materialSide = new SidebarMaterialConstantProperty( editor, 'side', strings.getKey( 'sidebar/material/side' ), materialSideOptions );
+	container.add( materialSide );
 
 	// size
 
-	var materialSizeRow = new UIRow();
-	var materialSize = new UINumber( 1 ).setWidth( '60px' ).setRange( 0, Infinity ).onChange( update );
-
-	materialSizeRow.add( new UIText( strings.getKey( 'sidebar/material/size' ) ).setWidth( '90px' ) );
-	materialSizeRow.add( materialSize );
-
-	container.add( materialSizeRow );
+	const materialSize = new SidebarMaterialNumberProperty( editor, 'size', strings.getKey( 'sidebar/material/size' ), [ 0, Infinity ] );
+	container.add( materialSize );
 
 	// sizeAttenuation
 
-	var materialSizeAttenuationRow = new UIRow();
-	var materialSizeAttenuation = new UICheckbox( true ).onChange( update );
-
-	materialSizeAttenuationRow.add( new UIText( strings.getKey( 'sidebar/material/sizeAttenuation' ) ).setWidth( '90px' ) );
-	materialSizeAttenuationRow.add( materialSizeAttenuation );
+	const materialSizeAttenuation = new SidebarMaterialBooleanProperty( editor, 'sizeAttenuation', strings.getKey( 'sidebar/material/sizeAttenuation' ) );
+	container.add( materialSizeAttenuation );
 
-	container.add( materialSizeAttenuationRow );
+	// flatShading
 
-	// shading
-
-	var materialShadingRow = new UIRow();
-	var materialShading = new UICheckbox( false ).setLeft( '100px' ).onChange( update );
-
-	materialShadingRow.add( new UIText( strings.getKey( 'sidebar/material/flatshaded' ) ).setWidth( '90px' ) );
-	materialShadingRow.add( materialShading );
-
-	container.add( materialShadingRow );
+	const materialFlatShading = new SidebarMaterialBooleanProperty( editor, 'flatShading', strings.getKey( 'sidebar/material/flatShading' ) );
+	container.add( materialFlatShading );
 
 	// blending
 
-	var materialBlendingRow = new UIRow();
-	var materialBlending = new UISelect().setOptions( {
-
-		0: strings.getKey( 'sidebar/material/blending/no' ),
-		1: strings.getKey( 'sidebar/material/blending/normal' ),
-		2: strings.getKey( 'sidebar/material/blending/additive' ),
-		3: strings.getKey( 'sidebar/material/blending/subtractive' ),
-		4: strings.getKey( 'sidebar/material/blending/multiply' ),
-		5: strings.getKey( 'sidebar/material/blending/custom' )
-
-	} ).setWidth( '150px' ).setFontSize( '12px' ).onChange( update );
-
-	materialBlendingRow.add( new UIText( strings.getKey( 'sidebar/material/blending' ) ).setWidth( '90px' ) );
-	materialBlendingRow.add( materialBlending );
+	const materialBlendingOptions = {
+		0: 'No',
+		1: 'Normal',
+		2: 'Additive',
+		3: 'Subtractive',
+		4: 'Multiply',
+		5: 'Custom'
+	};
 
-	container.add( materialBlendingRow );
+	const materialBlending = new SidebarMaterialConstantProperty( editor, 'blending', strings.getKey( 'sidebar/material/blending' ), materialBlendingOptions );
+	container.add( materialBlending );
 
 	// opacity
 
-	var materialOpacityRow = new UIRow();
-	var materialOpacity = new UINumber( 1 ).setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
-
-	materialOpacityRow.add( new UIText( strings.getKey( 'sidebar/material/opacity' ) ).setWidth( '90px' ) );
-	materialOpacityRow.add( materialOpacity );
-
-	container.add( materialOpacityRow );
+	const materialOpacity = new SidebarMaterialNumberProperty( editor, 'opacity', strings.getKey( 'sidebar/material/opacity' ), [ 0, 1 ] );
+	container.add( materialOpacity );
 
 	// transparent
 
-	var materialTransparentRow = new UIRow();
-	var materialTransparent = new UICheckbox().setLeft( '100px' ).onChange( update );
-
-	materialTransparentRow.add( new UIText( strings.getKey( 'sidebar/material/transparent' ) ).setWidth( '90px' ) );
-	materialTransparentRow.add( materialTransparent );
-
-	container.add( materialTransparentRow );
+	const materialTransparent = new SidebarMaterialBooleanProperty( editor, 'transparent', strings.getKey( 'sidebar/material/transparent' ) );
+	container.add( materialTransparent );
 
 	// alpha test
 
-	var materialAlphaTestRow = new UIRow();
-	var materialAlphaTest = new UINumber().setWidth( '60px' ).setRange( 0, 1 ).onChange( update );
-
-	materialAlphaTestRow.add( new UIText( strings.getKey( 'sidebar/material/alphatest' ) ).setWidth( '90px' ) );
-	materialAlphaTestRow.add( materialAlphaTest );
-
-	container.add( materialAlphaTestRow );
+	const materialAlphaTest = new SidebarMaterialNumberProperty( editor, 'alphaTest', strings.getKey( 'sidebar/material/alphatest' ), [ 0, 1 ] );
+	container.add( materialAlphaTest );
 
 	// depth test
 
-	var materialDepthTestRow = new UIRow();
-	var materialDepthTest = new UICheckbox().onChange( update );
-
-	materialDepthTestRow.add( new UIText( strings.getKey( 'sidebar/material/depthtest' ) ).setWidth( '90px' ) );
-	materialDepthTestRow.add( materialDepthTest );
-
-	container.add( materialDepthTestRow );
+	const materialDepthTest = new SidebarMaterialBooleanProperty( editor, 'depthTest', strings.getKey( 'sidebar/material/depthtest' ) );
+	container.add( materialDepthTest );
 
 	// depth write
 
-	var materialDepthWriteRow = new UIRow();
-	var materialDepthWrite = new UICheckbox().onChange( update );
-
-	materialDepthWriteRow.add( new UIText( strings.getKey( 'sidebar/material/depthwrite' ) ).setWidth( '90px' ) );
-	materialDepthWriteRow.add( materialDepthWrite );
-
-	container.add( materialDepthWriteRow );
+	const materialDepthWrite = new SidebarMaterialBooleanProperty( editor, 'depthWrite', strings.getKey( 'sidebar/material/depthwrite' ) );
+	container.add( materialDepthWrite );
 
 	// wireframe
 
-	var materialWireframeRow = new UIRow();
-	var materialWireframe = new UICheckbox( false ).onChange( update );
-
-	materialWireframeRow.add( new UIText( strings.getKey( 'sidebar/material/wireframe' ) ).setWidth( '90px' ) );
-	materialWireframeRow.add( materialWireframe );
-
-	container.add( materialWireframeRow );
+	const materialWireframe = new SidebarMaterialBooleanProperty( editor, 'wireframe', strings.getKey( 'sidebar/material/wireframe' ) );
+	container.add( materialWireframe );
 
 	//
 
 	function update() {
 
-		var object = currentObject;
-
-		var geometry = object.geometry;
-
-		var previousSelectedSlot = currentMaterialSlot;
+		const previousSelectedSlot = currentMaterialSlot;
 
 		currentMaterialSlot = parseInt( materialSlotSelect.getValue() );
 
-		if ( currentMaterialSlot !== previousSelectedSlot ) refreshUI( true );
-
-		var material = editor.getObjectMaterial( currentObject, currentMaterialSlot );
+		if ( currentMaterialSlot !== previousSelectedSlot ) refreshUI();
 
-		var textureWarning = false;
-		var objectHasUvs = false;
-
-		if ( object.isSprite ) objectHasUvs = true;
-		if ( geometry.isGeometry && geometry.faceVertexUvs[ 0 ].length > 0 ) objectHasUvs = true;
-		if ( geometry.isBufferGeometry && geometry.attributes.uv !== undefined ) objectHasUvs = true;
+		let material = editor.getObjectMaterial( currentObject, currentMaterialSlot );
 
 		if ( material ) {
 
@@ -646,1144 +347,190 @@ function SidebarMaterial( editor ) {
 
 			}
 
-			if ( material.color !== undefined && material.color.getHex() !== materialColor.getHexValue() ) {
-
-				editor.execute( new SetMaterialColorCommand( editor, currentObject, 'color', materialColor.getHexValue(), currentMaterialSlot ) );
-
-			}
-
-			if ( material.roughness !== undefined && Math.abs( material.roughness - materialRoughness.getValue() ) >= epsilon ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'roughness', materialRoughness.getValue(), currentMaterialSlot ) );
-
-			}
-
-			if ( material.metalness !== undefined && Math.abs( material.metalness - materialMetalness.getValue() ) >= epsilon ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'metalness', materialMetalness.getValue(), currentMaterialSlot ) );
-
-			}
-
-			/*
-			if ( material.sheen !== undefined ) {
-
-				var sheenEnabled = materialSheenEnabled.getValue() === true;
-
-				var sheen = sheenEnabled ? new Color(materialSheen.getHexValue()) : null;
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'sheen', sheen, currentMaterialSlot ) );
-
-			}
-
-			if ( material.sheen !== undefined && material.sheen !== null && material.sheen.getHex() !== materialSheen.getHexValue() ) {
-
-				editor.execute( new SetMaterialColorCommand( editor, currentObject, 'sheen', materialSheen.getHexValue(), currentMaterialSlot ) );
+			refreshUI();
 
-			}
-			*/
+		}
 
-			if ( material.transmission !== undefined && Math.abs( material.transmission - materialTransmission.getValue() ) >= epsilon ) {
+	}
 
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'transmission', materialTransmission.getValue(), currentMaterialSlot ) );
+	//
 
-			}
+	function setRowVisibility() {
 
-			if ( material.emissive !== undefined && material.emissive.getHex() !== materialEmissive.getHexValue() ) {
+		const material = currentObject.material;
 
-				editor.execute( new SetMaterialColorCommand( editor, currentObject, 'emissive', materialEmissive.getHexValue(), currentMaterialSlot ) );
+		if ( Array.isArray( material ) ) {
 
-			}
+			materialSlotRow.setDisplay( '' );
 
-			if ( material.emissiveIntensity !== undefined && material.emissiveIntensity !== materialEmissiveIntensity.getValue() ) {
+			if ( material.length === 0 ) return;
 
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'emissiveIntensity', materialEmissiveIntensity.getValue(), currentMaterialSlot ) );
+			material = material[ currentMaterialSlot ];
 
-			}
+		} else {
 
-			if ( material.specular !== undefined && material.specular.getHex() !== materialSpecular.getHexValue() ) {
+			materialSlotRow.setDisplay( 'none' );
 
-				editor.execute( new SetMaterialColorCommand( editor, currentObject, 'specular', materialSpecular.getHexValue(), currentMaterialSlot ) );
+		}
 
-			}
+	}
 
-			if ( material.shininess !== undefined && Math.abs( material.shininess - materialShininess.getValue() ) >= epsilon ) {
+	function refreshUI() {
 
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'shininess', materialShininess.getValue(), currentMaterialSlot ) );
+		if ( ! currentObject ) return;
 
-			}
+		let material = currentObject.material;
 
-			if ( material.clearcoat !== undefined && Math.abs( material.clearcoat - materialClearcoat.getValue() ) >= epsilon ) {
+		if ( Array.isArray( material ) ) {
 
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'clearcoat', materialClearcoat.getValue(), currentMaterialSlot ) );
+			const slotOptions = {};
 
-			}
+			currentMaterialSlot = Math.max( 0, Math.min( material.length, currentMaterialSlot ) );
 
-			if ( material.clearcoatRoughness !== undefined && Math.abs( material.clearcoatRoughness - materialClearcoatRoughness.getValue() ) >= epsilon ) {
+			for ( let i = 0; i < material.length; i ++ ) {
 
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'clearcoatRoughness', materialClearcoatRoughness.getValue(), currentMaterialSlot ) );
+				slotOptions[ i ] = String( i + 1 ) + ': ' + material[ i ].name;
 
 			}
 
-			if ( material.vertexColors !== undefined ) {
-
-				var vertexColors = materialVertexColors.getValue();
-
-				if ( material.vertexColors !== vertexColors ) {
+			materialSlotSelect.setOptions( slotOptions ).setValue( currentMaterialSlot );
 
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'vertexColors', vertexColors, currentMaterialSlot ) );
+		}
 
-				}
+		material = editor.getObjectMaterial( currentObject, currentMaterialSlot );
 
-			}
+		if ( material.uuid !== undefined ) {
 
-			if ( material.depthPacking !== undefined ) {
+			materialUUID.setValue( material.uuid );
 
-				var depthPacking = parseInt( materialDepthPacking.getValue() );
-				if ( material.depthPacking !== depthPacking ) {
+		}
 
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'depthPacking', depthPacking, currentMaterialSlot ) );
+		if ( material.name !== undefined ) {
 
-				}
+			materialName.setValue( material.name );
 
-			}
+		}
 
-			if ( material.map !== undefined ) {
+		if ( currentObject.isMesh ) {
 
-				var mapEnabled = materialMapEnabled.getValue() === true;
+			materialClass.setOptions( meshMaterialOptions );
 
-				if ( objectHasUvs ) {
+		} else if ( currentObject.isSprite ) {
 
-					var map = mapEnabled ? materialMap.getValue() : null;
-					if ( material.map !== map ) {
+			materialClass.setOptions( spriteMaterialOptions );
 
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'map', map, currentMaterialSlot ) );
+		} else if ( currentObject.isPoints ) {
 
-					}
+			materialClass.setOptions( pointsMaterialOptions );
 
-				} else {
+		} else if ( currentObject.isLine ) {
 
-					if ( mapEnabled ) textureWarning = true;
+			materialClass.setOptions( lineMaterialOptions );
 
-				}
+		}
 
-			}
+		materialClass.setValue( material.type );
 
-			if ( material.matcap !== undefined ) {
+		setRowVisibility();
 
-				var mapEnabled = materialMatcapMapEnabled.getValue() === true;
+	}
 
-				if ( objectHasUvs ) {
+	// events
 
-					var matcap = mapEnabled ? materialMatcapMap.getValue() : null;
-					if ( material.matcap !== matcap ) {
+	signals.objectSelected.add( function ( object ) {
 
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'matcap', matcap, currentMaterialSlot ) );
+		let hasMaterial = false;
 
-					}
+		if ( object && object.material ) {
 
-				} else {
+			hasMaterial = true;
 
-					if ( mapEnabled ) textureWarning = true;
+			if ( Array.isArray( object.material ) && object.material.length === 0 ) {
 
-				}
+				hasMaterial = false;
 
 			}
 
-			if ( material.alphaMap !== undefined ) {
-
-				var mapEnabled = materialAlphaMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var alphaMap = mapEnabled ? materialAlphaMap.getValue() : null;
-					if ( material.alphaMap !== alphaMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'alphaMap', alphaMap, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( mapEnabled ) textureWarning = true;
+		}
 
-				}
+		if ( hasMaterial ) {
 
-			}
+			currentObject = object;
+			refreshUI();
+			container.setDisplay( '' );
 
-			if ( material.bumpMap !== undefined ) {
+		} else {
 
-				var bumpMapEnabled = materialBumpMapEnabled.getValue() === true;
+			currentObject = null;
+			container.setDisplay( 'none' );
 
-				if ( objectHasUvs ) {
+		}
 
-					var bumpMap = bumpMapEnabled ? materialBumpMap.getValue() : null;
-					if ( material.bumpMap !== bumpMap ) {
+	} );
 
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'bumpMap', bumpMap, currentMaterialSlot ) );
+	signals.materialChanged.add( refreshUI );
 
-					}
+	return container;
 
-					if ( material.bumpScale !== materialBumpScale.getValue() ) {
+}
 
-						editor.execute( new SetMaterialValueCommand( editor, currentObject, 'bumpScale', materialBumpScale.getValue(), currentMaterialSlot ) );
+const materialClasses = {
+	'LineBasicMaterial': THREE.LineBasicMaterial,
+	'LineDashedMaterial': THREE.LineDashedMaterial,
+	'MeshBasicMaterial': THREE.MeshBasicMaterial,
+	'MeshDepthMaterial': THREE.MeshDepthMaterial,
+	'MeshNormalMaterial': THREE.MeshNormalMaterial,
+	'MeshLambertMaterial': THREE.MeshLambertMaterial,
+	'MeshMatcapMaterial': THREE.MeshMatcapMaterial,
+	'MeshPhongMaterial': THREE.MeshPhongMaterial,
+	'MeshToonMaterial': THREE.MeshToonMaterial,
+	'MeshStandardMaterial': THREE.MeshStandardMaterial,
+	'MeshPhysicalMaterial': THREE.MeshPhysicalMaterial,
+	'RawShaderMaterial': THREE.RawShaderMaterial,
+	'ShaderMaterial': THREE.ShaderMaterial,
+	'ShadowMaterial': THREE.ShadowMaterial,
+	'SpriteMaterial': THREE.SpriteMaterial,
+	'PointsMaterial': THREE.PointsMaterial
+};
 
-					}
+const vertexShaderVariables = [
+	'uniform mat4 projectionMatrix;',
+	'uniform mat4 modelViewMatrix;\n',
+	'attribute vec3 position;\n\n',
+].join( '\n' );
+
+const meshMaterialOptions = {
+	'MeshBasicMaterial': 'MeshBasicMaterial',
+	'MeshDepthMaterial': 'MeshDepthMaterial',
+	'MeshNormalMaterial': 'MeshNormalMaterial',
+	'MeshLambertMaterial': 'MeshLambertMaterial',
+	'MeshMatcapMaterial': 'MeshMatcapMaterial',
+	'MeshPhongMaterial': 'MeshPhongMaterial',
+	'MeshToonMaterial': 'MeshToonMaterial',
+	'MeshStandardMaterial': 'MeshStandardMaterial',
+	'MeshPhysicalMaterial': 'MeshPhysicalMaterial',
+	'RawShaderMaterial': 'RawShaderMaterial',
+	'ShaderMaterial': 'ShaderMaterial',
+	'ShadowMaterial': 'ShadowMaterial'
+};
 
-				} else {
+const lineMaterialOptions = {
+	'LineBasicMaterial': 'LineBasicMaterial',
+	'LineDashedMaterial': 'LineDashedMaterial',
+	'RawShaderMaterial': 'RawShaderMaterial',
+	'ShaderMaterial': 'ShaderMaterial'
+};
 
-					if ( bumpMapEnabled ) textureWarning = true;
+const spriteMaterialOptions = {
+	'SpriteMaterial': 'SpriteMaterial',
+	'RawShaderMaterial': 'RawShaderMaterial',
+	'ShaderMaterial': 'ShaderMaterial'
+};
 
-				}
-
-			}
-
-			if ( material.normalMap !== undefined ) {
-
-				var normalMapEnabled = materialNormalMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var normalMap = normalMapEnabled ? materialNormalMap.getValue() : null;
-					if ( material.normalMap !== normalMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'normalMap', normalMap, currentMaterialSlot ) );
-
-					}
-
-					if ( material.normalScale.x !== materialNormalScaleX.getValue() ||
-						material.normalScale.y !== materialNormalScaleY.getValue() ) {
-
-						var value = [
-							materialNormalScaleX.getValue(),
-							materialNormalScaleY.getValue()
-						];
-						editor.execute( new SetMaterialVectorCommand( editor, currentObject, 'normalScale', value, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( normalMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.clearcoatNormalMap !== undefined ) {
-
-				var clearcoatNormalMapEnabled = materialClearcoatNormalMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var clearcoatNormalMap = clearcoatNormalMapEnabled ? materialClearcoatNormalMap.getValue() : null;
-
-					if ( material.clearcoatNormalMap !== clearcoatNormalMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'clearcoatNormalMap', clearcoatNormalMap, currentMaterialSlot ) );
-
-					}
-
-					if ( material.clearcoatNormalScale.x !== materialClearcoatNormalScaleX.getValue() ||
-						material.clearcoatNormalScale.y !== materialClearcoatNormalScaleY.getValue() ) {
-
-						var value = [
-							materialClearcoatNormalScaleX.getValue(),
-							materialClearcoatNormalScaleY.getValue()
-						];
-						editor.execute( new SetMaterialVectorCommand( editor, currentObject, 'clearcoatNormalScale', value, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( clearcoatNormalMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.displacementMap !== undefined ) {
-
-				var displacementMapEnabled = materialDisplacementMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var displacementMap = displacementMapEnabled ? materialDisplacementMap.getValue() : null;
-					if ( material.displacementMap !== displacementMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'displacementMap', displacementMap, currentMaterialSlot ) );
-
-					}
-
-					if ( material.displacementScale !== materialDisplacementScale.getValue() ) {
-
-						editor.execute( new SetMaterialValueCommand( editor, currentObject, 'displacementScale', materialDisplacementScale.getValue(), currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( displacementMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.roughnessMap !== undefined ) {
-
-				var roughnessMapEnabled = materialRoughnessMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var roughnessMap = roughnessMapEnabled ? materialRoughnessMap.getValue() : null;
-					if ( material.roughnessMap !== roughnessMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'roughnessMap', roughnessMap, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( roughnessMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.metalnessMap !== undefined ) {
-
-				var metalnessMapEnabled = materialMetalnessMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var metalnessMap = metalnessMapEnabled ? materialMetalnessMap.getValue() : null;
-					if ( material.metalnessMap !== metalnessMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'metalnessMap', metalnessMap, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( metalnessMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.specularMap !== undefined ) {
-
-				var specularMapEnabled = materialSpecularMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var specularMap = specularMapEnabled ? materialSpecularMap.getValue() : null;
-					if ( material.specularMap !== specularMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'specularMap', specularMap, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( specularMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.envMap !== undefined ) {
-
-				var envMapEnabled = materialEnvMapEnabled.getValue() === true;
-
-				var envMap = envMapEnabled ? materialEnvMap.getValue() : null;
-
-				if ( material.envMap !== envMap ) {
-
-					editor.execute( new SetMaterialMapCommand( editor, currentObject, 'envMap', envMap, currentMaterialSlot ) );
-
-				}
-
-			}
-
-			if ( material.reflectivity !== undefined ) {
-
-				var reflectivity = materialReflectivity.getValue();
-
-				if ( material.reflectivity !== reflectivity ) {
-
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'reflectivity', reflectivity, currentMaterialSlot ) );
-
-				}
-
-			}
-
-			if ( material.lightMap !== undefined ) {
-
-				var lightMapEnabled = materialLightMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var lightMap = lightMapEnabled ? materialLightMap.getValue() : null;
-					if ( material.lightMap !== lightMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'lightMap', lightMap, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( lightMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.aoMap !== undefined ) {
-
-				var aoMapEnabled = materialAOMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var aoMap = aoMapEnabled ? materialAOMap.getValue() : null;
-					if ( material.aoMap !== aoMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'aoMap', aoMap, currentMaterialSlot ) );
-
-					}
-
-					if ( material.aoMapIntensity !== materialAOScale.getValue() ) {
-
-						editor.execute( new SetMaterialValueCommand( editor, currentObject, 'aoMapIntensity', materialAOScale.getValue(), currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( aoMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.emissiveMap !== undefined ) {
-
-				var emissiveMapEnabled = materialEmissiveMapEnabled.getValue() === true;
-
-				if ( objectHasUvs ) {
-
-					var emissiveMap = emissiveMapEnabled ? materialEmissiveMap.getValue() : null;
-					if ( material.emissiveMap !== emissiveMap ) {
-
-						editor.execute( new SetMaterialMapCommand( editor, currentObject, 'emissiveMap', emissiveMap, currentMaterialSlot ) );
-
-					}
-
-				} else {
-
-					if ( emissiveMapEnabled ) textureWarning = true;
-
-				}
-
-			}
-
-			if ( material.gradientMap !== undefined ) {
-
-				var gradientMapEnabled = materialGradientMapEnabled.getValue() === true;
-
-				var gradientMap = gradientMapEnabled ? materialGradientMap.getValue() : null;
-
-				if ( material.gradientMap !== gradientMap ) {
-
-					editor.execute( new SetMaterialMapCommand( editor, currentObject, 'gradientMap', gradientMap, currentMaterialSlot ) );
-
-				}
-
-			}
-
-			if ( material.side !== undefined ) {
-
-				var side = parseInt( materialSide.getValue() );
-				if ( material.side !== side ) {
-
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'side', side, currentMaterialSlot ) );
-
-				}
-
-
-			}
-
-			if ( material.size !== undefined ) {
-
-				var size = materialSize.getValue();
-				if ( material.size !== size ) {
-
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'size', size, currentMaterialSlot ) );
-
-				}
-
-			}
-
-			if ( material.sizeAttenuation !== undefined ) {
-
-				var sizeAttenuation = materialSizeAttenuation.getValue();
-				if ( material.sizeAttenuation !== sizeAttenuation ) {
-
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'sizeAttenuation', sizeAttenuation, currentMaterialSlot ) );
-
-				}
-
-			}
-
-			if ( material.flatShading !== undefined ) {
-
-				var flatShading = materialShading.getValue();
-				if ( material.flatShading != flatShading ) {
-
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'flatShading', flatShading, currentMaterialSlot ) );
-
-				}
-
-			}
-
-			if ( material.blending !== undefined ) {
-
-				var blending = parseInt( materialBlending.getValue() );
-				if ( material.blending !== blending ) {
-
-					editor.execute( new SetMaterialValueCommand( editor, currentObject, 'blending', blending, currentMaterialSlot ) );
-
-				}
-
-			}
-
-			if ( material.opacity !== undefined && Math.abs( material.opacity - materialOpacity.getValue() ) >= epsilon ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'opacity', materialOpacity.getValue(), currentMaterialSlot ) );
-
-			}
-
-			if ( material.transparent !== undefined && material.transparent !== materialTransparent.getValue() ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'transparent', materialTransparent.getValue(), currentMaterialSlot ) );
-
-			}
-
-			if ( material.alphaTest !== undefined && Math.abs( material.alphaTest - materialAlphaTest.getValue() ) >= epsilon ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'alphaTest', materialAlphaTest.getValue(), currentMaterialSlot ) );
-
-			}
-
-			if ( material.depthTest !== undefined && material.depthTest !== materialDepthTest.getValue() ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'depthTest', materialDepthTest.getValue(), currentMaterialSlot ) );
-
-			}
-
-			if ( material.depthWrite !== undefined && material.depthWrite !== materialDepthWrite.getValue() ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'depthWrite', materialDepthWrite.getValue(), currentMaterialSlot ) );
-
-			}
-
-			if ( material.wireframe !== undefined && material.wireframe !== materialWireframe.getValue() ) {
-
-				editor.execute( new SetMaterialValueCommand( editor, currentObject, 'wireframe', materialWireframe.getValue(), currentMaterialSlot ) );
-
-			}
-
-			refreshUI();
-
-		}
-
-		if ( textureWarning ) {
-
-			console.warn( 'Can\'t set texture, model doesn\'t have texture coordinates' );
-
-		}
-
-	}
-
-	function updateMaterial( texture ) {
-
-		if ( texture !== null ) {
-
-			if ( texture.isDataTexture !== true && texture.encoding !== THREE.sRGBEncoding ) {
-
-				texture.encoding = THREE.sRGBEncoding;
-				var object = currentObject;
-				if ( object !== null ) {
-
-					object.material.needsUpdate = true;
-
-				}
-
-			}
-
-		}
-
-		update();
-
-	}
-
-	//
-
-	function setRowVisibility() {
-
-		var properties = {
-			'name': materialNameRow,
-			'color': materialColorRow,
-			'roughness': materialRoughnessRow,
-			'metalness': materialMetalnessRow,
-			'emissive': materialEmissiveRow,
-			// 'sheen': materialSheenRow,
-			'transmission': materialTransmissionRow,
-			'specular': materialSpecularRow,
-			'shininess': materialShininessRow,
-			'clearcoat': materialClearcoatRow,
-			'clearcoatRoughness': materialClearcoatRoughnessRow,
-			'vertexShader': materialProgramRow,
-			'vertexColors': materialVertexColorsRow,
-			'depthPacking': materialDepthPackingRow,
-			'map': materialMapRow,
-			'matcap': materialMatcapMapRow,
-			'alphaMap': materialAlphaMapRow,
-			'bumpMap': materialBumpMapRow,
-			'normalMap': materialNormalMapRow,
-			'clearcoatNormalMap': materialClearcoatNormalMapRow,
-			'displacementMap': materialDisplacementMapRow,
-			'roughnessMap': materialRoughnessMapRow,
-			'metalnessMap': materialMetalnessMapRow,
-			'specularMap': materialSpecularMapRow,
-			'envMap': materialEnvMapRow,
-			'lightMap': materialLightMapRow,
-			'aoMap': materialAOMapRow,
-			'emissiveMap': materialEmissiveMapRow,
-			'gradientMap': materialGradientMapRow,
-			'side': materialSideRow,
-			'size': materialSizeRow,
-			'sizeAttenuation': materialSizeAttenuationRow,
-			'flatShading': materialShadingRow,
-			'blending': materialBlendingRow,
-			'opacity': materialOpacityRow,
-			'transparent': materialTransparentRow,
-			'alphaTest': materialAlphaTestRow,
-			'depthTest': materialDepthTestRow,
-			'depthWrite': materialDepthWriteRow,
-			'wireframe': materialWireframeRow
-		};
-
-		var material = currentObject.material;
-
-		if ( Array.isArray( material ) ) {
-
-			materialSlotRow.setDisplay( '' );
-
-			if ( material.length === 0 ) return;
-
-			material = material[ currentMaterialSlot ];
-
-		} else {
-
-			materialSlotRow.setDisplay( 'none' );
-
-		}
-
-		for ( var property in properties ) {
-
-			properties[ property ].setDisplay( material[ property ] !== undefined ? '' : 'none' );
-
-		}
-
-	}
-
-
-	function refreshUI( resetTextureSelectors ) {
-
-		if ( ! currentObject ) return;
-
-		var material = currentObject.material;
-
-		if ( Array.isArray( material ) ) {
-
-			var slotOptions = {};
-
-			currentMaterialSlot = Math.max( 0, Math.min( material.length, currentMaterialSlot ) );
-
-			for ( var i = 0; i < material.length; i ++ ) {
-
-				slotOptions[ i ] = String( i + 1 ) + ': ' + material[ i ].name;
-
-			}
-
-			materialSlotSelect.setOptions( slotOptions ).setValue( currentMaterialSlot );
-
-		}
-
-		material = editor.getObjectMaterial( currentObject, currentMaterialSlot );
-
-		if ( material.uuid !== undefined ) {
-
-			materialUUID.setValue( material.uuid );
-
-		}
-
-		if ( material.name !== undefined ) {
-
-			materialName.setValue( material.name );
-
-		}
-
-		if ( currentObject.isMesh ) {
-
-			materialClass.setOptions( meshMaterialOptions );
-
-		} else if ( currentObject.isSprite ) {
-
-			materialClass.setOptions( spriteMaterialOptions );
-
-		} else if ( currentObject.isPoints ) {
-
-			materialClass.setOptions( pointsMaterialOptions );
-
-		} else if ( currentObject.isLine ) {
-
-			materialClass.setOptions( lineMaterialOptions );
-
-		}
-
-		materialClass.setValue( material.type );
-
-
-		if ( material.color !== undefined ) {
-
-			materialColor.setHexValue( material.color.getHexString() );
-
-		}
-
-		if ( material.roughness !== undefined ) {
-
-			materialRoughness.setValue( material.roughness );
-
-		}
-
-		if ( material.metalness !== undefined ) {
-
-			materialMetalness.setValue( material.metalness );
-
-		}
-
-		/*
-		if ( material.sheen !== undefined && material.sheen !== null ) {
-
-			materialSheenEnabled.setValue( true );
-			materialSheen.setHexValue( material.sheen.getHexString() );
-
-		}
-		*/
-
-		if ( material.transmission !== undefined ) {
-
-			materialTransmission.setValue( material.transmission );
-
-		}
-
-		if ( material.emissive !== undefined ) {
-
-			materialEmissive.setHexValue( material.emissive.getHexString() );
-
-			materialEmissiveIntensity.setValue( material.emissiveIntensity );
-
-		}
-
-		if ( material.specular !== undefined ) {
-
-			materialSpecular.setHexValue( material.specular.getHexString() );
-
-		}
-
-		if ( material.shininess !== undefined ) {
-
-			materialShininess.setValue( material.shininess );
-
-		}
-
-		if ( material.clearcoat !== undefined ) {
-
-			materialClearcoat.setValue( material.clearcoat );
-
-		}
-
-		if ( material.clearcoatRoughness !== undefined ) {
-
-			materialClearcoatRoughness.setValue( material.clearcoatRoughness );
-
-		}
-
-		if ( material.vertexColors !== undefined ) {
-
-			materialVertexColors.setValue( material.vertexColors );
-
-		}
-
-		if ( material.depthPacking !== undefined ) {
-
-			materialDepthPacking.setValue( material.depthPacking );
-
-		}
-
-		if ( material.map !== undefined ) {
-
-			materialMapEnabled.setValue( material.map !== null );
-
-			if ( material.map !== null || resetTextureSelectors ) {
-
-				materialMap.setValue( material.map );
-
-			}
-
-		}
-
-		if ( material.matcap !== undefined ) {
-
-			materialMatcapMapEnabled.setValue( material.matcap !== null );
-
-			if ( material.matcap !== null || resetTextureSelectors ) {
-
-				materialMatcapMap.setValue( material.matcap );
-
-			}
-
-		}
-
-		if ( material.alphaMap !== undefined ) {
-
-			materialAlphaMapEnabled.setValue( material.alphaMap !== null );
-
-			if ( material.alphaMap !== null || resetTextureSelectors ) {
-
-				materialAlphaMap.setValue( material.alphaMap );
-
-			}
-
-		}
-
-		if ( material.bumpMap !== undefined ) {
-
-			materialBumpMapEnabled.setValue( material.bumpMap !== null );
-
-			if ( material.bumpMap !== null || resetTextureSelectors ) {
-
-				materialBumpMap.setValue( material.bumpMap );
-
-			}
-
-			materialBumpScale.setValue( material.bumpScale );
-
-		}
-
-		if ( material.normalMap !== undefined ) {
-
-			materialNormalMapEnabled.setValue( material.normalMap !== null );
-
-			if ( material.normalMap !== null || resetTextureSelectors ) {
-
-				materialNormalMap.setValue( material.normalMap );
-
-			}
-
-			materialNormalScaleX.setValue( material.normalScale.x );
-			materialNormalScaleY.setValue( material.normalScale.y );
-
-		}
-
-		if ( material.clearcoatNormalMap !== undefined ) {
-
-			materialClearcoatNormalMapEnabled.setValue( material.clearcoatNormalMap !== null );
-
-			if ( material.clearcoatNormalMap !== null || resetTextureSelectors ) {
-
-				materialClearcoatNormalMap.setValue( material.clearcoatNormalMap );
-
-			}
-
-			materialClearcoatNormalScaleX.setValue( material.clearcoatNormalScale.x );
-			materialClearcoatNormalScaleY.setValue( material.clearcoatNormalScale.y );
-
-		}
-
-		if ( material.displacementMap !== undefined ) {
-
-			materialDisplacementMapEnabled.setValue( material.displacementMap !== null );
-
-			if ( material.displacementMap !== null || resetTextureSelectors ) {
-
-				materialDisplacementMap.setValue( material.displacementMap );
-
-			}
-
-			materialDisplacementScale.setValue( material.displacementScale );
-
-		}
-
-		if ( material.roughnessMap !== undefined ) {
-
-			materialRoughnessMapEnabled.setValue( material.roughnessMap !== null );
-
-			if ( material.roughnessMap !== null || resetTextureSelectors ) {
-
-				materialRoughnessMap.setValue( material.roughnessMap );
-
-			}
-
-		}
-
-		if ( material.metalnessMap !== undefined ) {
-
-			materialMetalnessMapEnabled.setValue( material.metalnessMap !== null );
-
-			if ( material.metalnessMap !== null || resetTextureSelectors ) {
-
-				materialMetalnessMap.setValue( material.metalnessMap );
-
-			}
-
-		}
-
-		if ( material.specularMap !== undefined ) {
-
-			materialSpecularMapEnabled.setValue( material.specularMap !== null );
-
-			if ( material.specularMap !== null || resetTextureSelectors ) {
-
-				materialSpecularMap.setValue( material.specularMap );
-
-			}
-
-		}
-
-		if ( material.envMap !== undefined ) {
-
-			materialEnvMapEnabled.setValue( material.envMap !== null );
-
-			if ( material.envMap !== null || resetTextureSelectors ) {
-
-				materialEnvMap.setValue( material.envMap );
-
-			}
-
-		}
-
-		if ( material.gradientMap !== undefined ) {
-
-			materialGradientMapEnabled.setValue( material.gradientMap !== null );
-
-			if ( material.gradientMap !== null || resetTextureSelectors ) {
-
-				materialGradientMap.setValue( material.gradientMap );
-
-			}
-
-		}
-
-		if ( material.reflectivity !== undefined ) {
-
-			materialReflectivity.setValue( material.reflectivity );
-
-		}
-
-		if ( material.lightMap !== undefined ) {
-
-			materialLightMapEnabled.setValue( material.lightMap !== null );
-
-			if ( material.lightMap !== null || resetTextureSelectors ) {
-
-				materialLightMap.setValue( material.lightMap );
-
-			}
-
-		}
-
-		if ( material.aoMap !== undefined ) {
-
-			materialAOMapEnabled.setValue( material.aoMap !== null );
-
-			if ( material.aoMap !== null || resetTextureSelectors ) {
-
-				materialAOMap.setValue( material.aoMap );
-
-			}
-
-			materialAOScale.setValue( material.aoMapIntensity );
-
-		}
-
-		if ( material.emissiveMap !== undefined ) {
-
-			materialEmissiveMapEnabled.setValue( material.emissiveMap !== null );
-
-			if ( material.emissiveMap !== null || resetTextureSelectors ) {
-
-				materialEmissiveMap.setValue( material.emissiveMap );
-
-			}
-
-		}
-
-		if ( material.side !== undefined ) {
-
-			materialSide.setValue( material.side );
-
-		}
-
-		if ( material.size !== undefined ) {
-
-			materialSize.setValue( material.size );
-
-		}
-
-		if ( material.sizeAttenuation !== undefined ) {
-
-			materialSizeAttenuation.setValue( material.sizeAttenuation );
-
-		}
-
-		if ( material.flatShading !== undefined ) {
-
-			materialShading.setValue( material.flatShading );
-
-		}
-
-		if ( material.blending !== undefined ) {
-
-			materialBlending.setValue( material.blending );
-
-		}
-
-		if ( material.opacity !== undefined ) {
-
-			materialOpacity.setValue( material.opacity );
-
-		}
-
-		if ( material.transparent !== undefined ) {
-
-			materialTransparent.setValue( material.transparent );
-
-		}
-
-		if ( material.alphaTest !== undefined ) {
-
-			materialAlphaTest.setValue( material.alphaTest );
-
-		}
-
-		if ( material.depthTest !== undefined ) {
-
-			materialDepthTest.setValue( material.depthTest );
-
-		}
-
-		if ( material.depthWrite !== undefined ) {
-
-			materialDepthWrite.setValue( material.depthWrite );
-
-		}
-
-		if ( material.wireframe !== undefined ) {
-
-			materialWireframe.setValue( material.wireframe );
-
-		}
-
-		setRowVisibility();
-
-	}
-
-	// events
-
-	signals.objectSelected.add( function ( object ) {
-
-		var hasMaterial = false;
-
-		if ( object && object.material ) {
-
-			hasMaterial = true;
-
-			if ( Array.isArray( object.material ) && object.material.length === 0 ) {
-
-				hasMaterial = false;
-
-			}
-
-		}
-
-		if ( hasMaterial ) {
-
-			var objectChanged = object !== currentObject;
-
-			currentObject = object;
-			refreshUI( objectChanged );
-			container.setDisplay( '' );
-
-		} else {
-
-			currentObject = null;
-			container.setDisplay( 'none' );
-
-		}
-
-	} );
-
-	signals.materialChanged.add( function () {
-
-		refreshUI();
-
-	} );
-
-	var vertexShaderVariables = [
-		'uniform mat4 projectionMatrix;',
-		'uniform mat4 modelViewMatrix;\n',
-		'attribute vec3 position;\n\n',
-	].join( '\n' );
-
-	var meshMaterialOptions = {
-		'MeshBasicMaterial': 'MeshBasicMaterial',
-		'MeshDepthMaterial': 'MeshDepthMaterial',
-		'MeshNormalMaterial': 'MeshNormalMaterial',
-		'MeshLambertMaterial': 'MeshLambertMaterial',
-		'MeshMatcapMaterial': 'MeshMatcapMaterial',
-		'MeshPhongMaterial': 'MeshPhongMaterial',
-		'MeshToonMaterial': 'MeshToonMaterial',
-		'MeshStandardMaterial': 'MeshStandardMaterial',
-		'MeshPhysicalMaterial': 'MeshPhysicalMaterial',
-		'RawShaderMaterial': 'RawShaderMaterial',
-		'ShaderMaterial': 'ShaderMaterial',
-		'ShadowMaterial': 'ShadowMaterial'
-	};
-
-	var lineMaterialOptions = {
-		'LineBasicMaterial': 'LineBasicMaterial',
-		'LineDashedMaterial': 'LineDashedMaterial',
-		'RawShaderMaterial': 'RawShaderMaterial',
-		'ShaderMaterial': 'ShaderMaterial'
-	};
-
-	var spriteMaterialOptions = {
-		'SpriteMaterial': 'SpriteMaterial',
-		'RawShaderMaterial': 'RawShaderMaterial',
-		'ShaderMaterial': 'ShaderMaterial'
-	};
-
-	var pointsMaterialOptions = {
-		'PointsMaterial': 'PointsMaterial',
-		'RawShaderMaterial': 'RawShaderMaterial',
-		'ShaderMaterial': 'ShaderMaterial'
-	};
-
-	return container;
-
-}
+const pointsMaterialOptions = {
+	'PointsMaterial': 'PointsMaterial',
+	'RawShaderMaterial': 'RawShaderMaterial',
+	'ShaderMaterial': 'ShaderMaterial'
+};
 
 export { SidebarMaterial };

+ 15 - 39
editor/js/Strings.js

@@ -243,6 +243,7 @@ function Strings( config ) {
 			'sidebar/material/depthPacking': 'Depth Packing',
 			'sidebar/material/roughness': 'Roughness',
 			'sidebar/material/metalness': 'Metalness',
+			'sidebar/material/reflectivity': 'Reflectivity',
 			'sidebar/material/sheen': 'Sheen',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/emissive': 'Emissive',
@@ -257,9 +258,9 @@ function Strings( config ) {
 			'sidebar/material/bumpmap': 'Bump Map',
 			'sidebar/material/normalmap': 'Normal Map',
 			'sidebar/material/clearcoatnormalmap': 'Clearcoat Normal Map',
-			'sidebar/material/displacemap': 'Displace Map',
-			'sidebar/material/roughmap': 'Rough. Map',
-			'sidebar/material/metalmap': 'Metal. Map',
+			'sidebar/material/displacementmap': 'Displace Map',
+			'sidebar/material/roughnessmap': 'Rough. Map',
+			'sidebar/material/metalnessmap': 'Metal. Map',
 			'sidebar/material/specularmap': 'Specular Map',
 			'sidebar/material/envmap': 'Env Map',
 			'sidebar/material/lightmap': 'Light Map',
@@ -267,19 +268,10 @@ function Strings( config ) {
 			'sidebar/material/emissivemap': 'Emissive Map',
 			'sidebar/material/gradientmap': 'Gradient Map',
 			'sidebar/material/side': 'Side',
-			'sidebar/material/side/front': 'Front',
-			'sidebar/material/side/back': 'Back',
-			'sidebar/material/side/double': 'Double',
 			'sidebar/material/size': 'Size',
 			'sidebar/material/sizeAttenuation': 'Size Attenuation',
-			'sidebar/material/flatshaded': 'Flat Shaded',
+			'sidebar/material/flatShading': 'Flat Shading',
 			'sidebar/material/blending': 'Blending',
-			'sidebar/material/blending/no': 'No',
-			'sidebar/material/blending/normal': 'Normal',
-			'sidebar/material/blending/additive': 'Additive',
-			'sidebar/material/blending/subtractive': 'Subtractive',
-			'sidebar/material/blending/multiply': 'Multiply',
-			'sidebar/material/blending/custom': 'Custom',
 			'sidebar/material/opacity': 'Opacity',
 			'sidebar/material/transparent': 'Transparent',
 			'sidebar/material/alphatest': 'Alpha Test',
@@ -577,6 +569,7 @@ function Strings( config ) {
 			'sidebar/material/depthPacking': 'Encodage profondeur de couleur',
 			'sidebar/material/roughness': 'Rugosité',
 			'sidebar/material/metalness': 'Métal',
+			'sidebar/material/reflectivity': 'Reflectivity',
 			'sidebar/material/sheen': 'Éclat',
 			'sidebar/material/transmission': 'Transmission',
 			'sidebar/material/emissive': 'Émissif',
@@ -591,9 +584,9 @@ function Strings( config ) {
 			'sidebar/material/bumpmap': 'Texture de relief',
 			'sidebar/material/normalmap': 'Texture de normales',
 			'sidebar/material/clearcoatnormalmap': 'Texture des normales du vernis',
-			'sidebar/material/displacemap': 'Texture de déplacement',
-			'sidebar/material/roughmap': 'Texture de rugosité',
-			'sidebar/material/metalmap': 'Texture métallique',
+			'sidebar/material/displacementmap': 'Texture de déplacement',
+			'sidebar/material/roughnessmap': 'Texture de rugosité',
+			'sidebar/material/metalnessmap': 'Texture métallique',
 			'sidebar/material/specularmap': 'Texture spéculaire',
 			'sidebar/material/envmap': 'Texture d\'environnement',
 			'sidebar/material/lightmap': 'Texture d\'éclairage',
@@ -601,19 +594,10 @@ function Strings( config ) {
 			'sidebar/material/emissivemap': 'Texture d\'émission',
 			'sidebar/material/gradientmap': 'Texture de gradient',
 			'sidebar/material/side': 'Côté',
-			'sidebar/material/side/front': 'Face avant',
-			'sidebar/material/side/back': 'Face Arrière',
-			'sidebar/material/side/double': 'Double face',
 			'sidebar/material/size': 'Size',
 			'sidebar/material/sizeAttenuation': 'Size Attenuation',
-			'sidebar/material/flatshaded': 'Rendu plat',
+			'sidebar/material/flatShading': 'Flat Shading',
 			'sidebar/material/blending': 'Mélange',
-			'sidebar/material/blending/no': 'Non',
-			'sidebar/material/blending/normal': 'Normal',
-			'sidebar/material/blending/additive': 'Ajouter',
-			'sidebar/material/blending/subtractive': 'Soustraire',
-			'sidebar/material/blending/multiply': 'Multiplier',
-			'sidebar/material/blending/custom': 'Personnaliser',
 			'sidebar/material/opacity': 'Opacité',
 			'sidebar/material/transparent': 'Transparence',
 			'sidebar/material/alphatest': 'Test de transparence',
@@ -911,6 +895,7 @@ function Strings( config ) {
 			'sidebar/material/depthPacking': '深度包装',
 			'sidebar/material/roughness': '粗糙度',
 			'sidebar/material/metalness': '金属度',
+			'sidebar/material/reflectivity': 'Reflectivity',
 			'sidebar/material/sheen': '光泽',
 			'sidebar/material/transmission': '透射',
 			'sidebar/material/emissive': '自发光',
@@ -925,9 +910,9 @@ function Strings( config ) {
 			'sidebar/material/bumpmap': '凹凸贴图',
 			'sidebar/material/normalmap': '法线贴图',
 			'sidebar/material/clearcoatnormalmap': '清漆法线贴图',
-			'sidebar/material/displacemap': '置换贴图',
-			'sidebar/material/roughmap': '粗糙贴图',
-			'sidebar/material/metalmap': '金属贴图',
+			'sidebar/material/displacementmap': '置换贴图',
+			'sidebar/material/roughnessmap': '粗糙贴图',
+			'sidebar/material/metalnessmap': '金属贴图',
 			'sidebar/material/specularmap': '高光贴图',
 			'sidebar/material/envmap': '环境贴图',
 			'sidebar/material/lightmap': '光照贴图',
@@ -935,19 +920,10 @@ function Strings( config ) {
 			'sidebar/material/emissivemap': '自发光贴图',
 			'sidebar/material/gradientmap': '渐变贴图',
 			'sidebar/material/side': '面',
-			'sidebar/material/side/front': '正面',
-			'sidebar/material/side/back': '背面',
-			'sidebar/material/side/double': '双面',
 			'sidebar/material/size': '大小',
 			'sidebar/material/sizeAttenuation': '大小衰减',
-			'sidebar/material/flatshaded': '平面着色',
+			'sidebar/material/flatShading': 'Flat Shading',
 			'sidebar/material/blending': '混合',
-			'sidebar/material/blending/no': '无',
-			'sidebar/material/blending/normal': '正常混合',
-			'sidebar/material/blending/additive': '和混合',
-			'sidebar/material/blending/subtractive': '差混合',
-			'sidebar/material/blending/multiply': '积混合',
-			'sidebar/material/blending/custom': '自定义混合',
 			'sidebar/material/opacity': '透明度',
 			'sidebar/material/transparent': '透明性',
 			'sidebar/material/alphatest': 'α测试',

+ 6 - 0
editor/sw.js

@@ -175,6 +175,12 @@ const assets = [
 	'./js/Sidebar.Geometry.TubeGeometry.js',
 	'./js/Sidebar.Geometry.TeapotGeometry.js',
 	'./js/Sidebar.Material.js',
+	'./js/Sidebar.Material.BooleanProperty.js',
+	'./js/Sidebar.Material.ColorProperty.js',
+	'./js/Sidebar.Material.ConstantProperty.js',
+	'./js/Sidebar.Material.MapProperty.js',
+	'./js/Sidebar.Material.NumberProperty.js',
+	'./js/Sidebar.Material.Program.js',
 	'./js/Sidebar.Animation.js',
 	'./js/Sidebar.Script.js',
 	'./js/Strings.js',