Browse Source

Merge branch 'dev' into dev-node-sides

sunag 5 years ago
parent
commit
251a312cda
61 changed files with 1119 additions and 783 deletions
  1. 14 3
      build/three.js
  2. 316 329
      build/three.min.js
  3. 14 3
      build/three.module.js
  4. 3 3
      docs/api/en/math/Vector3.html
  5. 5 5
      docs/api/en/textures/DataTexture3D.html
  6. 1 1
      docs/api/zh/textures/DataTexture3D.html
  7. 0 1
      docs/manual/en/introduction/How-to-update-things.html
  8. 0 1
      editor/css/dark.css
  9. 0 1
      editor/css/light.css
  10. 1 0
      editor/css/main.css
  11. 30 0
      editor/js/Editor.js
  12. 4 47
      editor/js/Sidebar.Material.js
  13. 75 5
      editor/js/Sidebar.Project.js
  14. 1 0
      editor/js/Sidebar.Properties.js
  15. 1 0
      editor/js/Sidebar.js
  16. 2 0
      editor/js/Strings.js
  17. 38 34
      editor/js/libs/ui.js
  18. 26 5
      editor/sw.js
  19. 1 0
      examples/files.js
  20. 1 1
      examples/js/libs/basis/README.md
  21. 0 0
      examples/js/libs/basis/basis_transcoder.js
  22. BIN
      examples/js/libs/basis/basis_transcoder.wasm
  23. 71 55
      examples/js/loaders/BasisTextureLoader.js
  24. 7 8
      examples/js/loaders/GLTFLoader.js
  25. 0 14
      examples/jsm/controls/OrbitControls.d.ts
  26. 1 0
      examples/jsm/loaders/BasisTextureLoader.d.ts
  27. 73 55
      examples/jsm/loaders/BasisTextureLoader.js
  28. 7 8
      examples/jsm/loaders/GLTFLoader.js
  29. 4 4
      examples/misc_exporter_collada.html
  30. 4 4
      examples/webgl_geometry_teapot.html
  31. 132 0
      examples/webgl_instancing.html
  32. 0 1
      examples/webgl_loader_texture_basis.html
  33. 1 1
      examples/webgl_nearestneighbour.html
  34. 4 4
      examples/webgl_water.html
  35. 2 2
      examples/webvr_multiview.html
  36. 1 0
      src/Three.js
  37. 7 0
      src/core/Geometry.js
  38. 7 0
      src/materials/Material.d.ts
  39. 7 3
      src/math/Vector3.js
  40. 34 0
      src/objects/InstancedMesh.js
  41. 1 1
      src/renderers/WebGLRenderer.d.ts
  42. 37 20
      src/renderers/WebGLRenderer.js
  43. 3 0
      src/renderers/shaders/ShaderChunk/common.glsl.js
  44. 9 1
      src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js
  45. 1 7
      src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js
  46. 9 1
      src/renderers/shaders/ShaderChunk/project_vertex.glsl.js
  47. 2 1
      src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js
  48. 9 1
      src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js
  49. 5 3
      src/renderers/webgl/WebGLBufferRenderer.js
  50. 1 8
      src/renderers/webgl/WebGLCapabilities.js
  51. 5 3
      src/renderers/webgl/WebGLIndexedBufferRenderer.js
  52. 49 67
      src/renderers/webgl/WebGLMultiview.js
  53. 7 1
      src/renderers/webgl/WebGLObjects.js
  54. 1 2
      src/renderers/webgl/WebGLProgram.d.ts
  55. 38 25
      src/renderers/webgl/WebGLProgram.js
  56. 7 2
      src/renderers/webgl/WebGLPrograms.js
  57. 29 37
      src/renderers/webgl/WebGLTextures.js
  58. 1 1
      src/textures/DataTexture2DArray.js
  59. 1 1
      src/textures/DataTexture3D.js
  60. 7 2
      test/unit/src/math/Euler.tests.js
  61. 2 1
      test/unit/src/math/Quaternion.tests.js

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


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


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


+ 3 - 3
docs/api/en/math/Vector3.html

@@ -121,7 +121,7 @@ var d = a.distanceTo( b );
 
 		<h3>[method:Float angleTo]( [param:Vector3 v] )</h3>
 		<p>
-		Returns the angle between this vector and vector [page:Vector3 v] in radians.
+		Returns the angle between this vector and vector [page:Vector3 v] in radians. Neither this vector nor [page:Vector3 v] can be the zero vector.
 		</p>
 
 		<h3>[method:this ceil]()</h3>
@@ -320,8 +320,8 @@ var d = a.distanceTo( b );
 		normal from this vector.
 		</p>
 
-		<h3>[method:this projectOnVector]( [param:Vector3] )</h3>
-		<p>[link:https://en.wikipedia.org/wiki/Vector_projection Projects] this vector onto another vector.</p>
+		<h3>[method:this projectOnVector]( [param:Vector3 v] )</h3>
+		<p>[link:https://en.wikipedia.org/wiki/Vector_projection Projects] this vector onto [page:Vector3 v]. [page:Vector3 v] cannot be the zero vector.</p>
 
 		<h3>[method:this reflect]( [param:Vector3 normal] )</h3>
 		<p>

+ 5 - 5
docs/api/en/textures/DataTexture3D.html

@@ -30,15 +30,15 @@
 		<h2>Example</h2>
 
 		<div>[example:webgl2_materials_texture3d WebGL2 / materials / texture3d]</div>
-		<div>[example:webgl2_materials_texture3d_volume WebGL2 / materials / texture3d / volume]</div>
+		<div>[example:webgl2_materials_texture2darray WebGL2 / materials / texture2darray]</div>
 
-    <h2>Properties</h2>
+		<h2>Properties</h2>
 
 		<p>
-    See the base [page:Texture Texture] class for common properties.
-    </p>
+		See the base [page:Texture Texture] class for common properties.
+		</p>
 
-    <h3>[property:number wrapR]</h3>
+		<h3>[property:number wrapR]</h3>
 		<p>
 		This defines how the texture is wrapped in the depth direction.<br />
 		The default is [page:Textures THREE.ClampToEdgeWrapping], where the edge is clamped to the outer edge texels.

+ 1 - 1
docs/api/zh/textures/DataTexture3D.html

@@ -30,7 +30,7 @@
 		<h2>示例</h2>
 
 		<div>[example:webgl2_materials_texture3d WebGL2 / materials / texture3d]</div>
-		<div>[example:webgl2_materials_texture3d_volume WebGL2 / materials / texture3d / volume]</div>
+		<div>[example:webgl2_materials_texture2darray WebGL2 / materials / texture2darray]</div>
 
 		<h2>属性</h2>
 

+ 0 - 1
docs/manual/en/introduction/How-to-update-things.html

@@ -181,7 +181,6 @@ geometry.tangentsNeedUpdate = true;
 			<p>The following properties can't be easily changed at runtime (once the material is rendered at least once):</p>
 			<ul>
 				<li>numbers and types of uniforms</li>
-				<li>numbers and types of lights</li>
 				<li>presence or not of
 					<ul>
 						<li>texture</li>

+ 0 - 1
editor/css/dark.css

@@ -273,7 +273,6 @@ select {
 
 .Panel {
 	color: #888;
-	border-top: 1px solid #222;
 }
 /* */
 

+ 0 - 1
editor/css/light.css

@@ -267,7 +267,6 @@ select {
 
 .Panel {
 	color: #888;
-	border-top: 1px solid #ccc;
 }
 /* */
 

+ 1 - 0
editor/css/main.css

@@ -63,6 +63,7 @@ textarea, input { outline: none; } /* osx */
 .TabbedPanel .Tabs .Tab {
 	padding: 10px;
 	vertical-align: middle;
+	text-transform: uppercase;
 }
 
 .TabbedPanel .Tabs .Panels {

+ 30 - 0
editor/js/Editor.js

@@ -57,7 +57,9 @@ var Editor = function () {
 		helperAdded: new Signal(),
 		helperRemoved: new Signal(),
 
+		materialAdded: new Signal(),
 		materialChanged: new Signal(),
+		materialRemoved: new Signal(),
 
 		scriptAdded: new Signal(),
 		scriptChanged: new Signal(),
@@ -234,6 +236,34 @@ Editor.prototype = {
 	addMaterial: function ( material ) {
 
 		this.materials[ material.uuid ] = material;
+		this.signals.materialAdded.dispatch();
+
+	},
+
+	removeMaterial: function ( material ) {
+
+		delete this.materials[ material.uuid ];
+		this.signals.materialRemoved.dispatch();
+
+	},
+
+	getMaterialById: function ( id ) {
+
+		var material;
+		var materials = Object.values( this.materials );
+
+		for ( var i = 0; i < materials.length; i ++ ) {
+
+			if ( materials[ i ].id === id ) {
+
+				material = materials[ i ];
+				break;
+
+			}
+
+		}
+
+		return material;
 
 	},
 

+ 4 - 47
editor/js/Sidebar.Material.js

@@ -17,12 +17,6 @@ Sidebar.Material = function ( editor ) {
 	container.setDisplay( 'none' );
 	container.setPaddingTop( '20px' );
 
-	// New / Copy / Paste
-
-	var copiedMaterial;
-
-	var managerRow = new UI.Row();
-
 	// Current material slot
 
 	var materialSlotRow = new UI.Row();
@@ -35,43 +29,6 @@ Sidebar.Material = function ( editor ) {
 
 	container.add( materialSlotRow );
 
-	managerRow.add( new UI.Text( '' ).setWidth( '90px' ) );
-
-	managerRow.add( new UI.Button( strings.getKey( 'sidebar/material/new' ) ).onClick( function () {
-
-		var material = new THREE[ materialClass.getValue() ]();
-		editor.execute( new SetMaterialCommand( editor, currentObject, material, currentMaterialSlot ), 'New Material: ' + materialClass.getValue() );
-		update();
-
-	} ) );
-
-	managerRow.add( new UI.Button( strings.getKey( 'sidebar/material/copy' ) ).setMarginLeft( '4px' ).onClick( function () {
-
-		copiedMaterial = currentObject.material;
-
-		if ( Array.isArray( copiedMaterial ) ) {
-
-			if ( copiedMaterial.length === 0 ) return;
-
-			copiedMaterial = copiedMaterial[ currentMaterialSlot ];
-
-		}
-
-	} ) );
-
-	managerRow.add( new UI.Button( strings.getKey( 'sidebar/material/paste' ) ).setMarginLeft( '4px' ).onClick( function () {
-
-		if ( copiedMaterial === undefined ) return;
-
-		editor.execute( new SetMaterialCommand( editor, currentObject, copiedMaterial, currentMaterialSlot ), 'Pasted Material: ' + materialClass.getValue() );
-		refreshUI();
-		update();
-
-	} ) );
-
-	container.add( managerRow );
-
-
 	// type
 
 	var materialClassRow = new UI.Row();
@@ -655,9 +612,9 @@ Sidebar.Material = function ( editor ) {
 				var sheen = sheenEnabled ? new THREE.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 ) );
@@ -1297,9 +1254,9 @@ Sidebar.Material = function ( editor ) {
 			materialMetalness.setValue( material.metalness );
 
 		}
-		
+
 		if ( material.sheen !== undefined && material.sheen !== null ) {
-			
+
 			materialSheenEnabled.setValue( true );
 			materialSheen.setHexValue( material.sheen.getHexString() );
 

+ 75 - 5
editor/js/Sidebar.Project.js

@@ -19,8 +19,14 @@ Sidebar.Project = function ( editor ) {
 
 	var container = new UI.Panel();
 	container.setBorderTop( '0' );
+	container.setPadding( '0' );
 	container.setPaddingTop( '20px' );
 
+	var projectsettings = new UI.Panel();
+	projectsettings.setBorderTop( '0' );
+
+	container.add( projectsettings );
+
 	// Title
 
 	var titleRow = new UI.Row();
@@ -33,7 +39,7 @@ Sidebar.Project = function ( editor ) {
 	titleRow.add( new UI.Text( strings.getKey( 'sidebar/project/title' ) ).setWidth( '90px' ) );
 	titleRow.add( title );
 
-	container.add( titleRow );
+	projectsettings.add( titleRow );
 
 	// Editable
 
@@ -47,7 +53,7 @@ Sidebar.Project = function ( editor ) {
 	editableRow.add( new UI.Text( strings.getKey( 'sidebar/project/editable' ) ).setWidth( '90px' ) );
 	editableRow.add( editable );
 
-	container.add( editableRow );
+	projectsettings.add( editableRow );
 
 	// VR
 
@@ -61,7 +67,7 @@ Sidebar.Project = function ( editor ) {
 	vrRow.add( new UI.Text( strings.getKey( 'sidebar/project/vr' ) ).setWidth( '90px' ) );
 	vrRow.add( vr );
 
-	container.add( vrRow );
+	projectsettings.add( vrRow );
 
 	// Renderer
 
@@ -89,7 +95,7 @@ Sidebar.Project = function ( editor ) {
 	rendererTypeRow.add( new UI.Text( strings.getKey( 'sidebar/project/renderer' ) ).setWidth( '90px' ) );
 	rendererTypeRow.add( rendererType );
 
-	container.add( rendererTypeRow );
+	projectsettings.add( rendererTypeRow );
 
 	if ( config.getKey( 'project/renderer' ) !== undefined ) {
 
@@ -119,7 +125,7 @@ Sidebar.Project = function ( editor ) {
 	} );
 	rendererPropertiesRow.add( rendererShadows );
 
-	container.add( rendererPropertiesRow );
+	projectsettings.add( rendererPropertiesRow );
 
 	//
 
@@ -165,6 +171,70 @@ Sidebar.Project = function ( editor ) {
 
 	createRenderer( config.getKey( 'project/renderer' ), config.getKey( 'project/renderer/antialias' ), config.getKey( 'project/renderer/shadows' ) );
 
+	// Materials
+
+	var materials = new UI.Panel();
+
+	var headerRow = new UI.Row();
+	headerRow.add( new UI.Text( strings.getKey( 'sidebar/project/materials' ) ) );
+
+	materials.add( headerRow );
+
+	var listbox = new UI.Listbox();
+	signals.materialAdded.add( function () {
+
+		listbox.setItems( Object.values( editor.materials ) );
+
+	} );
+	materials.add( listbox );
+
+	var buttonsRow = new UI.Row();
+	buttonsRow.setPadding( '10px 0px' );
+	materials.add( buttonsRow );
+
+	/*
+	var addButton = new UI.Button().setLabel( 'Add' ).setMarginRight( '5px' );
+	addButton.onClick( function () {
+
+		editor.addMaterial( new THREE.MeshStandardMaterial() );
+
+	} );
+	buttonsRow.add( addButton );
+	*/
+
+	var assignMaterial = new UI.Button().setLabel( 'Assign' ).setMargin( '0px 5px' );
+	assignMaterial.onClick( function () {
+
+		if ( editor.selected !== null ) {
+
+			var material = editor.getMaterialById( parseInt( listbox.getValue() ) );
+
+			if ( material !== undefined ) {
+
+				editor.execute( new SetMaterialCommand( editor, editor.selected, material ) );
+
+			}
+
+		}
+
+	} );
+	buttonsRow.add( assignMaterial );
+
+	container.add( materials );
+
+	// events
+
+	signals.objectSelected.add( function ( object ) {
+
+		if ( object !== null ) {
+
+			var index = Object.values( editor.materials ).indexOf( object.material );
+			listbox.selectIndex( index );
+
+		}
+
+	} );
+
 	return container;
 
 };

+ 1 - 0
editor/js/Sidebar.Properties.js

@@ -12,6 +12,7 @@ Sidebar.Properties = function ( editor ) {
 	container.addTab( 'object', strings.getKey( 'sidebar/properties/object' ), new Sidebar.Object( editor ) );
 	container.addTab( 'geometry', strings.getKey( 'sidebar/properties/geometry' ), new Sidebar.Geometry( editor ) );
 	container.addTab( 'material', strings.getKey( 'sidebar/properties/material' ), new Sidebar.Material( editor ) );
+	container.select( 'object' );
 
 	return container;
 

+ 1 - 0
editor/js/Sidebar.js

@@ -24,6 +24,7 @@ var Sidebar = function ( editor ) {
 	container.addTab( 'scene', strings.getKey( 'sidebar/scene' ), scene );
 	container.addTab( 'project', strings.getKey( 'sidebar/project' ), new Sidebar.Project( editor ) );
 	container.addTab( 'settings', strings.getKey( 'sidebar/settings' ), settings );
+	container.select( 'scene' );
 
 	return container;
 

+ 2 - 0
editor/js/Strings.js

@@ -280,6 +280,7 @@ var Strings = function ( config ) {
 			'sidebar/project/renderer': 'Renderer',
 			'sidebar/project/antialias': 'antialias',
 			'sidebar/project/shadows': 'shadows',
+			'sidebar/project/materials': 'Materials',
 
 			'sidebar/settings': 'Settings',
 			'sidebar/settings/language': 'Language',
@@ -555,6 +556,7 @@ var Strings = function ( config ) {
 			'sidebar/project/renderer': '渲染器',
 			'sidebar/project/antialias': '抗锯齿',
 			'sidebar/project/shadows': '阴影',
+			'sidebar/project/materials': 'Materials',
 
 			'sidebar/settings': '设置',
 			'sidebar/settings/language': '语言',

+ 38 - 34
editor/js/libs/ui.js

@@ -1025,8 +1025,8 @@ UI.TabbedPanel = function ( ) {
 
 	UI.Element.call( this );
 
-	var dom = document.createElement('div');
-	
+	var dom = document.createElement( 'div' );
+
 	this.dom = dom;
 
 	this.setClass( 'TabbedPanel' );
@@ -1047,7 +1047,7 @@ UI.TabbedPanel = function ( ) {
 
 	return this;
 
-}
+};
 
 UI.TabbedPanel.prototype = Object.create( UI.Element.prototype );
 UI.TabbedPanel.prototype.constructor = UI.TabbedPanel;
@@ -1057,9 +1057,10 @@ UI.TabbedPanel.prototype.select = function ( id ) {
 	var tab;
 	var panel;
 	var scope = this;
-	
+
 	// Deselect current selection
 	if ( this.selected && this.selected.length ) {
+
 		tab = this.tabs.find( function ( item ) { return item.dom.id === scope.selected } );
 		panel = this.panels.find( function ( item ) { return item.dom.id === scope.selected } );
 
@@ -1069,7 +1070,7 @@ UI.TabbedPanel.prototype.select = function ( id ) {
 
 		}
 
-		if( panel ) {
+		if ( panel ) {
 
 			panel.setDisplay( 'none' );
 
@@ -1079,14 +1080,14 @@ UI.TabbedPanel.prototype.select = function ( id ) {
 
 	tab = this.tabs.find( function ( item ) { return item.dom.id === id } );
 	panel = this.panels.find( function ( item ) { return item.dom.id === id } );
-	
+
 	if ( tab ) {
 
 		tab.addClass( 'selected' );
 
 	}
 
-	if( panel ) {
+	if ( panel ) {
 
 		panel.setDisplay( '' );
 
@@ -1096,7 +1097,7 @@ UI.TabbedPanel.prototype.select = function ( id ) {
 
 	return this;
 
-}
+};
 
 UI.TabbedPanel.prototype.addTab = function ( id, label, items ) {
 
@@ -1114,7 +1115,7 @@ UI.TabbedPanel.prototype.addTab = function ( id, label, items ) {
 
 	this.select( id );
 
-}
+};
 
 UI.TabbedPanel.Tab = function ( text, parent ) {
 
@@ -1124,15 +1125,16 @@ UI.TabbedPanel.Tab = function ( text, parent ) {
 	this.setClass( 'Tab' );
 
 	var scope = this;
+
 	this.dom.addEventListener( 'click', function ( event ) {
 
 		scope.parent.select( scope.dom.id );
 
-	} )
+	} );
 
 	return this;
 
-}
+};
 
 UI.TabbedPanel.Tab.prototype = Object.create( UI.Text.prototype );
 UI.TabbedPanel.Tab.prototype.constructor = UI.TabbedPanel.Tab;
@@ -1154,10 +1156,10 @@ UI.Listbox = function ( ) {
 
 	return this;
 
-}
+};
 
 UI.Listbox.prototype = Object.create( UI.Element.prototype );
-UI.Listbox.prototype.constructor = UI.ListboxItem;
+UI.Listbox.prototype.constructor = UI.Listbox;
 
 UI.Listbox.prototype.setItems = function ( items ) {
 
@@ -1167,25 +1169,25 @@ UI.Listbox.prototype.setItems = function ( items ) {
 
 	}
 
-	this.render( );
+	this.render();
 
-}
+};
 
 UI.Listbox.prototype.render = function ( ) {
 
-	while( this.listitems.length ) {
+	while ( this.listitems.length ) {
 
-		var item = this.listitems[0];
+		var item = this.listitems[ 0 ];
 
 		item.dom.remove();
 
-		this.listitems.splice(0, 1);
+		this.listitems.splice( 0, 1 );
 
 	}
 
 	for ( var i = 0; i < this.items.length; i ++ ) {
 
-		var item = this.items[i];
+		var item = this.items[ i ];
 
 		var listitem = new UI.Listbox.ListboxItem( this );
 		listitem.setId( item.id || `Listbox-${i}` );
@@ -1194,10 +1196,10 @@ UI.Listbox.prototype.render = function ( ) {
 
 	}
 
-}
+};
 
 // Assuming user passes valid list items
-UI.Listbox.prototype.add = function (  ) {
+UI.Listbox.prototype.add = function () {
 
 	var items = Array.from( arguments );
 
@@ -1205,25 +1207,25 @@ UI.Listbox.prototype.add = function (  ) {
 
 	UI.Element.prototype.add.apply( this, items );
 
-}
+};
 
 UI.Listbox.prototype.selectIndex = function ( index ) {
 
 	if ( index >= 0 && index < this.items.length ) {
 
-		this.setValue( this.listitems[ index ].getId(  ) );
+		this.setValue( this.listitems[ index ].getId() );
 
 	}
 
-	this.selectIndex = index;
+	this.selectedIndex = index;
 
-}
+};
 
-UI.Listbox.prototype.getValue = function ( index ) {
+UI.Listbox.prototype.getValue = function () {
 
 	return this.selectedValue;
 
-}
+};
 
 UI.Listbox.prototype.setValue = function ( value ) {
 
@@ -1231,12 +1233,12 @@ UI.Listbox.prototype.setValue = function ( value ) {
 
 		var element = this.listitems[ i ];
 
-		if ( element.getId( ) === value ) {
+		if ( element.getId() === value ) {
 
 			element.addClass( 'active' );
 
 		} else {
-			
+
 			element.removeClass( 'active' );
 
 		}
@@ -1249,7 +1251,7 @@ UI.Listbox.prototype.setValue = function ( value ) {
 	changeEvent.initEvent( 'change', true, true );
 	this.dom.dispatchEvent( changeEvent );
 
-}
+};
 
 // Listbox Item
 UI.Listbox.ListboxItem = function ( parent ) {
@@ -1264,10 +1266,12 @@ UI.Listbox.ListboxItem = function ( parent ) {
 
 	var scope = this;
 
-	function onClick ( ) {
-		
-		if( scope.parent ) {
+	function onClick() {
+
+		if ( scope.parent ) {
+
 			scope.parent.setValue( scope.getId( ) );
+
 		}
 
 	}
@@ -1276,7 +1280,7 @@ UI.Listbox.ListboxItem = function ( parent ) {
 
 	return this;
 
-}
+};
 
 UI.Listbox.ListboxItem.prototype = Object.create( UI.Element.prototype );
 UI.Listbox.ListboxItem.prototype.constructor = UI.Listbox.ListboxItem;

+ 26 - 5
editor/sw.js

@@ -1,13 +1,12 @@
 // r108
 
-const staticAssets = [
+const assets = [
 	'./',
 
 	'../files/favicon.ico',
 
 	'../build/three.js',
 
-	'../examples/js/controls/EditorControls.js',
 	'../examples/js/controls/TransformControls.js',
 
 	'../examples/js/libs/chevrotain.min.js',
@@ -42,6 +41,9 @@ const staticAssets = [
 	'../examples/js/renderers/SoftwareRenderer.js',
 	'../examples/js/renderers/SVGRenderer.js',
 
+	'./manifest.json',
+	'./images/icon.png',
+
 	'./js/libs/codemirror/codemirror.css',
 	'./js/libs/codemirror/theme/monokai.css',
 
@@ -92,6 +94,7 @@ const staticAssets = [
 	'./css/dark.css',
 	'./css/light.css',
 
+	'./js/EditorControls.js',
 	'./js/Storage.js',
 
 	'./js/Editor.js',
@@ -122,6 +125,7 @@ const staticAssets = [
 	'./js/Sidebar.Geometry.BoxGeometry.js',
 	'./js/Sidebar.Geometry.CircleGeometry.js',
 	'./js/Sidebar.Geometry.CylinderGeometry.js',
+	'./js/Sidebar.Geometry.DodecahedronGeometry.js',
 	'./js/Sidebar.Geometry.ExtrudeGeometry.js',
 	'./js/Sidebar.Geometry.IcosahedronGeometry.js',
 	'./js/Sidebar.Geometry.OctahedronGeometry.js',
@@ -179,10 +183,19 @@ const staticAssets = [
 
 ];
 
-self.addEventListener( 'install', async function ( event ) {
+self.addEventListener( 'install', async function () {
 
 	const cache = await caches.open( 'threejs-editor' );
-	cache.addAll( staticAssets );
+
+	assets.forEach( function ( asset ) {
+
+		cache.add( asset ).catch( function () {
+
+			console.error( '[SW] Cound\'t cache:', asset );
+
+		} );
+
+	} );
 
 } );
 
@@ -196,6 +209,14 @@ self.addEventListener( 'fetch', async function ( event ) {
 async function cacheFirst( request ) {
 
 	const cachedResponse = await caches.match( request );
-	return cachedResponse || fetch( request );
+
+	if ( cachedResponse === undefined ) {
+
+		console.error( '[SW] Not cached:', request.url );
+		return fetch( request );
+
+	}
+
+	return cachedResponse;
 
 }

+ 1 - 0
examples/files.js

@@ -47,6 +47,7 @@ var files = {
 		"webgl_geometry_text_shapes",
 		"webgl_geometry_text_stroke",
 		"webgl_helpers",
+		"webgl_instancing",
 		"webgl_interactive_buffergeometry",
 		"webgl_interactive_cubes",
 		"webgl_interactive_cubes_gpu",

+ 1 - 1
examples/js/libs/basis/README.md

@@ -36,7 +36,7 @@ basisLoader.load( 'diffuse.basis', function ( texture ) {
 ```
 
 For further documentation about the Basis compressor and transcoder, refer to
-the [Basis GitHub repository](https://github.com/BinomialLLC/basis_universal). The JavaScript wrapper requires one modification from the version provided in the Basis repository – the declaration on the first line is changed from `var Module` to `Module`, to accomodate lazy initialization in a Web Worker ([details](https://github.com/mrdoob/three.js/issues/16524)).
+the [Basis GitHub repository](https://github.com/BinomialLLC/basis_universal).
 
 ## License
 

File diff suppressed because it is too large
+ 0 - 0
examples/js/libs/basis/basis_transcoder.js


BIN
examples/js/libs/basis/basis_transcoder.wasm


+ 71 - 55
examples/js/loaders/BasisTextureLoader.js

@@ -4,8 +4,6 @@
  * @author Shrek Shao / https://github.com/shrekshao
  */
 
-/* global Module, createBasisModule */
-
 /**
  * Loader for Basis Universal GPU Texture Codec.
  *
@@ -32,6 +30,7 @@ THREE.BasisTextureLoader = function ( manager ) {
 	this.workerSourceURL = '';
 	this.workerConfig = {
 		format: null,
+		astcSupported: false,
 		etcSupported: false,
 		dxtSupported: false,
 		pvrtcSupported: false,
@@ -63,22 +62,27 @@ THREE.BasisTextureLoader.prototype = Object.assign( Object.create( THREE.Loader.
 
 		var config = this.workerConfig;
 
+		config.astcSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_astc' );
 		config.etcSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_etc1' );
 		config.dxtSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_s3tc' );
 		config.pvrtcSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_pvrtc' )
 			|| !! renderer.extensions.get( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
 
-		if ( config.etcSupported ) {
+		if ( config.astcSupported ) {
 
-			config.format = THREE.BasisTextureLoader.BASIS_FORMAT.cTFETC1;
+			config.format = THREE.BasisTextureLoader.BASIS_FORMAT.cTFASTC_4x4;
 
 		} else if ( config.dxtSupported ) {
 
-			config.format = THREE.BasisTextureLoader.BASIS_FORMAT.cTFBC1;
+			config.format = THREE.BasisTextureLoader.BASIS_FORMAT.cTFBC3;
 
 		} else if ( config.pvrtcSupported ) {
 
-			config.format = THREE.BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_OPAQUE_ONLY;
+			config.format = THREE.BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGBA;
+
+		} else if ( config.etcSupported ) {
+
+			config.format = THREE.BasisTextureLoader.BASIS_FORMAT.cTFETC1;
 
 		} else {
 
@@ -136,25 +140,30 @@ THREE.BasisTextureLoader.prototype = Object.assign( Object.create( THREE.Loader.
 
 				var config = this.workerConfig;
 
-				var { width, height, mipmaps } = message;
+				var { width, height, hasAlpha, mipmaps, format } = message;
 
 				var texture;
 
-				if ( config.etcSupported ) {
-
-					texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_ETC1_Format );
-
-				} else if ( config.dxtSupported ) {
-
-					texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.BasisTextureLoader.DXT_FORMAT_MAP[ config.format ], THREE.UnsignedByteType );
-
-				} else if ( config.pvrtcSupported ) {
-
-					texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_PVRTC_4BPPV1_Format );
-
-				} else {
-
-					throw new Error( 'THREE.BasisTextureLoader: No supported format available.' );
+				switch ( format ) {
+
+					case THREE.BasisTextureLoader.BASIS_FORMAT.cTFASTC_4x4:
+						texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGBA_ASTC_4x4_Format );
+						break;
+					case THREE.BasisTextureLoader.BASIS_FORMAT.cTFBC1:
+					case THREE.BasisTextureLoader.BASIS_FORMAT.cTFBC3:
+						texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.BasisTextureLoader.DXT_FORMAT_MAP[ config.format ], THREE.UnsignedByteType );
+						break;
+					case THREE.BasisTextureLoader.BASIS_FORMAT.cTFETC1:
+						texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_ETC1_Format );
+						break;
+					case THREE.BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGB:
+						texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGB_PVRTC_4BPPV1_Format );
+						break;
+					case THREE.BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGBA:
+						texture = new THREE.CompressedTexture( mipmaps, width, height, THREE.RGBA_PVRTC_4BPPV1_Format );
+						break;
+					default:
+						throw new Error( 'THREE.BasisTextureLoader: No supported format available.' );
 
 				}
 
@@ -214,12 +223,7 @@ THREE.BasisTextureLoader.prototype = Object.assign( Object.create( THREE.Loader.
 
 					var body = [
 						'/* basis_transcoder.js */',
-						'var Module;',
-						'function createBasisModule () {',
-						'  ' + jsContent,
-						'  return Module;',
-						'}',
-						'',
+						jsContent,
 						'/* worker */',
 						fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
 					].join( '\n' );
@@ -312,13 +316,22 @@ THREE.BasisTextureLoader.prototype = Object.assign( Object.create( THREE.Loader.
 
 THREE.BasisTextureLoader.BASIS_FORMAT = {
 	cTFETC1: 0,
-	cTFBC1: 1,
-	cTFBC4: 2,
-	cTFPVRTC1_4_OPAQUE_ONLY: 3,
-	cTFBC7_M6_OPAQUE_ONLY: 4,
-	cTFETC2: 5,
-	cTFBC3: 6,
-	cTFBC5: 7,
+	cTFETC2: 1,
+	cTFBC1: 2,
+	cTFBC3: 3,
+	cTFBC4: 4,
+	cTFBC5: 5,
+	cTFBC7_M6_OPAQUE_ONLY: 6,
+	cTFBC7_M5: 7,
+	cTFPVRTC1_4_RGB: 8,
+	cTFPVRTC1_4_RGBA: 9,
+	cTFASTC_4x4: 10,
+	cTFATC_RGB: 11,
+	cTFATC_RGBA_INTERPOLATED_ALPHA: 12,
+	cTFRGBA32: 13,
+	cTFRGB565: 14,
+	cTFBGR565: 15,
+	cTFRGBA4444: 16,
 };
 
 // DXT formats, from:
@@ -359,7 +372,7 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 					try {
 
-						var { width, height, mipmaps } = transcode( message.buffer );
+						var { width, height, hasAlpha, mipmaps, format } = transcode( message.buffer );
 
 						var buffers = [];
 
@@ -369,7 +382,7 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 						}
 
-						self.postMessage( { type: 'transcode', id: message.id, width, height, mipmaps }, buffers );
+						self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format }, buffers );
 
 					} catch ( error ) {
 
@@ -388,19 +401,15 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 	function init( wasmBinary ) {
 
+		var BasisModule;
 		transcoderPending = new Promise( ( resolve ) => {
 
-			// The 'Module' global is used by the Basis wrapper, which will check for
-			// the 'wasmBinary' property before trying to load the file itself.
-
-			// TODO(donmccurdy): This only works with a modified version of the
-			// emscripten-generated wrapper. The default seems to have a bug making it
-			// impossible to override the WASM binary.
-			Module = { wasmBinary, onRuntimeInitialized: resolve };
+			BasisModule = { wasmBinary, onRuntimeInitialized: resolve };
+			BASIS( BasisModule );
 
 		} ).then( () => {
 
-			var { BasisFile, initializeBasis } = Module;
+			var { BasisFile, initializeBasis } = BasisModule;
 
 			_BasisFile = BasisFile;
 
@@ -408,8 +417,6 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 		} );
 
-		createBasisModule();
-
 	}
 
 	function transcode( buffer ) {
@@ -419,6 +426,7 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 		var width = basisFile.getImageWidth( 0, 0 );
 		var height = basisFile.getImageHeight( 0, 0 );
 		var levels = basisFile.getNumLevels( 0 );
+		var hasAlpha = basisFile.getHasAlpha();
 
 		function cleanup() {
 
@@ -427,6 +435,20 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 		}
 
+		if ( ! hasAlpha ) {
+
+			switch ( config.format ) {
+
+				case 9: // Hardcoded: THREE.BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGBA
+					config.format = 8; // Hardcoded: THREE.BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGB;
+					break;
+				default:
+					break;
+
+			}
+
+		}
+
 		if ( ! width || ! height || ! levels ) {
 
 			cleanup();
@@ -441,12 +463,6 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 		}
 
-		if ( basisFile.getHasAlpha() ) {
-
-			console.warn( 'THREE.BasisTextureLoader: Alpha not yet implemented.' );
-
-		}
-
 		var mipmaps = [];
 
 		for ( var mip = 0; mip < levels; mip ++ ) {
@@ -460,7 +476,7 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 				0,
 				mip,
 				config.format,
-				config.etcSupported ? 0 : ( config.dxtSupported ? 1 : 0 ),
+				hasAlpha,
 				0
 			);
 
@@ -477,7 +493,7 @@ THREE.BasisTextureLoader.BasisWorker = function () {
 
 		cleanup();
 
-		return { width, height, mipmaps };
+		return { width, height, hasAlpha, mipmaps, format: config.format };
 
 	}
 

+ 7 - 8
examples/js/loaders/GLTFLoader.js

@@ -269,8 +269,7 @@ THREE.GLTFLoader = ( function () {
 	/**
 	 * DDS Texture Extension
 	 *
-	 * Specification:
-	 * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
 	 *
 	 */
 	function GLTFTextureDDSExtension( ddsLoader ) {
@@ -287,9 +286,9 @@ THREE.GLTFLoader = ( function () {
 	}
 
 	/**
-	 * Lights Extension
+	 * Punctual Lights Extension
 	 *
-	 * Specification: PENDING
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
 	 */
 	function GLTFLightsExtension( json ) {
 
@@ -356,9 +355,9 @@ THREE.GLTFLoader = ( function () {
 	};
 
 	/**
-	 * Unlit Materials Extension (pending)
+	 * Unlit Materials Extension
 	 *
-	 * PR: https://github.com/KhronosGroup/glTF/pull/1163
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
 	 */
 	function GLTFMaterialsUnlitExtension() {
 
@@ -473,7 +472,7 @@ THREE.GLTFLoader = ( function () {
 	/**
 	 * DRACO Mesh Compression Extension
 	 *
-	 * Specification: https://github.com/KhronosGroup/glTF/pull/874
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
 	 */
 	function GLTFDracoMeshCompressionExtension( json, dracoLoader ) {
 
@@ -551,7 +550,7 @@ THREE.GLTFLoader = ( function () {
 	/**
 	 * Texture Transform Extension
 	 *
-	 * Specification:
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
 	 */
 	function GLTFTextureTransformExtension() {
 

+ 0 - 14
examples/jsm/controls/OrbitControls.d.ts

@@ -48,20 +48,6 @@ export class OrbitControls {
 	mouseButtons: { LEFT: MOUSE; MIDDLE: MOUSE; RIGHT: MOUSE; };
 	touches: { ONE: TOUCH; TWO: TOUCH };
 
-	rotateLeft( angle?: number ): void;
-
-	rotateUp( angle?: number ): void;
-
-	panLeft( distance?: number ): void;
-
-	panUp( distance?: number ): void;
-
-	pan( deltaX: number, deltaY: number ): void;
-
-	dollyIn( dollyScale: number ): void;
-
-	dollyOut( dollyScale: number ): void;
-
 	update(): boolean;
 
 	saveState(): void;

+ 1 - 0
examples/jsm/loaders/BasisTextureLoader.d.ts

@@ -14,6 +14,7 @@ export class BasisTextureLoader extends Loader {
 
 	workerConfig: {
 		format: number;
+		astcSupported: boolean;
 		etcSupported: boolean;
 		dxtSupported: boolean;
 		pvrtcSupported: boolean;

+ 73 - 55
examples/jsm/loaders/BasisTextureLoader.js

@@ -10,13 +10,13 @@ import {
 	LinearFilter,
 	LinearMipmapLinearFilter,
 	Loader,
+	RGBA_ASTC_4x4_Format,
+	RGBA_PVRTC_4BPPV1_Format,
 	RGB_ETC1_Format,
 	RGB_PVRTC_4BPPV1_Format,
 	UnsignedByteType
 } from "../../../build/three.module.js";
 
-/* global Module, createBasisModule */
-
 /**
  * Loader for Basis Universal GPU Texture Codec.
  *
@@ -43,6 +43,7 @@ var BasisTextureLoader = function ( manager ) {
 	this.workerSourceURL = '';
 	this.workerConfig = {
 		format: null,
+		astcSupported: false,
 		etcSupported: false,
 		dxtSupported: false,
 		pvrtcSupported: false,
@@ -74,22 +75,27 @@ BasisTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ),
 
 		var config = this.workerConfig;
 
+		config.astcSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_astc' );
 		config.etcSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_etc1' );
 		config.dxtSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_s3tc' );
 		config.pvrtcSupported = !! renderer.extensions.get( 'WEBGL_compressed_texture_pvrtc' )
 			|| !! renderer.extensions.get( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );
 
-		if ( config.etcSupported ) {
+		if ( config.astcSupported ) {
 
-			config.format = BasisTextureLoader.BASIS_FORMAT.cTFETC1;
+			config.format = BasisTextureLoader.BASIS_FORMAT.cTFASTC_4x4;
 
 		} else if ( config.dxtSupported ) {
 
-			config.format = BasisTextureLoader.BASIS_FORMAT.cTFBC1;
+			config.format = BasisTextureLoader.BASIS_FORMAT.cTFBC3;
 
 		} else if ( config.pvrtcSupported ) {
 
-			config.format = BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_OPAQUE_ONLY;
+			config.format = BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGBA;
+
+		} else if ( config.etcSupported ) {
+
+			config.format = BasisTextureLoader.BASIS_FORMAT.cTFETC1;
 
 		} else {
 
@@ -147,25 +153,30 @@ BasisTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ),
 
 				var config = this.workerConfig;
 
-				var { width, height, mipmaps } = message;
+				var { width, height, hasAlpha, mipmaps, format } = message;
 
 				var texture;
 
-				if ( config.etcSupported ) {
-
-					texture = new CompressedTexture( mipmaps, width, height, RGB_ETC1_Format );
-
-				} else if ( config.dxtSupported ) {
-
-					texture = new CompressedTexture( mipmaps, width, height, BasisTextureLoader.DXT_FORMAT_MAP[ config.format ], UnsignedByteType );
-
-				} else if ( config.pvrtcSupported ) {
-
-					texture = new CompressedTexture( mipmaps, width, height, RGB_PVRTC_4BPPV1_Format );
-
-				} else {
-
-					throw new Error( 'THREE.BasisTextureLoader: No supported format available.' );
+				switch ( format ) {
+
+					case BasisTextureLoader.BASIS_FORMAT.cTFASTC_4x4:
+						texture = new CompressedTexture( mipmaps, width, height, RGBA_ASTC_4x4_Format );
+						break;
+					case BasisTextureLoader.BASIS_FORMAT.cTFBC1:
+					case BasisTextureLoader.BASIS_FORMAT.cTFBC3:
+						texture = new CompressedTexture( mipmaps, width, height, BasisTextureLoader.DXT_FORMAT_MAP[ config.format ], UnsignedByteType );
+						break;
+					case BasisTextureLoader.BASIS_FORMAT.cTFETC1:
+						texture = new CompressedTexture( mipmaps, width, height, RGB_ETC1_Format );
+						break;
+					case BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGB:
+						texture = new CompressedTexture( mipmaps, width, height, RGB_PVRTC_4BPPV1_Format );
+						break;
+					case BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGBA:
+						texture = new CompressedTexture( mipmaps, width, height, RGBA_PVRTC_4BPPV1_Format );
+						break;
+					default:
+						throw new Error( 'THREE.BasisTextureLoader: No supported format available.' );
 
 				}
 
@@ -225,12 +236,7 @@ BasisTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ),
 
 					var body = [
 						'/* basis_transcoder.js */',
-						'var Module;',
-						'function createBasisModule () {',
-						'  ' + jsContent,
-						'  return Module;',
-						'}',
-						'',
+						jsContent,
 						'/* worker */',
 						fn.substring( fn.indexOf( '{' ) + 1, fn.lastIndexOf( '}' ) )
 					].join( '\n' );
@@ -323,13 +329,22 @@ BasisTextureLoader.prototype = Object.assign( Object.create( Loader.prototype ),
 
 BasisTextureLoader.BASIS_FORMAT = {
 	cTFETC1: 0,
-	cTFBC1: 1,
-	cTFBC4: 2,
-	cTFPVRTC1_4_OPAQUE_ONLY: 3,
-	cTFBC7_M6_OPAQUE_ONLY: 4,
-	cTFETC2: 5,
-	cTFBC3: 6,
-	cTFBC5: 7,
+	cTFETC2: 1,
+	cTFBC1: 2,
+	cTFBC3: 3,
+	cTFBC4: 4,
+	cTFBC5: 5,
+	cTFBC7_M6_OPAQUE_ONLY: 6,
+	cTFBC7_M5: 7,
+	cTFPVRTC1_4_RGB: 8,
+	cTFPVRTC1_4_RGBA: 9,
+	cTFASTC_4x4: 10,
+	cTFATC_RGB: 11,
+	cTFATC_RGBA_INTERPOLATED_ALPHA: 12,
+	cTFRGBA32: 13,
+	cTFRGB565: 14,
+	cTFBGR565: 15,
+	cTFRGBA4444: 16,
 };
 
 // DXT formats, from:
@@ -370,7 +385,7 @@ BasisTextureLoader.BasisWorker = function () {
 
 					try {
 
-						var { width, height, mipmaps } = transcode( message.buffer );
+						var { width, height, hasAlpha, mipmaps, format } = transcode( message.buffer );
 
 						var buffers = [];
 
@@ -380,7 +395,7 @@ BasisTextureLoader.BasisWorker = function () {
 
 						}
 
-						self.postMessage( { type: 'transcode', id: message.id, width, height, mipmaps }, buffers );
+						self.postMessage( { type: 'transcode', id: message.id, width, height, hasAlpha, mipmaps, format }, buffers );
 
 					} catch ( error ) {
 
@@ -399,19 +414,15 @@ BasisTextureLoader.BasisWorker = function () {
 
 	function init( wasmBinary ) {
 
+		var BasisModule;
 		transcoderPending = new Promise( ( resolve ) => {
 
-			// The 'Module' global is used by the Basis wrapper, which will check for
-			// the 'wasmBinary' property before trying to load the file itself.
-
-			// TODO(donmccurdy): This only works with a modified version of the
-			// emscripten-generated wrapper. The default seems to have a bug making it
-			// impossible to override the WASM binary.
-			Module = { wasmBinary, onRuntimeInitialized: resolve };
+			BasisModule = { wasmBinary, onRuntimeInitialized: resolve };
+			BASIS( BasisModule );
 
 		} ).then( () => {
 
-			var { BasisFile, initializeBasis } = Module;
+			var { BasisFile, initializeBasis } = BasisModule;
 
 			_BasisFile = BasisFile;
 
@@ -419,8 +430,6 @@ BasisTextureLoader.BasisWorker = function () {
 
 		} );
 
-		createBasisModule();
-
 	}
 
 	function transcode( buffer ) {
@@ -430,6 +439,7 @@ BasisTextureLoader.BasisWorker = function () {
 		var width = basisFile.getImageWidth( 0, 0 );
 		var height = basisFile.getImageHeight( 0, 0 );
 		var levels = basisFile.getNumLevels( 0 );
+		var hasAlpha = basisFile.getHasAlpha();
 
 		function cleanup() {
 
@@ -438,6 +448,20 @@ BasisTextureLoader.BasisWorker = function () {
 
 		}
 
+		if ( ! hasAlpha ) {
+
+			switch ( config.format ) {
+
+				case 9: // Hardcoded: BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGBA
+					config.format = 8; // Hardcoded: BasisTextureLoader.BASIS_FORMAT.cTFPVRTC1_4_RGB;
+					break;
+				default:
+					break;
+
+			}
+
+		}
+
 		if ( ! width || ! height || ! levels ) {
 
 			cleanup();
@@ -452,12 +476,6 @@ BasisTextureLoader.BasisWorker = function () {
 
 		}
 
-		if ( basisFile.getHasAlpha() ) {
-
-			console.warn( 'THREE.BasisTextureLoader: Alpha not yet implemented.' );
-
-		}
-
 		var mipmaps = [];
 
 		for ( var mip = 0; mip < levels; mip ++ ) {
@@ -471,7 +489,7 @@ BasisTextureLoader.BasisWorker = function () {
 				0,
 				mip,
 				config.format,
-				config.etcSupported ? 0 : ( config.dxtSupported ? 1 : 0 ),
+				hasAlpha,
 				0
 			);
 
@@ -488,7 +506,7 @@ BasisTextureLoader.BasisWorker = function () {
 
 		cleanup();
 
-		return { width, height, mipmaps };
+		return { width, height, hasAlpha, mipmaps, format: config.format };
 
 	}
 

+ 7 - 8
examples/jsm/loaders/GLTFLoader.js

@@ -333,8 +333,7 @@ var GLTFLoader = ( function () {
 	/**
 	 * DDS Texture Extension
 	 *
-	 * Specification:
-	 * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
 	 *
 	 */
 	function GLTFTextureDDSExtension( ddsLoader ) {
@@ -351,9 +350,9 @@ var GLTFLoader = ( function () {
 	}
 
 	/**
-	 * Lights Extension
+	 * Punctual Lights Extension
 	 *
-	 * Specification: PENDING
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
 	 */
 	function GLTFLightsExtension( json ) {
 
@@ -420,9 +419,9 @@ var GLTFLoader = ( function () {
 	};
 
 	/**
-	 * Unlit Materials Extension (pending)
+	 * Unlit Materials Extension
 	 *
-	 * PR: https://github.com/KhronosGroup/glTF/pull/1163
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
 	 */
 	function GLTFMaterialsUnlitExtension() {
 
@@ -537,7 +536,7 @@ var GLTFLoader = ( function () {
 	/**
 	 * DRACO Mesh Compression Extension
 	 *
-	 * Specification: https://github.com/KhronosGroup/glTF/pull/874
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
 	 */
 	function GLTFDracoMeshCompressionExtension( json, dracoLoader ) {
 
@@ -615,7 +614,7 @@ var GLTFLoader = ( function () {
 	/**
 	 * Texture Transform Extension
 	 *
-	 * Specification:
+	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_texture_transform
 	 */
 	function GLTFTextureTransformExtension() {
 

+ 4 - 4
examples/misc_exporter_collada.html

@@ -90,11 +90,11 @@
 				textureMap.anisotropy = 16;
 
 				// REFLECTION MAP
-				var path = "textures/cube/skybox/";
+				var path = "textures/cube/pisa/";
 				var urls = [
-					path + "px.jpg", path + "nx.jpg",
-					path + "py.jpg", path + "ny.jpg",
-					path + "pz.jpg", path + "nz.jpg"
+					path + "px.png", path + "nx.png",
+					path + "py.png", path + "ny.png",
+					path + "pz.png", path + "nz.png"
 				];
 
 				textureCube = new THREE.CubeTextureLoader().load( urls );

+ 4 - 4
examples/webgl_geometry_teapot.html

@@ -84,11 +84,11 @@
 				textureMap.anisotropy = 16;
 
 				// REFLECTION MAP
-				var path = "textures/cube/skybox/";
+				var path = "textures/cube/pisa/";
 				var urls = [
-					path + "px.jpg", path + "nx.jpg",
-					path + "py.jpg", path + "ny.jpg",
-					path + "pz.jpg", path + "nz.jpg"
+					path + "px.png", path + "nx.png",
+					path + "py.png", path + "ny.png",
+					path + "pz.png", path + "nz.png"
 				];
 
 				textureCube = new THREE.CubeTextureLoader().load( urls );

+ 132 - 0
examples/webgl_instancing.html

@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - instancing</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import Stats from './jsm/libs/stats.module.js';
+
+			var camera, scene, renderer, stats;
+
+			var mesh;
+			var amount = 12;
+			var count = Math.pow( amount, 3 );
+			var dummy = new THREE.Object3D();
+
+			init();
+			animate();
+
+			function init() {
+
+				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 1000 );
+				camera.position.set( 10, 10, 10 );
+				camera.lookAt( 0, 0, 0 );
+
+				scene = new THREE.Scene();
+
+				var material = new THREE.MeshNormalMaterial();
+				// check overdraw
+				// var material = new THREE.MeshBasicMaterial( { color: 0xff0000, opacity: 0.1, transparent: true } );
+
+
+				var loader = new THREE.BufferGeometryLoader();
+				loader.load( 'models/json/suzanne_buffergeometry.json', function ( geometry ) {
+
+					geometry.computeVertexNormals();
+					geometry.scale( 0.5, 0.5, 0.5 );
+
+					mesh = new THREE.InstancedMesh( geometry, material, count );
+					scene.add( mesh );
+
+				} );
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				stats = new Stats();
+				document.body.appendChild( stats.dom );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			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() {
+
+				if ( mesh ) {
+
+					var time = Date.now() * 0.001;
+
+					mesh.rotation.x = Math.sin( time / 4 );
+					mesh.rotation.y = Math.sin( time / 2 );
+
+					var i = 0;
+
+					for ( var x = 0; x < amount; x ++ ) {
+
+						for ( var y = 0; y < amount; y ++ ) {
+
+							for ( var z = 0; z < amount; z ++ ) {
+
+								dummy.position.set( 6 - x, 6 - y, 6 - z );
+								dummy.rotation.y = ( Math.sin( x / 4 + time ) + Math.sin( y / 4 + time ) + Math.sin( z / 4 + time ) );
+								dummy.rotation.z = dummy.rotation.y * 2;
+
+								dummy.updateMatrix();
+
+								mesh.setMatrixAt( i ++, dummy.matrix );
+
+							}
+
+						}
+
+					}
+
+					mesh.instanceMatrix.needsUpdate = true;
+
+				}
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+
+	</body>
+</html>

+ 0 - 1
examples/webgl_loader_texture_basis.html

@@ -51,7 +51,6 @@
 				var loader = new BasisTextureLoader();
 				loader.setTranscoderPath( 'js/libs/basis/' );
 				loader.detectSupport( renderer );
-
 				loader.load( 'textures/compressed/PavingStones.basis', function ( texture ) {
 
 					texture.encoding = THREE.sRGBEncoding;

+ 1 - 1
examples/webgl_nearestneighbour.html

@@ -82,7 +82,7 @@
 				// add a skybox background
 				var cubeTextureLoader = new THREE.CubeTextureLoader();
 
-				cubeTextureLoader.setPath( 'textures/cube/skybox/' );
+				cubeTextureLoader.setPath( 'textures/cube/skyboxsun25deg/' );
 
 				var cubeTexture = cubeTextureLoader.load( [
 					'px.jpg', 'nx.jpg',

+ 4 - 4
examples/webgl_water.html

@@ -100,12 +100,12 @@
 				// skybox
 
 				var cubeTextureLoader = new THREE.CubeTextureLoader();
-				cubeTextureLoader.setPath( 'textures/cube/skybox/' );
+				cubeTextureLoader.setPath( 'textures/cube/Park2/' );
 
 				var cubeTexture = cubeTextureLoader.load( [
-					'px.jpg', 'nx.jpg',
-					'py.jpg', 'ny.jpg',
-					'pz.jpg', 'nz.jpg',
+					"posx.jpg", "negx.jpg",
+					"posy.jpg", "negy.jpg",
+					"posz.jpg", "negz.jpg"
 				] );
 
 				scene.background = cubeTexture;

+ 2 - 2
examples/webvr_multiview.html

@@ -74,7 +74,7 @@
 				info.style.width = '100%';
 				info.style.textAlign = 'center';
 				info.innerHTML = '<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webvr - multiview<br/>';
-				info.innerHTML += renderer.capabilities.multiview ? `<span style="color: #33ff33"><b>OVR_multiview2</b> is supported in your browser</span>` :
+				info.innerHTML += renderer.extensions.get( 'OVR_multiview2' ) ? `<span style="color: #33ff33"><b>OVR_multiview2</b> is supported in your browser</span>` :
 					`<span style="color: #ff3333"><b>OVR_multiview2</b> is not supported or enabled in your browser</span>`;
 
 				container.appendChild( info );
@@ -86,7 +86,7 @@
 
 				room = new THREE.LineSegments(
 					new BoxLineGeometry( 6, 6, 6, 10, 10, 10 ),
-					new THREE.LineBasicMaterial( { color: renderer.capabilities.multiview ? 0x99ff99 : 0xff3333 } )
+					new THREE.LineBasicMaterial( { color: renderer.extensions.get( 'OVR_multiview2' ) ? 0x99ff99 : 0xff3333 } )
 				);
 				room.geometry.translate( 0, 3, 0 );
 				scene.add( room );

+ 1 - 0
src/Three.js

@@ -18,6 +18,7 @@ export { SkinnedMesh } from './objects/SkinnedMesh.js';
 export { Skeleton } from './objects/Skeleton.js';
 export { Bone } from './objects/Bone.js';
 export { Mesh } from './objects/Mesh.js';
+export { InstancedMesh } from './objects/InstancedMesh.js';
 export { LineSegments } from './objects/LineSegments.js';
 export { LineLoop } from './objects/LineLoop.js';
 export { Line } from './objects/Line.js';

+ 7 - 0
src/core/Geometry.js

@@ -189,6 +189,13 @@ Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ),
 		var indices = geometry.index !== null ? geometry.index.array : undefined;
 		var attributes = geometry.attributes;
 
+		if ( attributes.position === undefined ) {
+
+			console.error( 'THREE.Geometry.fromBufferGeometry(): Position attribute required for conversion.' );
+			return this;
+
+		}
+
 		var positions = attributes.position.array;
 		var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;
 		var colors = attributes.color !== undefined ? attributes.color.array : undefined;

+ 7 - 0
src/materials/Material.d.ts

@@ -47,6 +47,7 @@ export interface MaterialParameters {
 	flatShading?: boolean;
 	side?: Side;
 	shadowSide?: Side;
+	toneMapped?: boolean;
 	transparent?: boolean;
 	vertexColors?: Colors;
 	vertexTangents?: boolean;
@@ -261,6 +262,12 @@ export class Material extends EventDispatcher {
 	 */
 	side: Side;
 
+	/**
+	 * Defines whether this material is tone mapped according to the renderer's toneMapping setting.
+	 * Default is true.
+	 */
+	toneMapped: boolean;
+
 	/**
 	 * Defines whether this material is transparent. This has an effect on rendering as transparent objects need special treatment and are rendered after non-transparent objects.
 	 * When set to true, the extent to which the material is transparent is controlled by setting it's .opacity property.

+ 7 - 3
src/math/Vector3.js

@@ -526,11 +526,13 @@ Object.assign( Vector3.prototype, {
 
 	},
 
-	projectOnVector: function ( vector ) {
+	projectOnVector: function ( v ) {
 
-		var scalar = vector.dot( this ) / vector.lengthSq();
+		// v cannot be the zero v
 
-		return this.copy( vector ).multiplyScalar( scalar );
+		var scalar = v.dot( this ) / v.lengthSq();
+
+		return this.copy( v ).multiplyScalar( scalar );
 
 	},
 
@@ -553,6 +555,8 @@ Object.assign( Vector3.prototype, {
 
 	angleTo: function ( v ) {
 
+		// assumes this and v are not the zero vector
+
 		var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );
 
 		// clamp, to handle numerical problems

+ 34 - 0
src/objects/InstancedMesh.js

@@ -0,0 +1,34 @@
+/**
+ * @author mrdoob / http://mrdoob.com/
+ */
+
+import { BufferAttribute } from '../core/BufferAttribute.js';
+import { Mesh } from './Mesh.js';
+
+function InstancedMesh( geometry, material, count ) {
+
+	Mesh.call( this, geometry, material );
+
+	this.instanceMatrix = new BufferAttribute( new Float32Array( count * 16 ), 16 );
+
+}
+
+InstancedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {
+
+	constructor: InstancedMesh,
+
+	isInstancedMesh: true,
+
+	raycast: function () {},
+
+	setMatrixAt: function ( index, matrix ) {
+
+		matrix.toArray( this.instanceMatrix.array, index * 16 );
+
+	},
+
+	updateMorphTargets: function () {}
+
+} );
+
+export { InstancedMesh };

+ 1 - 1
src/renderers/WebGLRenderer.d.ts

@@ -31,7 +31,7 @@ export interface WebGLRendererParameters {
 	/**
 	 * A Canvas where the renderer draws its output.
 	 */
-	canvas?: HTMLCanvasElement;
+	canvas?: HTMLCanvasElement | OffscreenCanvas;
 
 	/**
 	 * A WebGL Rendering Context.

+ 37 - 20
src/renderers/WebGLRenderer.js

@@ -258,7 +258,7 @@ function WebGLRenderer( parameters ) {
 
 		capabilities = new WebGLCapabilities( _gl, extensions, parameters );
 
-		if ( ! capabilities.isWebGL2 ) {
+		if ( capabilities.isWebGL2 === false ) {
 
 			extensions.get( 'WEBGL_depth_texture' );
 			extensions.get( 'OES_texture_float' );
@@ -283,7 +283,7 @@ function WebGLRenderer( parameters ) {
 		textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
 		attributes = new WebGLAttributes( _gl );
 		geometries = new WebGLGeometries( _gl, attributes, info );
-		objects = new WebGLObjects( geometries, info );
+		objects = new WebGLObjects( _gl, geometries, attributes, info );
 		morphtargets = new WebGLMorphtargets( _gl );
 		programCache = new WebGLPrograms( _this, extensions, capabilities );
 		renderLists = new WebGLRenderLists();
@@ -765,7 +765,7 @@ function WebGLRenderer( parameters ) {
 
 		if ( updateBuffers ) {
 
-			setupVertexAttributes( material, program, geometry );
+			setupVertexAttributes( object, geometry, material, program );
 
 			if ( index !== null ) {
 
@@ -831,7 +831,6 @@ function WebGLRenderer( parameters ) {
 
 			}
 
-
 		} else if ( object.isLine ) {
 
 			var lineWidth = material.linewidth;
@@ -864,13 +863,13 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		if ( geometry && geometry.isInstancedBufferGeometry ) {
+		if ( object.isInstancedMesh ) {
 
-			if ( geometry.maxInstancedCount > 0 ) {
+			renderer.renderInstances( geometry, drawStart, drawCount, object.instanceMatrix.count );
 
-				renderer.renderInstances( geometry, drawStart, drawCount );
+		} else if ( geometry.isInstancedBufferGeometry ) {
 
-			}
+			renderer.renderInstances( geometry, drawStart, drawCount, geometry.maxInstancedCount );
 
 		} else {
 
@@ -880,16 +879,11 @@ function WebGLRenderer( parameters ) {
 
 	};
 
-	function setupVertexAttributes( material, program, geometry ) {
-
-		if ( geometry && geometry.isInstancedBufferGeometry && ! capabilities.isWebGL2 ) {
+	function setupVertexAttributes( object, geometry, material, program ) {
 
-			if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) {
+		if ( capabilities.isWebGL2 === false && ( object.isInstancedMesh || geometry.isInstancedBufferGeometry ) ) {
 
-				console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );
-				return;
-
-			}
+			if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) return;
 
 		}
 
@@ -972,6 +966,29 @@ function WebGLRenderer( parameters ) {
 
 					}
 
+				} else if ( name === 'instanceMatrix' ) {
+
+					var attribute = attributes.get( object.instanceMatrix );
+
+					// TODO Attribute may not be available on context restore
+
+					if ( attribute === undefined ) continue;
+
+					var buffer = attribute.buffer;
+					var type = attribute.type;
+
+					state.enableAttributeAndDivisor( programAttribute + 0, 1 );
+					state.enableAttributeAndDivisor( programAttribute + 1, 1 );
+					state.enableAttributeAndDivisor( programAttribute + 2, 1 );
+					state.enableAttributeAndDivisor( programAttribute + 3, 1 );
+
+					_gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );
+
+					_gl.vertexAttribPointer( programAttribute + 0, 4, type, false, 64, 0 );
+					_gl.vertexAttribPointer( programAttribute + 1, 4, type, false, 64, 16 );
+					_gl.vertexAttribPointer( programAttribute + 2, 4, type, false, 64, 32 );
+					_gl.vertexAttribPointer( programAttribute + 3, 4, type, false, 64, 48 );
+
 				} else if ( materialDefaultAttributeValues !== undefined ) {
 
 					var value = materialDefaultAttributeValues[ name ];
@@ -1180,7 +1197,7 @@ function WebGLRenderer( parameters ) {
 
 		}
 
-		if ( capabilities.multiview ) {
+		if ( camera.isArrayCamera && multiview.isAvailable() ) {
 
 			multiview.attachCamera( camera );
 
@@ -1240,7 +1257,7 @@ function WebGLRenderer( parameters ) {
 
 		state.setPolygonOffset( false );
 
-		if ( capabilities.multiview ) {
+		if ( camera.isArrayCamera && multiview.isAvailable() ) {
 
 			multiview.detachCamera( camera );
 
@@ -1399,9 +1416,9 @@ function WebGLRenderer( parameters ) {
 
 				_currentArrayCamera = camera;
 
-				if ( capabilities.multiview ) {
+				if ( multiview.isAvailable() ) {
 
-					renderObject(	object, scene, camera, geometry, material, group );
+					renderObject( object, scene, camera, geometry, material, group );
 
 				} else {
 

+ 3 - 0
src/renderers/shaders/ShaderChunk/common.glsl.js

@@ -7,7 +7,10 @@ export default /* glsl */`
 #define LOG2 1.442695
 #define EPSILON 1e-6
 
+#ifndef saturate
+// <tonemapping_pars_fragment> may have defined saturate() already
 #define saturate(a) clamp( a, 0.0, 1.0 )
+#endif
 #define whiteComplement(a) ( 1.0 - saturate( a ) )
 
 float pow2( const in float x ) { return x*x; }

+ 9 - 1
src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl.js

@@ -1,5 +1,13 @@
 export default /* glsl */`
-vec3 transformedNormal = normalMatrix * objectNormal;
+vec3 transformedNormal = objectNormal;
+
+#ifdef USE_INSTANCING
+
+	transformedNormal = mat3( instanceMatrix ) * transformedNormal;
+
+#endif
+
+transformedNormal = normalMatrix * transformedNormal;
 
 #ifdef FLIP_SIDED
 

+ 1 - 7
src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl.js

@@ -39,14 +39,8 @@ export default /* glsl */`
 		#ifdef DOUBLE_SIDED
 
 			// Workaround for Adreno GPUs gl_FrontFacing bug. See #15850 and #10331
-			// http://hacksoflife.blogspot.com/2009/11/per-pixel-tangent-space-normal-mapping.html?showComment=1522254677437#c5087545147696715943
-			vec3 NfromST = cross( S, T );
-			if( dot( NfromST, N ) < 0.0 ) {
 
-				S *= -1.0;
-				T *= -1.0;
-
-			}
+			if ( dot( cross( S, T ), N ) < 0.0 ) mapN.xy *= - 1.0;
 
 		#else
 

+ 9 - 1
src/renderers/shaders/ShaderChunk/project_vertex.glsl.js

@@ -1,5 +1,13 @@
 export default /* glsl */`
-vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );
+vec4 mvPosition = vec4( transformed, 1.0 );
+
+#ifdef USE_INSTANCING
+
+	mvPosition = instanceMatrix * mvPosition;
+
+#endif
+
+mvPosition = modelViewMatrix * mvPosition;
 
 gl_Position = projectionMatrix * mvPosition;
 `;

+ 2 - 1
src/renderers/shaders/ShaderChunk/tonemapping_pars_fragment.glsl.js

@@ -1,6 +1,7 @@
 export default /* glsl */`
 #ifndef saturate
-	#define saturate(a) clamp( a, 0.0, 1.0 )
+// <common> may have defined saturate() already
+#define saturate(a) clamp( a, 0.0, 1.0 )
 #endif
 
 uniform float toneMappingExposure;

+ 9 - 1
src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl.js

@@ -1,7 +1,15 @@
 export default /* glsl */`
 #if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )
 
-	vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );
+	vec4 worldPosition = vec4( transformed, 1.0 );
+
+	#ifdef USE_INSTANCING
+
+		worldPosition = instanceMatrix * worldPosition;
+
+	#endif
+
+	worldPosition = modelMatrix * worldPosition;
 
 #endif
 `;

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

@@ -20,7 +20,9 @@ function WebGLBufferRenderer( gl, extensions, info, capabilities ) {
 
 	}
 
-	function renderInstances( geometry, start, count ) {
+	function renderInstances( geometry, start, count, primcount ) {
+
+		if ( primcount === 0 ) return;
 
 		var extension, methodName;
 
@@ -43,9 +45,9 @@ function WebGLBufferRenderer( gl, extensions, info, capabilities ) {
 
 		}
 
-		extension[ methodName ]( mode, start, count, geometry.maxInstancedCount );
+		extension[ methodName ]( mode, start, count, primcount );
 
-		info.update( count, mode, geometry.maxInstancedCount );
+		info.update( count, mode, primcount );
 
 	}
 

+ 1 - 8
src/renderers/webgl/WebGLCapabilities.js

@@ -86,10 +86,6 @@ function WebGLCapabilities( gl, extensions, parameters ) {
 
 	var maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0;
 
-	var multiviewExt = extensions.get( 'OVR_multiview2' );
-	var multiview = isWebGL2 && !! multiviewExt && ! gl.getContextAttributes().antialias;
-	var maxMultiviewViews = multiview ? gl.getParameter( multiviewExt.MAX_VIEWS_OVR ) : 0;
-
 	return {
 
 		isWebGL2: isWebGL2,
@@ -114,10 +110,7 @@ function WebGLCapabilities( gl, extensions, parameters ) {
 		floatFragmentTextures: floatFragmentTextures,
 		floatVertexTextures: floatVertexTextures,
 
-		maxSamples: maxSamples,
-
-		multiview: multiview,
-		maxMultiviewViews: maxMultiviewViews
+		maxSamples: maxSamples
 
 	};
 

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

@@ -29,7 +29,9 @@ function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) {
 
 	}
 
-	function renderInstances( geometry, start, count ) {
+	function renderInstances( geometry, start, count, primcount ) {
+
+		if ( primcount === 0 ) return;
 
 		var extension, methodName;
 
@@ -52,9 +54,9 @@ function WebGLIndexedBufferRenderer( gl, extensions, info, capabilities ) {
 
 		}
 
-		extension[ methodName ]( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );
+		extension[ methodName ]( mode, count, type, start * bytesPerElement, primcount );
 
-		info.update( count, mode, geometry.maxInstancedCount );
+		info.update( count, mode, primcount );
 
 	}
 

+ 49 - 67
src/renderers/webgl/WebGLMultiview.js

@@ -12,29 +12,52 @@ function WebGLMultiview( renderer, gl ) {
 
 	var DEFAULT_NUMVIEWS = 2;
 
-	var capabilities = renderer.capabilities;
+	var extensions = renderer.extensions;
 	var properties = renderer.properties;
 
-	var maxNumViews = capabilities.maxMultiviewViews;
-
 	var renderTarget, currentRenderTarget;
-	var mat3, mat4, cameraArray, renderSize;
+	var mat3, mat4, renderSize;
 
-	function getCameraArray( camera ) {
+	var available;
+	var maxNumViews = 0;
 
-		if ( camera.isArrayCamera ) return camera.cameras;
+	//
 
-		cameraArray[ 0 ] = camera;
+	function isAvailable() {
 
-		return cameraArray;
+		if ( available === undefined ) {
 
-	}
+			var extension = extensions.get( 'OVR_multiview2' );
 
-	//
+			available = extension !== null && gl.getContextAttributes().antialias === false;
+
+			if ( available ) {
+
+				maxNumViews = gl.getParameter( extension.MAX_VIEWS_OVR );
+				renderTarget = new WebGLMultiviewRenderTarget( 0, 0, DEFAULT_NUMVIEWS );
+
+				renderSize = new Vector2();
+				mat4 = [];
+				mat3 = [];
+
+				for ( var i = 0; i < maxNumViews; i ++ ) {
+
+					mat4[ i ] = new Matrix4();
+					mat3[ i ] = new Matrix3();
+
+				}
+
+			}
+
+		}
+
+		return available;
+
+	}
 
 	function updateCameraProjectionMatricesUniform( camera, uniforms ) {
 
-		var cameras = getCameraArray( camera );
+		var cameras = camera.cameras;
 
 		for ( var i = 0; i < cameras.length; i ++ ) {
 
@@ -48,7 +71,7 @@ function WebGLMultiview( renderer, gl ) {
 
 	function updateCameraViewMatricesUniform( camera, uniforms ) {
 
-		var cameras = getCameraArray( camera );
+		var cameras = camera.cameras;
 
 		for ( var i = 0; i < cameras.length; i ++ ) {
 
@@ -62,7 +85,7 @@ function WebGLMultiview( renderer, gl ) {
 
 	function updateObjectMatricesUniforms( object, camera, uniforms ) {
 
-		var cameras = getCameraArray( camera );
+		var cameras = camera.cameras;
 
 		for ( var i = 0; i < cameras.length; i ++ ) {
 
@@ -78,8 +101,6 @@ function WebGLMultiview( renderer, gl ) {
 
 	function isMultiviewCompatible( camera ) {
 
-		if ( ! camera.isArrayCamera ) return true;
-
 		var cameras = camera.cameras;
 
 		if ( cameras.length > maxNumViews ) return false;
@@ -107,20 +128,10 @@ function WebGLMultiview( renderer, gl ) {
 
 		}
 
-		if ( camera.isArrayCamera ) {
+		var viewport = camera.cameras[ 0 ].viewport;
 
-			var viewport = camera.cameras[ 0 ].viewport;
-
-			renderTarget.setSize( viewport.z, viewport.w );
-
-			renderTarget.setNumViews( camera.cameras.length );
-
-		} else {
-
-			renderTarget.setSize( renderSize.x, renderSize.y );
-			renderTarget.setNumViews( DEFAULT_NUMVIEWS );
-
-		}
+		renderTarget.setSize( viewport.z, viewport.w );
+		renderTarget.setNumViews( camera.cameras.length );
 
 	}
 
@@ -139,6 +150,7 @@ function WebGLMultiview( renderer, gl ) {
 		if ( renderTarget !== renderer.getRenderTarget() ) return;
 
 		renderer.setRenderTarget( currentRenderTarget );
+
 		flush( camera );
 
 	}
@@ -153,53 +165,23 @@ function WebGLMultiview( renderer, gl ) {
 		var viewWidth = srcRenderTarget.width;
 		var viewHeight = srcRenderTarget.height;
 
-		if ( camera.isArrayCamera ) {
-
-			for ( var i = 0; i < numViews; i ++ ) {
+		for ( var i = 0; i < numViews; i ++ ) {
 
-				var viewport = camera.cameras[ i ].viewport;
+			var viewport = camera.cameras[ i ].viewport;
 
-				var x1 = viewport.x;
-				var y1 = viewport.y;
-				var x2 = x1 + viewport.z;
-				var y2 = y1 + viewport.w;
+			var x1 = viewport.x;
+			var y1 = viewport.y;
+			var x2 = x1 + viewport.z;
+			var y2 = y1 + viewport.w;
 
-				gl.bindFramebuffer( gl.READ_FRAMEBUFFER, srcFramebuffers[ i ] );
-				gl.blitFramebuffer( 0, 0, viewWidth, viewHeight, x1, y1, x2, y2, gl.COLOR_BUFFER_BIT, gl.NEAREST );
-
-			}
-
-		} else {
-
-			gl.bindFramebuffer( gl.READ_FRAMEBUFFER, srcFramebuffers[ 0 ] );
-			gl.blitFramebuffer( 0, 0, viewWidth, viewHeight, 0, 0, renderSize.x, renderSize.y, gl.COLOR_BUFFER_BIT, gl.NEAREST );
+			gl.bindFramebuffer( gl.READ_FRAMEBUFFER, srcFramebuffers[ i ] );
+			gl.blitFramebuffer( 0, 0, viewWidth, viewHeight, x1, y1, x2, y2, gl.COLOR_BUFFER_BIT, gl.NEAREST );
 
 		}
 
 	}
 
-
-	if ( renderer.capabilities.multiview ) {
-
-		renderTarget = new WebGLMultiviewRenderTarget( 0, 0, DEFAULT_NUMVIEWS );
-
-		renderSize = new Vector2();
-		mat4 = [];
-		mat3 = [];
-		cameraArray = [];
-
-		var maxViews = capabilities.maxMultiviewViews;
-
-		for ( var i = 0; i < maxViews; i ++ ) {
-
-			mat4[ i ] = new Matrix4();
-			mat3[ i ] = new Matrix3();
-
-		}
-
-	}
-
-
+	this.isAvailable = isAvailable;
 	this.attachCamera = attachCamera;
 	this.detachCamera = detachCamera;
 	this.updateCameraProjectionMatricesUniform = updateCameraProjectionMatricesUniform;

+ 7 - 1
src/renderers/webgl/WebGLObjects.js

@@ -2,7 +2,7 @@
  * @author mrdoob / http://mrdoob.com/
  */
 
-function WebGLObjects( geometries, info ) {
+function WebGLObjects( gl, geometries, attributes, info ) {
 
 	var updateList = {};
 
@@ -29,6 +29,12 @@ function WebGLObjects( geometries, info ) {
 
 		}
 
+		if ( object.isInstancedMesh ) {
+
+			attributes.update( object.instanceMatrix, gl.ARRAY_BUFFER );
+
+		}
+
 		return buffergeometry;
 
 	}

+ 1 - 2
src/renderers/webgl/WebGLProgram.d.ts

@@ -13,8 +13,7 @@ export class WebGLProgram {
 		code: string,
 		material: ShaderMaterial,
 		shader: WebGLShader,
-		parameters: WebGLRendererParameters,
-		capabilities: WebGLCapabilities
+		parameters: WebGLRendererParameters
 	);
 
 	id: number;

+ 38 - 25
src/renderers/webgl/WebGLProgram.js

@@ -197,67 +197,73 @@ function replaceClippingPlaneNums( string, parameters ) {
 
 }
 
-function parseIncludes( string ) {
+// Resolve Includes
 
-	var pattern = /^[ \t]*#include +<([\w\d./]+)>/gm;
+var includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm;
 
-	function replace( match, include ) {
+function resolveIncludes( string ) {
 
-		var replace = ShaderChunk[ include ];
+	return string.replace( includePattern, includeReplacer );
 
-		if ( replace === undefined ) {
+}
 
-			throw new Error( 'Can not resolve #include <' + include + '>' );
+function includeReplacer( match, include ) {
 
-		}
+	var string = ShaderChunk[ include ];
 
-		return parseIncludes( replace );
+	if ( string === undefined ) {
+
+		throw new Error( 'Can not resolve #include <' + include + '>' );
 
 	}
 
-	return string.replace( pattern, replace );
+	return resolveIncludes( string );
 
 }
 
-function unrollLoops( string ) {
+// Unroll Loops
 
-	var pattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;
+var loopPattern = /#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;
 
-	function replace( match, start, end, snippet ) {
+function unrollLoops( string ) {
 
-		var unroll = '';
+	return string.replace( loopPattern, loopReplacer );
 
-		for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) {
+}
 
-			unroll += snippet
-				.replace( /\[ i \]/g, '[ ' + i + ' ]' )
-				.replace( /UNROLLED_LOOP_INDEX/g, i );
+function loopReplacer( match, start, end, snippet ) {
 
-		}
+	var string = '';
 
-		return unroll;
+	for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) {
+
+		string += snippet
+			.replace( /\[ i \]/g, '[ ' + i + ' ]' )
+			.replace( /UNROLLED_LOOP_INDEX/g, i );
 
 	}
 
-	return string.replace( pattern, replace );
+	return string;
 
 }
 
+//
+
 function generatePrecision( parameters ) {
 
 	var precisionstring = "precision " + parameters.precision + " float;\nprecision " + parameters.precision + " int;";
 
 	if ( parameters.precision === "highp" ) {
 
-		precisionstring += "\n#define HIGH_PRECISION;";
+		precisionstring += "\n#define HIGH_PRECISION";
 
 	} else if ( parameters.precision === "mediump" ) {
 
-		precisionstring += "\n#define MEDIUM_PRECISION;";
+		precisionstring += "\n#define MEDIUM_PRECISION";
 
 	} else if ( parameters.precision === "lowp" ) {
 
-		precisionstring += "\n#define LOW_PRECISION;";
+		precisionstring += "\n#define LOW_PRECISION";
 
 	}
 
@@ -435,6 +441,7 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters
 
 			customDefines,
 
+			parameters.instancing ? '#define USE_INSTANCING' : '',
 			parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',
 
 			'#define GAMMA_FACTOR ' + gammaFactorDefine,
@@ -505,6 +512,12 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters
 
 			].join( '\n' ),
 
+			'#ifdef USE_INSTANCING',
+
+			' attribute mat4 instanceMatrix;',
+
+			'#endif',
+
 			'attribute vec3 position;',
 			'attribute vec3 normal;',
 			'attribute vec2 uv;',
@@ -649,11 +662,11 @@ function WebGLProgram( renderer, extensions, code, material, shader, parameters
 
 	}
 
-	vertexShader = parseIncludes( vertexShader );
+	vertexShader = resolveIncludes( vertexShader );
 	vertexShader = replaceLightNums( vertexShader, parameters );
 	vertexShader = replaceClippingPlaneNums( vertexShader, parameters );
 
-	fragmentShader = parseIncludes( fragmentShader );
+	fragmentShader = resolveIncludes( fragmentShader );
 	fragmentShader = replaceLightNums( fragmentShader, parameters );
 	fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );
 

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

@@ -28,7 +28,8 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 	};
 
 	var parameterNames = [
-		"precision", "supportsVertexTextures", "map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding",
+		"precision", "supportsVertexTextures", "instancing",
+		"map", "mapEncoding", "matcap", "matcapEncoding", "envMap", "envMapMode", "envMapEncoding",
 		"lightMap", "aoMap", "emissiveMap", "emissiveMapEncoding", "bumpMap", "normalMap", "objectSpaceNormalMap", "tangentSpaceNormalMap", "clearcoatNormalMap", "displacementMap", "specularMap",
 		"roughnessMap", "metalnessMap", "gradientMap",
 		"alphaMap", "combine", "vertexColors", "vertexTangents", "fog", "useFog", "fogExp2",
@@ -134,10 +135,14 @@ function WebGLPrograms( renderer, extensions, capabilities ) {
 
 		var parameters = {
 
+			isWebGL2: capabilities.isWebGL2,
+
 			shaderID: shaderID,
 
 			precision: precision,
-			isWebGL2: capabilities.isWebGL2,
+
+			instancing: object.isInstancedMesh === true,
+
 			supportsVertexTextures: capabilities.vertexTextures,
 			outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ),
 			map: !! material.map,

+ 29 - 37
src/renderers/webgl/WebGLTextures.js

@@ -1016,53 +1016,45 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
 
 			} else if ( isMultiview ) {
 
-				if ( capabilities.multiview ) {
-
-					var width = renderTarget.width;
-					var height = renderTarget.height;
-					var numViews = renderTarget.numViews;
-
-					_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );
-
-					var ext = extensions.get( 'OVR_multiview2' );
-
-					info.memory.textures += 2;
+				var width = renderTarget.width;
+				var height = renderTarget.height;
+				var numViews = renderTarget.numViews;
 
-					var colorTexture = _gl.createTexture();
-					_gl.bindTexture( _gl.TEXTURE_2D_ARRAY, colorTexture );
-					_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
-					_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
-					_gl.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, _gl.RGBA8, width, height, numViews, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null );
-					ext.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, colorTexture, 0, 0, numViews );
+				_gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );
 
-					var depthStencilTexture = _gl.createTexture();
-					_gl.bindTexture( _gl.TEXTURE_2D_ARRAY, depthStencilTexture );
-					_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
-					_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
-					_gl.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, _gl.DEPTH24_STENCIL8, width, height, numViews, 0, _gl.DEPTH_STENCIL, _gl.UNSIGNED_INT_24_8, null );
-					ext.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, depthStencilTexture, 0, 0, numViews );
+				var ext = extensions.get( 'OVR_multiview2' );
 
-					var viewFramebuffers = new Array( numViews );
-					for ( var i = 0; i < numViews; ++ i ) {
+				info.memory.textures += 2;
 
-						viewFramebuffers[ i ] = _gl.createFramebuffer();
-						_gl.bindFramebuffer( _gl.FRAMEBUFFER, viewFramebuffers[ i ] );
-						_gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, colorTexture, 0, i );
+				var colorTexture = _gl.createTexture();
+				_gl.bindTexture( _gl.TEXTURE_2D_ARRAY, colorTexture );
+				_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
+				_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
+				_gl.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, _gl.RGBA8, width, height, numViews, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null );
+				ext.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, colorTexture, 0, 0, numViews );
 
-					}
+				var depthStencilTexture = _gl.createTexture();
+				_gl.bindTexture( _gl.TEXTURE_2D_ARRAY, depthStencilTexture );
+				_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
+				_gl.texParameteri( _gl.TEXTURE_2D_ARRAY, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
+				_gl.texImage3D( _gl.TEXTURE_2D_ARRAY, 0, _gl.DEPTH24_STENCIL8, width, height, numViews, 0, _gl.DEPTH_STENCIL, _gl.UNSIGNED_INT_24_8, null );
+				ext.framebufferTextureMultiviewOVR( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, depthStencilTexture, 0, 0, numViews );
 
-					renderTargetProperties.__webglColorTexture = colorTexture;
-					renderTargetProperties.__webglDepthStencilTexture = depthStencilTexture;
-					renderTargetProperties.__webglViewFramebuffers = viewFramebuffers;
+				var viewFramebuffers = new Array( numViews );
+				for ( var i = 0; i < numViews; ++ i ) {
 
-					_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
-					_gl.bindTexture( _gl.TEXTURE_2D_ARRAY, null );
+					viewFramebuffers[ i ] = _gl.createFramebuffer();
+					_gl.bindFramebuffer( _gl.FRAMEBUFFER, viewFramebuffers[ i ] );
+					_gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, colorTexture, 0, i );
 
-				} else {
+				}
 
-					console.warn( 'THREE.WebGLRenderer: WebGLMultiviewRenderTarget can only be used with WebGL2 and Multiview extension support.' );
+				renderTargetProperties.__webglColorTexture = colorTexture;
+				renderTargetProperties.__webglDepthStencilTexture = depthStencilTexture;
+				renderTargetProperties.__webglViewFramebuffers = viewFramebuffers;
 
-				}
+				_gl.bindFramebuffer( _gl.FRAMEBUFFER, null );
+				_gl.bindTexture( _gl.TEXTURE_2D_ARRAY, null );
 
 			}
 

+ 1 - 1
src/textures/DataTexture2DArray.js

@@ -9,7 +9,7 @@ function DataTexture2DArray( data, width, height, depth ) {
 
 	Texture.call( this, null );
 
-	this.image = { data: data, width: width, height: height, depth: depth };
+	this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 };
 
 	this.magFilter = NearestFilter;
 	this.minFilter = NearestFilter;

+ 1 - 1
src/textures/DataTexture3D.js

@@ -17,7 +17,7 @@ function DataTexture3D( data, width, height, depth ) {
 
 	Texture.call( this, null );
 
-	this.image = { data: data, width: width, height: height, depth: depth };
+	this.image = { data: data || null, width: width || 1, height: height || 1, depth: depth || 1 };
 
 	this.magFilter = NearestFilter;
 	this.minFilter = NearestFilter;

+ 7 - 2
test/unit/src/math/Euler.tests.js

@@ -425,17 +425,22 @@ export default QUnit.module( 'Maths', () => {
 
 		QUnit.test( "_onChangeCallback", ( assert ) => {
 
+			var b = false;
+			var a = new Euler( 11, 12, 13, "XYZ" );
 			var f = function () {
 
-				var b = true;
+				b = true;
+				assert.ok( a === this, "Passed!" );
 
 			};
 
-			var a = new Euler( 11, 12, 13, "XYZ" );
 			a._onChangeCallback = f;
 			assert.ok( a._onChangeCallback === f, "Passed!" );
 
 
+			a._onChangeCallback();
+			assert.ok( b, "Passed!" );
+
 		} );
 
 		// OTHERS

+ 2 - 1
test/unit/src/math/Quaternion.tests.js

@@ -733,13 +733,14 @@ export default QUnit.module( 'Maths', () => {
 		QUnit.test( "_onChangeCallback", ( assert ) => {
 
 			var b = false;
+			var a = new Quaternion( 11, 12, 13, 1 );
 			var f = function () {
 
 				b = true;
+				assert.ok( a === this, "Passed!" );
 
 			};
 
-			var a = new Quaternion( 11, 12, 13, 1 );
 			a._onChangeCallback = f;
 			assert.ok( a._onChangeCallback === f, "Passed!" );
 

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