Browse Source

Merge remote-tracking branch 'upstream/dev' into nodematerial-shared-uniforms

sunag 4 years ago
parent
commit
c56f3dfe4f

+ 2 - 3
build/three.js

@@ -19575,7 +19575,7 @@
 
 
 					var halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has('EXT_color_buffer_half_float') || capabilities.isWebGL2 && extensions.has('EXT_color_buffer_float'));
 					var halfFloatSupportedByExt = textureType === HalfFloatType && (extensions.has('EXT_color_buffer_half_float') || capabilities.isWebGL2 && extensions.has('EXT_color_buffer_float'));
 
 
-					if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(35738) && // IE11, Edge and Chrome Mac < 52 (#9513)
+					if (textureType !== UnsignedByteType && utils.convert(textureType) !== _gl.getParameter(35738) && // Edge and Chrome Mac < 52 (#9513)
 					!(textureType === FloatType && (capabilities.isWebGL2 || extensions.has('OES_texture_float') || extensions.has('WEBGL_color_buffer_float'))) && // Chrome Mac >= 52 and Firefox
 					!(textureType === FloatType && (capabilities.isWebGL2 || extensions.has('OES_texture_float') || extensions.has('WEBGL_color_buffer_float'))) && // Chrome Mac >= 52 and Firefox
 					!halfFloatSupportedByExt) {
 					!halfFloatSupportedByExt) {
 						console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.');
 						console.error('THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.');
@@ -31396,8 +31396,7 @@
 	}();
 	}();
 
 
 	function createPaths(text, size, data) {
 	function createPaths(text, size, data) {
-		var chars = Array.from ? Array.from(text) : String(text).split(''); // workaround for IE11, see #13988
-
+		var chars = Array.from(text);
 		var scale = size / data.resolution;
 		var scale = size / data.resolution;
 		var line_height = (data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness) * scale;
 		var line_height = (data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness) * scale;
 		var paths = [];
 		var paths = [];

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


+ 2 - 2
build/three.module.js

@@ -24854,7 +24854,7 @@ function WebGLRenderer( parameters ) {
 
 
 				const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) );
 				const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) );
 
 
-				if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // IE11, Edge and Chrome Mac < 52 (#9513)
+				if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( 35738 ) && // Edge and Chrome Mac < 52 (#9513)
 					! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox
 					! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox
 					! halfFloatSupportedByExt ) {
 					! halfFloatSupportedByExt ) {
 
 
@@ -40054,7 +40054,7 @@ class Font {
 
 
 function createPaths( text, size, data ) {
 function createPaths( text, size, data ) {
 
 
-	const chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // workaround for IE11, see #13988
+	const chars = Array.from( text );
 	const scale = size / data.resolution;
 	const scale = size / data.resolution;
 	const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
 	const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
 
 

+ 5 - 0
editor/js/Editor.js

@@ -26,6 +26,11 @@ function Editor() {
 		startPlayer: new Signal(),
 		startPlayer: new Signal(),
 		stopPlayer: new Signal(),
 		stopPlayer: new Signal(),
 
 
+		// vr
+
+		toggleVR: new Signal(),
+		exitedVR: new Signal(),
+
 		// notifications
 		// notifications
 
 
 		editorCleared: new Signal(),
 		editorCleared: new Signal(),

+ 48 - 3
editor/js/Menubar.View.js

@@ -14,18 +14,63 @@ function MenubarView( editor ) {
 	options.setClass( 'options' );
 	options.setClass( 'options' );
 	container.add( options );
 	container.add( options );
 
 
-	// VR mode
+	// Fullscreen
 
 
 	var option = new UIRow();
 	var option = new UIRow();
 	option.setClass( 'option' );
 	option.setClass( 'option' );
-	option.setTextContent( 'VR mode' );
+	option.setTextContent( 'Fullscreen' );
 	option.onClick( function () {
 	option.onClick( function () {
 
 
-		editor.signals.enterVR.dispatch();
+		if ( document.fullscreenElement === null ) {
+
+			document.documentElement.requestFullscreen();
+
+		} else if ( document.exitFullscreen ) {
+
+			document.exitFullscreen();
+
+		}
+
+		// Safari
+
+		if ( document.webkitFullscreenElement === null ) {
+
+			document.documentElement.webkitRequestFullscreen();
+
+		} else if ( document.webkitExitFullscreen ) {
+
+			document.webkitExitFullscreen();
+
+		}
 
 
 	} );
 	} );
 	options.add( option );
 	options.add( option );
 
 
+	// VR (Work in progress)
+
+	if ( 'xr' in navigator ) {
+
+		navigator.xr.isSessionSupported( 'immersive-vr' )
+			.then( function ( supported ) {
+
+				if ( supported ) {
+
+					var option = new UIRow();
+					option.setClass( 'option' );
+					option.setTextContent( 'VR' );
+					option.onClick( function () {
+
+						editor.signals.toggleVR.dispatch();
+
+					} );
+					options.add( option );
+
+				}
+
+			} );
+
+	}
+
 	return container;
 	return container;
 
 
 }
 }

+ 2 - 0
editor/js/Menubar.js

@@ -4,6 +4,7 @@ import { MenubarAdd } from './Menubar.Add.js';
 import { MenubarEdit } from './Menubar.Edit.js';
 import { MenubarEdit } from './Menubar.Edit.js';
 import { MenubarFile } from './Menubar.File.js';
 import { MenubarFile } from './Menubar.File.js';
 import { MenubarExamples } from './Menubar.Examples.js';
 import { MenubarExamples } from './Menubar.Examples.js';
+import { MenubarView } from './Menubar.View.js';
 import { MenubarHelp } from './Menubar.Help.js';
 import { MenubarHelp } from './Menubar.Help.js';
 import { MenubarPlay } from './Menubar.Play.js';
 import { MenubarPlay } from './Menubar.Play.js';
 import { MenubarStatus } from './Menubar.Status.js';
 import { MenubarStatus } from './Menubar.Status.js';
@@ -18,6 +19,7 @@ function Menubar( editor ) {
 	container.add( new MenubarAdd( editor ) );
 	container.add( new MenubarAdd( editor ) );
 	container.add( new MenubarPlay( editor ) );
 	container.add( new MenubarPlay( editor ) );
 	container.add( new MenubarExamples( editor ) );
 	container.add( new MenubarExamples( editor ) );
+	container.add( new MenubarView( editor ) );
 	container.add( new MenubarHelp( editor ) );
 	container.add( new MenubarHelp( editor ) );
 
 
 	container.add( new MenubarStatus( editor ) );
 	container.add( new MenubarStatus( editor ) );

+ 92 - 0
editor/js/Viewport.VR.js

@@ -0,0 +1,92 @@
+import * as THREE from '../../build/three.module.js';
+
+import { HTMLMesh } from './libs/three.html.js';
+
+import { XRControllerModelFactory } from '../../examples/jsm/webxr/XRControllerModelFactory.js';
+
+class VR {
+
+	constructor( editor ) {
+
+		const signals = editor.signals;
+
+		let group = null;
+		let renderer = null;
+
+		this.currentSession = null;
+
+		const onSessionStarted = async ( session ) => {
+
+			if ( group === null ) {
+
+				group = new THREE.Group();
+				editor.sceneHelpers.add( group );
+
+				const mesh = new HTMLMesh( document.getElementById( 'sidebar' ) );
+				mesh.position.set( 1, 1.5, 0 );
+				mesh.rotation.y = - 0.5;
+				group.add( mesh );
+
+				//
+
+				const controllerModelFactory = new XRControllerModelFactory();
+
+				const controllerGrip1 = renderer.xr.getControllerGrip( 0 );
+				controllerGrip1.add( controllerModelFactory.createControllerModel( controllerGrip1 ) );
+				group.add( controllerGrip1 );
+
+				const controllerGrip2 = renderer.xr.getControllerGrip( 1 );
+				controllerGrip2.add( controllerModelFactory.createControllerModel( controllerGrip2 ) );
+				group.add( controllerGrip2 );
+
+			}
+
+			group.visible = true;
+
+			this.currentSession = session;
+			this.currentSession.addEventListener( 'end', onSessionEnded );
+
+			await renderer.xr.setSession( this.currentSession );
+
+		}
+
+		 const onSessionEnded = async () => {
+
+			group.visible = false;
+
+			this.currentSession.removeEventListener( 'end', onSessionEnded );
+			this.currentSession = null;
+
+			await renderer.xr.setSession( null );
+
+			signals.exitedVR.dispatch();
+
+		};
+
+		signals.toggleVR.add( () => {
+
+			if ( this.currentSession === null ) {
+
+				const sessionInit = { optionalFeatures: [ 'local-floor', 'bounded-floor' ] };
+				navigator.xr.requestSession( 'immersive-vr', sessionInit ).then( onSessionStarted );
+
+			} else {
+
+				this.currentSession.end();
+
+			}
+
+		} );
+
+		signals.rendererChanged.add( ( value ) => {
+
+			renderer = value;
+			renderer.xr.enabled = true;
+
+		} );
+
+	}
+
+}
+
+export { VR };

+ 13 - 5
editor/js/Viewport.js

@@ -9,6 +9,7 @@ import { EditorControls } from './EditorControls.js';
 import { ViewportCamera } from './Viewport.Camera.js';
 import { ViewportCamera } from './Viewport.Camera.js';
 import { ViewportInfo } from './Viewport.Info.js';
 import { ViewportInfo } from './Viewport.Info.js';
 import { ViewHelper } from './Viewport.ViewHelper.js';
 import { ViewHelper } from './Viewport.ViewHelper.js';
+import { VR } from './Viewport.VR.js';
 
 
 import { SetPositionCommand } from './commands/SetPositionCommand.js';
 import { SetPositionCommand } from './commands/SetPositionCommand.js';
 import { SetRotationCommand } from './commands/SetRotationCommand.js';
 import { SetRotationCommand } from './commands/SetRotationCommand.js';
@@ -42,6 +43,7 @@ function Viewport( editor ) {
 
 
 	var grid = new THREE.GridHelper( 30, 30, 0x444444, 0x888888 );
 	var grid = new THREE.GridHelper( 30, 30, 0x444444, 0x888888 );
 	var viewHelper = new ViewHelper( camera, container );
 	var viewHelper = new ViewHelper( camera, container );
+	var vr = new VR( editor );
 
 
 	//
 	//
 
 
@@ -335,6 +337,7 @@ function Viewport( editor ) {
 
 
 		if ( renderer !== null ) {
 		if ( renderer !== null ) {
 
 
+			renderer.setAnimationLoop( null );
 			renderer.dispose();
 			renderer.dispose();
 			pmremGenerator.dispose();
 			pmremGenerator.dispose();
 			pmremTexture = null;
 			pmremTexture = null;
@@ -345,6 +348,7 @@ function Viewport( editor ) {
 
 
 		renderer = newRenderer;
 		renderer = newRenderer;
 
 
+		renderer.setAnimationLoop( animate );
 		renderer.setClearColor( 0xaaaaaa );
 		renderer.setClearColor( 0xaaaaaa );
 
 
 		if ( window.matchMedia ) {
 		if ( window.matchMedia ) {
@@ -659,6 +663,8 @@ function Viewport( editor ) {
 
 
 	} );
 	} );
 
 
+	signals.exitedVR.add( render );
+
 	//
 	//
 
 
 	signals.windowResize.add( function () {
 	signals.windowResize.add( function () {
@@ -695,8 +701,6 @@ function Viewport( editor ) {
 
 
 	function animate() {
 	function animate() {
 
 
-		requestAnimationFrame( animate );
-
 		var mixer = editor.mixer;
 		var mixer = editor.mixer;
 		var delta = clock.getDelta();
 		var delta = clock.getDelta();
 
 
@@ -716,12 +720,16 @@ function Viewport( editor ) {
 
 
 		}
 		}
 
 
+		if ( vr.currentSession !== null ) {
+
+			needsUpdate = true;
+
+		}
+
 		if ( needsUpdate === true ) render();
 		if ( needsUpdate === true ) render();
 
 
 	}
 	}
 
 
-	requestAnimationFrame( animate );
-
 	//
 	//
 
 
 	var startTime = 0;
 	var startTime = 0;
@@ -743,7 +751,7 @@ function Viewport( editor ) {
 
 
 			renderer.autoClear = false;
 			renderer.autoClear = false;
 			if ( showSceneHelpers === true ) renderer.render( sceneHelpers, camera );
 			if ( showSceneHelpers === true ) renderer.render( sceneHelpers, camera );
-			viewHelper.render( renderer );
+			if ( vr.currentSession === null ) viewHelper.render( renderer );
 			renderer.autoClear = true;
 			renderer.autoClear = true;
 
 
 		}
 		}

+ 237 - 0
editor/js/libs/three.html.js

@@ -0,0 +1,237 @@
+import {
+	CanvasTexture,
+	Mesh,
+	MeshBasicMaterial,
+	PlaneGeometry,
+	sRGBEncoding
+} from '../../../build/three.module.js';
+
+class HTMLMesh extends Mesh {
+
+	constructor( dom ) {
+
+		const texture = new HTMLTexture( dom );
+
+		const geometry = new PlaneGeometry( texture.image.width * 0.001, texture.image.height * 0.001 );
+		const material = new MeshBasicMaterial( { map: texture, toneMapped: false } );
+
+		super( geometry, material );
+
+	}
+
+}
+
+class HTMLTexture extends CanvasTexture {
+
+	constructor( dom ) {
+
+		super( html2canvas( dom ) );
+
+		this.dom = dom;
+		this.encoding = sRGBEncoding;
+		this.anisotropy = 16;
+
+	}
+
+	update() {
+
+		this.image = html2canvas( this.dom );
+		this.needsUpdate = true;
+
+	}
+
+}
+
+//
+
+function html2canvas( element ) {
+
+	var range = document.createRange();
+
+	function Clipper( context ) {
+
+		var clips = [];
+		var isClipping = false;
+
+		function doClip() {
+
+			if ( isClipping ) {
+
+				isClipping = false;
+				context.restore();
+
+			}
+
+			if ( clips.length === 0 ) return;
+
+			var minX = - Infinity, minY = - Infinity;
+			var maxX = Infinity, maxY = Infinity;
+
+			for ( var i = 0; i < clips.length; i ++ ) {
+
+				var clip = clips[ i ];
+
+				minX = Math.max( minX, clip.x );
+				minY = Math.max( minY, clip.y );
+				maxX = Math.min( maxX, clip.x + clip.width );
+				maxY = Math.min( maxY, clip.y + clip.height );
+
+			}
+
+			context.save();
+			context.beginPath();
+			context.rect( minX, minY, maxX - minX, maxY - minY );
+			context.clip();
+
+			isClipping = true;
+
+		}
+
+		return {
+			add: function ( clip ) {
+
+				clips.push( clip );
+				doClip();
+
+			},
+			remove: function () {
+
+				clips.pop();
+				doClip();
+
+			}
+
+		};
+
+	}
+
+	function drawText( style, x, y, string ) {
+
+		if ( string !== '' ) {
+
+			context.font = style.fontSize + ' ' + style.fontFamily;
+			context.textBaseline = 'top';
+			context.fillStyle = style.color;
+			context.fillText( string, x, y );
+
+		}
+
+	}
+
+	function drawBorder( style, which, x, y, width, height ) {
+
+		var borderWidth = style[ which + 'Width' ];
+		var borderStyle = style[ which + 'Style' ];
+		var borderColor = style[ which + 'Color' ];
+
+		if ( borderWidth !== '0px' && borderStyle !== 'none' && borderColor !== 'transparent' && borderColor !== 'rgba(0, 0, 0, 0)' ) {
+
+			context.strokeStyle = borderColor;
+			context.beginPath();
+			context.moveTo( x, y );
+			context.lineTo( x + width, y + height );
+			context.stroke();
+
+		}
+
+	}
+
+	function drawElement( element, style ) {
+
+		var x = 0, y = 0, width = 0, height = 0;
+
+		if ( element.nodeType === 3 ) {
+
+			// text
+
+			range.selectNode( element );
+
+			var rect = range.getBoundingClientRect();
+
+			x = rect.left - offset.left - 0.5;
+			y = rect.top - offset.top - 0.5;
+			width = rect.width;
+			height = rect.height;
+
+			drawText( style, x, y, element.nodeValue.trim() );
+
+		} else {
+
+			if ( element.style.display === 'none' ) return;
+
+			var rect = element.getBoundingClientRect();
+
+			x = rect.left - offset.left - 0.5;
+			y = rect.top - offset.top - 0.5;
+			width = rect.width;
+			height = rect.height;
+
+			style = window.getComputedStyle( element );
+
+			var backgroundColor = style.backgroundColor;
+
+			if ( backgroundColor !== 'transparent' && backgroundColor !== 'rgba(0, 0, 0, 0)' ) {
+
+				context.fillStyle = backgroundColor;
+				context.fillRect( x, y, width, height );
+
+			}
+
+			drawBorder( style, 'borderTop', x, y, width, 0 );
+			drawBorder( style, 'borderLeft', x, y, 0, height );
+			drawBorder( style, 'borderBottom', x, y + height, width, 0 );
+			drawBorder( style, 'borderRight', x + width, y, 0, height );
+
+			if ( element.type === 'color' || element.type === 'text' ) {
+
+				clipper.add( { x: x, y: y, width: width, height: height } );
+
+				drawText( style, x + parseInt( style.paddingLeft ), y + parseInt( style.paddingTop ), element.value );
+
+				clipper.remove();
+
+			}
+
+		}
+
+		/*
+		// debug
+		context.strokeStyle = '#' + Math.random().toString( 16 ).slice( - 3 );
+		context.strokeRect( x - 0.5, y - 0.5, width + 1, height + 1 );
+		*/
+
+		var isClipping = style.overflow === 'auto' || style.overflow === 'hidden';
+
+		if ( isClipping ) clipper.add( { x: x, y: y, width: width, height: height } );
+
+		for ( var i = 0; i < element.childNodes.length; i ++ ) {
+
+			drawElement( element.childNodes[ i ], style );
+
+		}
+
+		if ( isClipping ) clipper.remove();
+
+	}
+
+	var offset = element.getBoundingClientRect();
+
+	var canvas = document.createElement( 'canvas' );
+	canvas.width = offset.width;
+	canvas.height = offset.height;
+
+	var context = canvas.getContext( '2d'/*, { alpha: false }*/ );
+
+	var clipper = new Clipper( context );
+
+	console.time( 'drawElement' );
+
+	drawElement( element );
+
+	console.timeEnd( 'drawElement' );
+
+	return canvas;
+
+}
+
+export { HTMLMesh, HTMLTexture };

+ 4 - 3
examples/files.json

@@ -314,11 +314,11 @@
 		"webgl2_volume_perlin"
 		"webgl2_volume_perlin"
 	],
 	],
 	"webgpu": [
 	"webgpu": [
-		"webgpu_sandbox",
-		"webgpu_rtt",
 		"webgpu_compute",
 		"webgpu_compute",
 		"webgpu_instance_uniform",
 		"webgpu_instance_uniform",
-		"webgpu_materials"
+		"webgpu_materials",
+		"webgpu_rtt",
+		"webgpu_sandbox"
 	],
 	],
 	"webaudio": [
 	"webaudio": [
 		"webaudio_orientation",
 		"webaudio_orientation",
@@ -329,6 +329,7 @@
 	"webxr": [
 	"webxr": [
 		"webxr_ar_cones",
 		"webxr_ar_cones",
 		"webxr_ar_hittest",
 		"webxr_ar_hittest",
+		"webxr_ar_lighting",
 		"webxr_ar_paint",
 		"webxr_ar_paint",
 		"webxr_vr_ballshooter",
 		"webxr_vr_ballshooter",
 		"webxr_vr_cubes",
 		"webxr_vr_cubes",

+ 21 - 5
examples/jsm/loaders/3DMLoader.js

@@ -1247,11 +1247,16 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 
 			}
 			}
 
 
+			if ( _geometry.userStringCount > 0 ) {
+
+				attributes.geometry.userStrings = _geometry.getUserStrings();
+
+			}
+
 			attributes.drawColor = _attributes.drawColor( doc );
 			attributes.drawColor = _attributes.drawColor( doc );
 
 
 			objectType = objectType.constructor.name;
 			objectType = objectType.constructor.name;
 			objectType = objectType.substring( 11, objectType.length );
 			objectType = objectType.substring( 11, objectType.length );
-			attributes.geometry.objectType = objectType;
 
 
 			return { geometry, attributes, objectType };
 			return { geometry, attributes, objectType };
 
 
@@ -1269,13 +1274,24 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 
 
 		for ( var property in object ) {
 		for ( var property in object ) {
 
 
-			if ( typeof object[ property ] !== 'function' ) {
+			var value = object[ property ];
+
+			if ( typeof value !== 'function' ) {
+
+				if ( typeof value === 'object' && value !== null && value.hasOwnProperty( 'constructor' ) ) {
 
 
-				result[ property ] = object[ property ];
+					result[ property ] = { name: value.constructor.name, value: value.value };
+
+				} else {
+
+					result[ property ] = value;
+
+				}
 
 
 			} else {
 			} else {
 
 
-				// console.log( `${property}: ${object[ property ]}` );
+				// these are functions that could be called to extract more data.
+				//console.log( `${property}: ${object[ property ].constructor.name}` );
 
 
 			}
 			}
 
 
@@ -1368,7 +1384,7 @@ Rhino3dmLoader.Rhino3dmWorker = function () {
 			var tan = curve.tangentAt( t );
 			var tan = curve.tangentAt( t );
 			var prevTan = curve.tangentAt( ts.slice( - 1 )[ 0 ] );
 			var prevTan = curve.tangentAt( ts.slice( - 1 )[ 0 ] );
 
 
-			// Duplicaated from THREE.Vector3
+			// Duplicated from THREE.Vector3
 			// How to pass imports to worker?
 			// How to pass imports to worker?
 
 
 			var tS = tan[ 0 ] * tan[ 0 ] + tan[ 1 ] * tan[ 1 ] + tan[ 2 ] * tan[ 2 ];
 			var tS = tan[ 0 ] * tan[ 0 ] + tan[ 1 ] * tan[ 1 ] + tan[ 2 ] * tan[ 2 ];

+ 7 - 3
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -97,10 +97,14 @@ class WebGPURenderPipelines {
 
 
 			const materialProperties = properties.get( material );
 			const materialProperties = properties.get( material );
 
 
-			const disposeCallback = onMaterialDispose.bind( this );
-			materialProperties.disposeCallback = disposeCallback;
+			if ( materialProperties.disposeCallback === undefined ) {
 
 
-			material.addEventListener( 'dispose', disposeCallback );
+				const disposeCallback = onMaterialDispose.bind( this );
+				materialProperties.disposeCallback = disposeCallback;
+
+				material.addEventListener( 'dispose', disposeCallback );
+
+			}
 
 
 			// determine shader attributes
 			// determine shader attributes
 
 

+ 2 - 2
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -852,7 +852,7 @@ class WebGPURenderer {
 				size: {
 				size: {
 					width: this._width * this._pixelRatio,
 					width: this._width * this._pixelRatio,
 					height: this._height * this._pixelRatio,
 					height: this._height * this._pixelRatio,
-					depth: 1
+					depthOrArrayLayers: 1
 				},
 				},
 				sampleCount: this._parameters.sampleCount,
 				sampleCount: this._parameters.sampleCount,
 				format: GPUTextureFormat.BRGA8Unorm,
 				format: GPUTextureFormat.BRGA8Unorm,
@@ -875,7 +875,7 @@ class WebGPURenderer {
 				size: {
 				size: {
 					width: this._width * this._pixelRatio,
 					width: this._width * this._pixelRatio,
 					height: this._height * this._pixelRatio,
 					height: this._height * this._pixelRatio,
-					depth: 1
+					depthOrArrayLayers: 1
 				},
 				},
 				sampleCount: this._parameters.sampleCount,
 				sampleCount: this._parameters.sampleCount,
 				format: GPUTextureFormat.Depth24PlusStencil8,
 				format: GPUTextureFormat.Depth24PlusStencil8,

+ 6 - 6
examples/jsm/renderers/webgpu/WebGPUTextures.js

@@ -203,7 +203,7 @@ class WebGPUTextures {
 				size: {
 				size: {
 					width: width,
 					width: width,
 					height: height,
 					height: height,
-					depth: 1
+					depthOrArrayLayers: 1
 				},
 				},
 				format: colorTextureFormat,
 				format: colorTextureFormat,
 				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.SAMPLED
 				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.SAMPLED
@@ -231,7 +231,7 @@ class WebGPUTextures {
 					size: {
 					size: {
 						width: width,
 						width: width,
 						height: height,
 						height: height,
-						depth: 1
+						depthOrArrayLayers: 1
 					},
 					},
 					format: depthTextureFormat,
 					format: depthTextureFormat,
 					usage: GPUTextureUsage.RENDER_ATTACHMENT
 					usage: GPUTextureUsage.RENDER_ATTACHMENT
@@ -332,7 +332,7 @@ class WebGPUTextures {
 			size: {
 			size: {
 				width: width,
 				width: width,
 				height: height,
 				height: height,
-				depth: depth,
+				depthOrArrayLayers: depth,
 			},
 			},
 			mipLevelCount: mipLevelCount,
 			mipLevelCount: mipLevelCount,
 			sampleCount: 1,
 			sampleCount: 1,
@@ -403,7 +403,7 @@ class WebGPUTextures {
 			{
 			{
 				width: image.width,
 				width: image.width,
 				height: image.height,
 				height: image.height,
-				depth: ( image.depth !== undefined ) ? image.depth : 1
+				depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1
 			} );
 			} );
 
 
 	}
 	}
@@ -436,7 +436,7 @@ class WebGPUTextures {
 			}, {
 			}, {
 				width: image.width,
 				width: image.width,
 				height: image.height,
 				height: image.height,
-				depth: 1
+				depthOrArrayLayers: 1
 			}
 			}
 		);
 		);
 
 
@@ -470,7 +470,7 @@ class WebGPUTextures {
 				{
 				{
 					width: Math.ceil( width / blockData.width ) * blockData.width,
 					width: Math.ceil( width / blockData.width ) * blockData.width,
 					height: Math.ceil( height / blockData.width ) * blockData.width,
 					height: Math.ceil( height / blockData.width ) * blockData.width,
-					depth: 1,
+					depthOrArrayLayers: 1,
 				} );
 				} );
 
 
 		}
 		}

+ 8 - 2
examples/jsm/webxr/ARButton.js

@@ -31,7 +31,13 @@ class ARButton {
 				path.setAttribute( 'stroke-width', 2 );
 				path.setAttribute( 'stroke-width', 2 );
 				svg.appendChild( path );
 				svg.appendChild( path );
 
 
-				sessionInit.optionalFeatures = [ 'dom-overlay' ];
+				if ( sessionInit.optionalFeatures === undefined ) {
+
+					sessionInit.optionalFeatures = [];
+
+				}
+
+				sessionInit.optionalFeatures.push( 'dom-overlay' );
 				sessionInit.domOverlay = { root: overlay };
 				sessionInit.domOverlay = { root: overlay };
 
 
 			}
 			}
@@ -47,7 +53,7 @@ class ARButton {
 				renderer.xr.setReferenceSpaceType( 'local' );
 				renderer.xr.setReferenceSpaceType( 'local' );
 
 
 				await renderer.xr.setSession( session );
 				await renderer.xr.setSession( session );
-        
+
 				button.textContent = 'STOP AR';
 				button.textContent = 'STOP AR';
 				sessionInit.domOverlay.root.style.display = '';
 				sessionInit.domOverlay.root.style.display = '';
 
 

+ 221 - 0
examples/jsm/webxr/XREstimatedLight.js

@@ -0,0 +1,221 @@
+import {
+	DirectionalLight,
+	Group,
+	LightProbe,
+	WebGLCubeRenderTarget
+} from "../../../build/three.module.js";
+
+class SessionLightProbe {
+
+	constructor( xrLight, renderer, lightProbe, environmentEstimation, estimationStartCallback ) {
+
+		this.xrLight = xrLight;
+		this.renderer = renderer;
+		this.lightProbe = lightProbe;
+		this.xrWebGLBinding = null;
+		this.estimationStartCallback = estimationStartCallback;
+		this.frameCallback = this.onXRFrame.bind( this );
+
+		const session = renderer.xr.getSession();
+
+		// If the XRWebGLBinding class is available then we can also query an
+		// estimated reflection cube map.
+		if ( environmentEstimation && 'XRWebGLBinding' in window ) {
+
+			// This is the simplest way I know of to initialize a WebGL cubemap in Three.
+			const cubeRenderTarget = new WebGLCubeRenderTarget( 16 );
+			xrLight.environment = cubeRenderTarget.texture;
+
+			const gl = renderer.getContext();
+
+			// Ensure that we have any extensions needed to use the preferred cube map format.
+			switch ( session.preferredReflectionFormat ) {
+
+				case 'srgba8':
+					gl.getExtension( 'EXT_sRGB' );
+					break;
+
+				case 'rgba16f':
+					gl.getExtension( 'OES_texture_half_float' );
+					break;
+
+			}
+
+			this.xrWebGLBinding = new XRWebGLBinding( session, gl );
+
+			this.lightProbe.addEventListener('reflectionchange', () => {
+
+				this.updateReflection();
+
+			});
+
+		}
+
+		// Start monitoring the XR animation frame loop to look for lighting
+		// estimation changes.
+		session.requestAnimationFrame( this.frameCallback );
+
+	}
+
+	updateReflection() {
+
+		const textureProperties = this.renderer.properties.get( this.xrLight.environment );
+
+		if ( textureProperties ) {
+
+			const cubeMap = this.xrWebGLBinding.getReflectionCubeMap( this.lightProbe );
+
+			if ( cubeMap ) {
+
+				textureProperties.__webglTexture = cubeMap;
+
+			}
+
+		}
+
+	}
+
+	onXRFrame( time, xrFrame ) {
+
+		// If either this obejct or the XREstimatedLight has been destroyed, stop
+		// running the frame loop.
+		if ( ! this.xrLight ) {
+
+			return;
+
+		}
+
+		const session = xrFrame.session;
+		session.requestAnimationFrame( this.frameCallback );
+
+		const lightEstimate = xrFrame.getLightEstimate( this.lightProbe );
+		if ( lightEstimate ) {
+
+			// We can copy the estimate's spherical harmonics array directly into the light probe.
+			this.xrLight.lightProbe.sh.fromArray( lightEstimate.sphericalHarmonicsCoefficients );
+			this.xrLight.lightProbe.intensity = 1.0;
+
+			// For the directional light we have to normalize the color and set the scalar as the
+			// intensity, since WebXR can return color values that exceed 1.0.
+			const intensityScalar = Math.max( 1.0,
+				Math.max( lightEstimate.primaryLightIntensity.x,
+					Math.max( lightEstimate.primaryLightIntensity.y,
+						lightEstimate.primaryLightIntensity.z ) ) );
+
+			this.xrLight.directionalLight.color.setRGB(
+				lightEstimate.primaryLightIntensity.x / intensityScalar,
+				lightEstimate.primaryLightIntensity.y / intensityScalar,
+				lightEstimate.primaryLightIntensity.z / intensityScalar );
+			this.xrLight.directionalLight.intensity = intensityScalar;
+			this.xrLight.directionalLight.position.copy( lightEstimate.primaryLightDirection );
+
+			if ( this.estimationStartCallback ) {
+
+				this.estimationStartCallback();
+				this.estimationStartCallback = null;
+
+			}
+
+		}
+
+	}
+
+	dispose() {
+
+		this.xrLight = null;
+		this.renderer = null;
+		this.lightProbe = null;
+		this.xrWebGLBinding = null;
+
+	}
+
+}
+
+export class XREstimatedLight extends Group {
+
+	constructor( renderer, environmentEstimation = true ) {
+
+		super();
+
+		this.lightProbe = new LightProbe();
+		this.lightProbe.intensity = 0;
+		this.add( this.lightProbe );
+
+		this.directionalLight = new DirectionalLight();
+		this.directionalLight.intensity = 0;
+		this.add( this.directionalLight );
+
+		// Will be set to a cube map in the SessionLightProbe is environment estimation is
+		// available and requested.
+		this.environment = null;
+
+		let sessionLightProbe = null;
+		let estimationStarted = false;
+		renderer.xr.addEventListener( 'sessionstart', () => {
+
+			const session = renderer.xr.getSession();
+
+			if ( 'requestLightProbe' in session ) {
+
+				session.requestLightProbe( {
+
+					reflectionFormat: session.preferredReflectionFormat
+
+				} ).then( ( probe ) => {
+
+					sessionLightProbe = new SessionLightProbe( this, renderer, probe, environmentEstimation, () => {
+
+						estimationStarted = true;
+
+						// Fired to indicate that the estimated lighting values are now being updated.
+						this.dispatchEvent( { type: 'estimationstart' } );
+
+					} );
+
+				} );
+
+			}
+
+		} );
+
+		renderer.xr.addEventListener( 'sessionend', () => {
+
+			if ( sessionLightProbe ) {
+
+				sessionLightProbe.dispose();
+				sessionLightProbe = null;
+
+			}
+
+			if ( estimationStarted ) {
+
+				// Fired to indicate that the estimated lighting values are no longer being updated.
+				this.dispatchEvent( { type: 'estimationend' } );
+
+			}
+
+		} );
+
+		// Done inline to provide access to sessionLightProbe.
+		this.dispose = () => {
+
+			if ( sessionLightProbe ) {
+
+				sessionLightProbe.dispose();
+				sessionLightProbe = null;
+
+			}
+
+			this.remove( this.lightProbe );
+			this.lightProbe = null;
+
+			this.remove( this.directionalLight );
+			this.directionalLight = null;
+
+			this.environment = null;
+
+		};
+
+	}
+
+}

+ 1 - 1
examples/misc_legacy.html

@@ -8,7 +8,7 @@
 	</head>
 	</head>
 	<body>
 	<body>
 
 
-		<script src="https://polyfill.io/v3/polyfill.min.js?features=Function.name%2CMath.log2%2CMath.sign%2CNumber.EPSILON%2CNumber.isInteger%2CObject.assign%2CObject.values%2CPromise"></script>
+		<script src="https://polyfill.io/v3/polyfill.min.js?features=Array.from%2CFunction.name%2CMath.log2%2CMath.sign%2CNumber.EPSILON%2CNumber.isInteger%2CObject.assign%2CObject.values%2CPromise"></script>
 
 
 		<script src="../build/three.js"></script>
 		<script src="../build/three.js"></script>
 		<script src="./js/controls/OrbitControls.js"></script>
 		<script src="./js/controls/OrbitControls.js"></script>

BIN
examples/screenshots/webgpu_compute.jpg


BIN
examples/screenshots/webgpu_materials.jpg


BIN
examples/screenshots/webgpu_rtt.jpg


BIN
examples/screenshots/webgpu_sandbox.jpg


BIN
examples/screenshots/webxr_ar_lighting.jpg


+ 180 - 0
examples/webxr_ar_lighting.html

@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js ar - lighting estimation</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> ar - Lighting Estimation<br/>
+			(Chrome Android 90+)
+		</div>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+			import { RGBELoader } from './jsm/loaders/RGBELoader.js';
+			import { ARButton } from './jsm/webxr/ARButton.js';
+			import { XREstimatedLight } from './jsm/webxr/XREstimatedLight.js';
+
+			let camera, scene, renderer;
+			let controller;
+			let defaultEnvironment;
+
+			init();
+			animate();
+
+			function init() {
+
+				const container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				scene = new THREE.Scene();
+
+				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 20 );
+
+				const defaultLight = new THREE.HemisphereLight( 0xffffff, 0xbbbbff, 1 );
+				defaultLight.position.set( 0.5, 1, 0.25 );
+				scene.add( defaultLight );
+
+				//
+
+				renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.outputEncoding = THREE.sRGBEncoding;
+				renderer.physicallyCorrectLights = true;
+				renderer.xr.enabled = true;
+				container.appendChild( renderer.domElement );
+
+				// Don't add the XREstimatedLight to the scene initially.
+				// It doesn't have any estimated lighting values until an AR session starts.
+
+				const xrLight = new XREstimatedLight( renderer );
+
+				xrLight.addEventListener( 'estimationstart', () => {
+
+					// Swap the default light out for the estimated one one we start getting some estimated values.
+					scene.add( xrLight );
+					scene.remove( defaultLight );
+
+					// The estimated lighting also provides an environment cubemap, which we can apply here.
+					if ( xrLight.environment ) {
+
+						scene.environment = xrLight.environment;
+
+					}
+
+				} );
+
+				xrLight.addEventListener( 'estimationend', () => {
+
+					// Swap the lights back when we stop receiving estimated values.
+					scene.add( defaultLight );
+					scene.remove( xrLight );
+
+					// Revert back to the default environment.
+					scene.environment = defaultEnvironment;
+
+				} );
+
+				//
+
+				const pmremGenerator = new THREE.PMREMGenerator( renderer );
+				pmremGenerator.compileEquirectangularShader();
+
+				new RGBELoader()
+					.setDataType( THREE.UnsignedByteType )
+					.setPath( 'textures/equirectangular/' )
+					.load( 'royal_esplanade_1k.hdr', function ( texture ) {
+
+						defaultEnvironment = pmremGenerator.fromEquirectangular( texture ).texture;
+
+						scene.environment = defaultEnvironment;
+
+						texture.dispose();
+						pmremGenerator.dispose();
+
+					} );
+
+				//
+
+				// In order for lighting estimation to work, 'light-estimation' must be included as either an optional or required feature.
+				document.body.appendChild( ARButton.createButton( renderer, { optionalFeatures: [ 'light-estimation' ] } ) );
+
+				//
+
+				const ballGeometry = new THREE.SphereBufferGeometry( 0.175, 32, 32 );
+				const ballGroup = new THREE.Group();
+				ballGroup.position.z = - 2;
+
+				const rows = 3;
+				const cols = 3;
+
+				for ( let i = 0; i < rows; i ++ ) {
+
+					for ( let j = 0; j < cols; j ++ ) {
+
+						const ballMaterial = new THREE.MeshStandardMaterial( {
+							color: 0xdddddd,
+							roughness: i / rows,
+							metalness: j / cols
+						} );
+						const ballMesh = new THREE.Mesh( ballGeometry, ballMaterial );
+						ballMesh.position.set( ( i + 0.5 - rows * 0.5 ) * 0.4, ( j + 0.5 - cols * 0.5 ) * 0.4, 0 );
+						ballGroup.add( ballMesh );
+
+					}
+
+				}
+
+				scene.add( ballGroup );
+
+				//
+
+				function onSelect() {
+
+					ballGroup.position.set( 0, 0, - 2 ).applyMatrix4( controller.matrixWorld );
+					ballGroup.quaternion.setFromRotationMatrix( controller.matrixWorld );
+
+				}
+
+				controller = renderer.xr.getController( 0 );
+				controller.addEventListener( 'select', onSelect );
+				scene.add( controller );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				renderer.setAnimationLoop( render );
+
+			}
+
+			function render() {
+
+				renderer.render( scene, camera );
+
+			}
+
+		</script>
+	</body>
+</html>

+ 1 - 1
src/extras/core/Font.js

@@ -29,7 +29,7 @@ class Font {
 
 
 function createPaths( text, size, data ) {
 function createPaths( text, size, data ) {
 
 
-	const chars = Array.from ? Array.from( text ) : String( text ).split( '' ); // workaround for IE11, see #13988
+	const chars = Array.from( text );
 	const scale = size / data.resolution;
 	const scale = size / data.resolution;
 	const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
 	const line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;
 
 

+ 1 - 1
src/renderers/WebGLRenderer.js

@@ -1895,7 +1895,7 @@ function WebGLRenderer( parameters ) {
 
 
 				const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) );
 				const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) );
 
 
-				if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513)
+				if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513)
 					! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox
 					! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox
 					! halfFloatSupportedByExt ) {
 					! halfFloatSupportedByExt ) {
 
 

+ 7 - 1
test/e2e/puppeteer.js

@@ -47,7 +47,13 @@ const exceptionList = [
 	'webgl_tiled_forward', // exception for Github Actions
 	'webgl_tiled_forward', // exception for Github Actions
 	'webgl_video_kinect', // video tag not deterministic enough
 	'webgl_video_kinect', // video tag not deterministic enough
 	'webgl_worker_offscreencanvas', // in a worker, not robust
 	'webgl_worker_offscreencanvas', // in a worker, not robust
-
+	// webxr
+	'webxr_ar_lighting',
+	// webgpu
+	'webgpu_compute',
+	'webgpu_materials',
+	'webgpu_rtt',
+	'webgpu_sandbox'
 ].concat( ( process.platform === 'win32' ) ? [
 ].concat( ( process.platform === 'win32' ) ? [
 
 
 	'webgl_effects_ascii' // windows fonts not supported
 	'webgl_effects_ascii' // windows fonts not supported

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