Browse Source

Editor: Refactored XR mode code.

Mr.doob 1 year ago
parent
commit
0bf3908b73
6 changed files with 222 additions and 165 deletions
  1. 2 3
      editor/js/Editor.js
  2. 24 3
      editor/js/Menubar.View.js
  3. 0 131
      editor/js/Viewport.VR.js
  4. 183 0
      editor/js/Viewport.XR.js
  5. 12 27
      editor/js/Viewport.js
  6. 1 1
      editor/sw.js

+ 2 - 3
editor/js/Editor.js

@@ -27,10 +27,9 @@ function Editor() {
 		startPlayer: new Signal(),
 		startPlayer: new Signal(),
 		stopPlayer: new Signal(),
 		stopPlayer: new Signal(),
 
 
-		// vr
+		// xr
 
 
-		toggleVR: new Signal(),
-		exitedVR: new Signal(),
+		enterXR: new Signal(),
 
 
 		// notifications
 		// notifications
 
 

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

@@ -52,21 +52,42 @@ function MenubarView( editor ) {
 
 
 	if ( 'xr' in navigator ) {
 	if ( 'xr' in navigator ) {
 
 
-		navigator.xr.isSessionSupported( 'immersive-vr' )
+		navigator.xr.isSessionSupported( 'immersive-ar' )
 			.then( function ( supported ) {
 			.then( function ( supported ) {
 
 
 				if ( supported ) {
 				if ( supported ) {
 
 
 					const option = new UIRow();
 					const option = new UIRow();
 					option.setClass( 'option' );
 					option.setClass( 'option' );
-					option.setTextContent( 'VR' );
+					option.setTextContent( 'AR' );
 					option.onClick( function () {
 					option.onClick( function () {
 
 
-						editor.signals.toggleVR.dispatch();
+						editor.signals.enterXR.dispatch( 'immersive-ar' );
 
 
 					} );
 					} );
 					options.add( option );
 					options.add( option );
 
 
+				} else {
+
+					navigator.xr.isSessionSupported( 'immersive-vr' )
+						.then( function ( supported ) {
+
+							if ( supported ) {
+
+								const option = new UIRow();
+								option.setClass( 'option' );
+								option.setTextContent( 'VR' );
+								option.onClick( function () {
+
+									editor.signals.enterXR.dispatch( 'immersive-vr' );
+
+								} );
+								options.add( option );
+
+							}
+
+						} );
+
 				}
 				}
 
 
 			} );
 			} );

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

@@ -1,131 +0,0 @@
-import * as THREE from 'three';
-
-import { HTMLMesh } from 'three/addons/interactive/HTMLMesh.js';
-import { InteractiveGroup } from 'three/addons/interactive/InteractiveGroup.js';
-
-import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js';
-
-class VR {
-
-	constructor( editor ) {
-
-		const signals = editor.signals;
-
-		let group = null;
-
-		let camera = null;
-		let renderer = null;
-
-		const intersectables = [];
-
-		this.currentSession = null;
-
-		const onSessionStarted = async ( session ) => {
-
-			const sidebar = document.getElementById( 'sidebar' );
-			sidebar.style.width = '300px';
-			sidebar.style.height = '700px';
-
-			//
-
-			if ( group === null ) {
-
-				group = new InteractiveGroup( renderer );
-				editor.sceneHelpers.add( group );
-
-				const mesh = new HTMLMesh( sidebar );
-				mesh.position.set( 1, 1.5, - 0.5 );
-				mesh.rotation.y = - 0.5;
-				mesh.scale.setScalar( 2 );
-				group.add( mesh );
-
-				intersectables.push( mesh );
-
-				// controllers
-
-				const geometry = new THREE.BufferGeometry();
-				geometry.setFromPoints( [ new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, - 5 ) ] );
-
-				const controller1 = renderer.xr.getController( 0 );
-				controller1.add( new THREE.Line( geometry ) );
-				group.add( controller1 );
-
-				const controller2 = renderer.xr.getController( 1 );
-				controller2.add( new THREE.Line( geometry ) );
-				group.add( controller2 );
-
-				//
-
-				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 );
-
-			}
-
-			camera = editor.camera.clone();
-
-			group.visible = true;
-
-			this.currentSession = session;
-			this.currentSession.addEventListener( 'end', onSessionEnded );
-
-			await renderer.xr.setSession( this.currentSession );
-
-		};
-
-		const onSessionEnded = async () => {
-
-			const sidebar = document.getElementById( 'sidebar' );
-			sidebar.style.width = '';
-			sidebar.style.height = '';
-
-			//
-
-			editor.camera.copy( camera );
-
-			group.visible = false;
-
-			this.currentSession.removeEventListener( 'end', onSessionEnded );
-			this.currentSession = null;
-
-			await renderer.xr.setSession( null );
-
-			signals.exitedVR.dispatch();
-
-		};
-
-		// signals
-
-		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.rendererCreated.add( ( value ) => {
-
-			renderer = value;
-			renderer.xr.enabled = true;
-
-		} );
-
-	}
-
-}
-
-export { VR };

+ 183 - 0
editor/js/Viewport.XR.js

@@ -0,0 +1,183 @@
+import * as THREE from 'three';
+
+import { SetPositionCommand } from './commands/SetPositionCommand.js';
+import { SetRotationCommand } from './commands/SetRotationCommand.js';
+
+import { HTMLMesh } from 'three/addons/interactive/HTMLMesh.js';
+import { InteractiveGroup } from 'three/addons/interactive/InteractiveGroup.js';
+
+import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js';
+
+class XR {
+
+	constructor( editor ) {
+
+		const signals = editor.signals;
+
+		let group = null;
+		let renderer = null;
+
+		const camera = new THREE.PerspectiveCamera();
+
+		const onSessionStarted = async ( session ) => {
+
+			camera.copy( editor.camera );
+
+			const sidebar = document.getElementById( 'sidebar' );
+			sidebar.style.width = '300px';
+			sidebar.style.height = '700px';
+
+			//
+
+			if ( group === null ) {
+
+				group = new InteractiveGroup();
+				group.listenToXRControllerEvents( renderer );
+
+				const mesh = new HTMLMesh( sidebar );
+				mesh.position.set( 1, 1.5, - 0.5 );
+				mesh.rotation.y = - 0.5;
+				mesh.scale.setScalar( 2 );
+				group.add( mesh );
+
+				// controllers
+
+				const raycaster = new THREE.Raycaster();
+				const tempMatrix = new THREE.Matrix4();
+
+				function getIntersections( controller ) {
+
+					tempMatrix.identity().extractRotation( controller.matrixWorld );
+
+					raycaster.ray.origin.setFromMatrixPosition( controller.matrixWorld );
+					raycaster.ray.direction.set( 0, 0, - 1 ).applyMatrix4( tempMatrix );
+
+					return raycaster.intersectObjects( editor.scene.children, false );
+
+				}
+
+				function onSelectStart( event ) {
+
+					const controller = event.target;
+					const intersections = getIntersections( controller );
+
+					if ( intersections.length > 0 ) {
+
+						const intersection = intersections[ 0 ];
+						const object = intersection.object;
+
+						signals.objectSelected.dispatch( object );
+
+						controller.userData.selected = object;
+						controller.userData.position = object.position.clone();
+						controller.userData.rotation = object.rotation.clone();
+
+						controller.attach( object );
+
+					}
+
+				}
+
+				function onSelectEnd( event ) {
+
+					const controller = event.target;
+
+					if ( controller.userData.selected !== undefined ) {
+
+						const object = controller.userData.selected;
+						editor.scene.attach( object );
+
+						controller.userData.selected = undefined;
+
+						editor.execute( new SetPositionCommand( editor, object, object.position, controller.userData.position ) );
+						editor.execute( new SetRotationCommand( editor, object, object.rotation, controller.userData.rotation ) );
+
+						signals.objectChanged.dispatch( object );
+
+					} else {
+
+						signals.objectSelected.dispatch( null );
+
+					}
+
+				}
+
+				const geometry = new THREE.BufferGeometry();
+				geometry.setFromPoints( [ new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, - 5 ) ] );
+
+				const controller1 = renderer.xr.getController( 0 );
+				controller1.addEventListener( 'selectstart', onSelectStart );
+				controller1.addEventListener( 'selectend', onSelectEnd );
+				controller1.add( new THREE.Line( geometry ) );
+				group.add( controller1 );
+
+				const controller2 = renderer.xr.getController( 1 );
+				controller2.addEventListener( 'selectstart', onSelectStart );
+				controller2.addEventListener( 'selectend', onSelectEnd );
+				controller2.add( new THREE.Line( geometry ) );
+				group.add( controller2 );
+
+				//
+
+				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 );
+
+			}
+
+			editor.sceneHelpers.add( group );
+
+			renderer.xr.enabled = true;
+			renderer.xr.addEventListener( 'sessionend', onSessionEnded );
+
+			await renderer.xr.setSession( session );
+
+		};
+
+		const onSessionEnded = async () => {
+
+			editor.sceneHelpers.remove( group );
+
+			const sidebar = document.getElementById( 'sidebar' );
+			sidebar.style.width = '';
+			sidebar.style.height = '';
+
+			renderer.xr.removeEventListener( 'sessionend', onSessionEnded );
+			renderer.xr.enabled = false;
+
+			editor.camera.copy( camera );
+
+			signals.windowResize.dispatch();
+
+		};
+
+		// signals
+
+		signals.enterXR.add( ( mode ) => {
+
+			if ( 'xr' in navigator ) {
+
+				const sessionInit = { optionalFeatures: [ 'local-floor' ] };
+				navigator.xr.requestSession( mode, sessionInit ).then( onSessionStarted );
+
+			}
+
+		} );
+
+		signals.rendererCreated.add( ( value ) => {
+
+			renderer = value;
+
+		} );
+
+	}
+
+}
+
+export { XR };

+ 12 - 27
editor/js/Viewport.js

@@ -10,7 +10,7 @@ import { ViewportControls } from './Viewport.Controls.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 { XR } from './Viewport.XR.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';
@@ -55,7 +55,7 @@ function Viewport( editor ) {
 	grid.add( grid2 );
 	grid.add( grid2 );
 
 
 	const viewHelper = new ViewHelper( camera, container );
 	const viewHelper = new ViewHelper( camera, container );
-	const vr = new VR( editor );
+	const xr = new XR( editor );
 
 
 	//
 	//
 
 
@@ -72,27 +72,14 @@ function Viewport( editor ) {
 	let objectScaleOnDown = null;
 	let objectScaleOnDown = null;
 
 
 	const transformControls = new TransformControls( camera, container.dom );
 	const transformControls = new TransformControls( camera, container.dom );
-	transformControls.addEventListener( 'change', function () {
+	transformControls.addEventListener( 'axis-changed', function () {
 
 
-		const object = transformControls.object;
-
-		if ( object !== undefined ) {
-
-			box.setFromObject( object, true );
-
-			const helper = editor.helpers[ object.id ];
-
-			if ( helper !== undefined && helper.isSkeletonHelper !== true ) {
-
-				helper.update();
+		if ( editor.viewportShading !== 'realistic' ) render();
 
 
-			}
-
-			signals.refreshSidebarObject3D.dispatch( object );
-
-		}
+	} );
+	transformControls.addEventListener( 'objectChange', function () {
 
 
-		render( true );
+		signals.objectChanged.dispatch( transformControls.object );
 
 
 	} );
 	} );
 	transformControls.addEventListener( 'mouseDown', function () {
 	transformControls.addEventListener( 'mouseDown', function () {
@@ -671,8 +658,6 @@ function Viewport( editor ) {
 
 
 	} );
 	} );
 
 
-	signals.exitedVR.add( render );
-
 	//
 	//
 
 
 	signals.windowResize.add( function () {
 	signals.windowResize.add( function () {
@@ -699,7 +684,7 @@ function Viewport( editor ) {
 		sceneHelpers.visible = value;
 		sceneHelpers.visible = value;
 		transformControls.enabled = value;
 		transformControls.enabled = value;
 
 
-		render( true );
+		render();
 
 
 	} );
 	} );
 
 
@@ -747,7 +732,7 @@ function Viewport( editor ) {
 
 
 		}
 		}
 
 
-		if ( vr.currentSession !== null ) {
+		if ( renderer.xr.isPresenting === true ) {
 
 
 			needsUpdate = true;
 			needsUpdate = true;
 
 
@@ -768,9 +753,9 @@ function Viewport( editor ) {
 	let startTime = 0;
 	let startTime = 0;
 	let endTime = 0;
 	let endTime = 0;
 
 
-	function render( isHelper = false ) {
+	function render() {
 
 
-		if ( editor.viewportShading === 'realistic' && isHelper === false ) {
+		if ( editor.viewportShading === 'realistic' ) {
 
 
 			pathtracer.init( scene, camera );
 			pathtracer.init( scene, camera );
 
 
@@ -786,7 +771,7 @@ function Viewport( editor ) {
 			renderer.autoClear = false;
 			renderer.autoClear = false;
 			if ( grid.visible === true ) renderer.render( grid, camera );
 			if ( grid.visible === true ) renderer.render( grid, camera );
 			if ( sceneHelpers.visible === true ) renderer.render( sceneHelpers, camera );
 			if ( sceneHelpers.visible === true ) renderer.render( sceneHelpers, camera );
-			if ( vr.currentSession === null ) viewHelper.render( renderer );
+			if ( renderer.xr.isPresenting !== true ) viewHelper.render( renderer );
 			renderer.autoClear = true;
 			renderer.autoClear = true;
 
 
 		}
 		}

+ 1 - 1
editor/sw.js

@@ -191,7 +191,7 @@ const assets = [
 	'./js/Viewport.Info.js',
 	'./js/Viewport.Info.js',
 	'./js/Viewport.Selector.js',
 	'./js/Viewport.Selector.js',
 	'./js/Viewport.ViewHelper.js',
 	'./js/Viewport.ViewHelper.js',
-	'./js/Viewport.VR.js',
+	'./js/Viewport.XR.js',
 
 
 	'./js/Command.js',
 	'./js/Command.js',
 	'./js/commands/AddObjectCommand.js',
 	'./js/commands/AddObjectCommand.js',