text: /**
   * @author mrdoob / http://mrdoob.com/
   */
  
  import { Group } from '../../objects/Group.js';
  import { Vector4 } from '../../math/Vector4.js';
  import { ArrayCamera } from '../../cameras/ArrayCamera.js';
  import { PerspectiveCamera } from '../../cameras/PerspectiveCamera.js';
  import { WebGLAnimation } from '../webgl/WebGLAnimation.js';
  import { setProjectionFromUnion } from './WebVRUtils.js';
  
  function WebXRManager( renderer ) {
  
  	var gl = renderer.context;
  
  	var device = null;
  	var session = null;
  
  	var framebufferScaleFactor = 1.0;
  
  	var frameOfReference = null;
  	var frameOfReferenceType = 'stage';
  
  	var pose = null;
  
  	var controllers = [];
  	var inputSources = [];
  
  	function isPresenting() {
  
  		return session !== null && frameOfReference !== null;
  
  	}
  
  	//
  
  	var cameraL = new PerspectiveCamera();
  	cameraL.layers.enable( 1 );
  	cameraL.viewport = new Vector4();
  
  	var cameraR = new PerspectiveCamera();
  	cameraR.layers.enable( 2 );
  	cameraR.viewport = new Vector4();
  
  	var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );
  	cameraVR.layers.enable( 1 );
  	cameraVR.layers.enable( 2 );
  
  	//
  
  	this.enabled = false;
  
  	this.getController = function ( id ) {
  
  		var controller = controllers[ id ];
  
  		if ( controller === undefined ) {
  
  			controller = new Group();
  			controller.matrixAutoUpdate = false;
  			controller.visible = false;
  
  			controllers[ id ] = controller;
  
  		}
  
  		return controller;
  
  	};
  
  	this.getDevice = function () {
  
  		return device;
  
  	};
  
  	this.setDevice = function ( value ) {
  
  		if ( value !== undefined ) device = value;
  		if ( value instanceof XRDevice ) gl.setCompatibleXRDevice( value );
  
  	};
  
  	//
  
  	function onSessionEvent( event ) {
  
  		var controller = controllers[ inputSources.indexOf( event.inputSource ) ];
  		if ( controller ) controller.dispatchEvent( { type: event.type } );
  
  	}
  
  	function onSessionEnd() {
  
  		renderer.setFramebuffer( null );
  		animation.stop();
  
  	}
  
  	this.setFramebufferScaleFactor = function ( value ) {
  
  		framebufferScaleFactor = value;
  
  	};
  
  	this.setFrameOfReferenceType = function ( value ) {
  
  		frameOfReferenceType = value;
  
  	};
  
  	this.setSession = function ( value ) {
  
  		session = value;
  
  		if ( session !== null ) {
  
  			session.addEventListener( 'select', onSessionEvent );
  			session.addEventListener( 'selectstart', onSessionEvent );
  			session.addEventListener( 'selectend', onSessionEvent );
  			session.addEventListener( 'end', onSessionEnd );
  
  			session.baseLayer = new XRWebGLLayer( session, gl, { framebufferScaleFactor: framebufferScaleFactor } );
  			session.requestFrameOfReference( frameOfReferenceType ).then( function ( value ) {
  
  				frameOfReference = value;
  
  				renderer.setFramebuffer( session.baseLayer.framebuffer );
  
  				animation.setContext( session );
  				animation.start();
  
  			} );
  
  			//
  
  			inputSources = session.getInputSources();
  
  			session.addEventListener( 'inputsourceschange', function () {
  
  				inputSources = session.getInputSources();
  				console.log( inputSources );
  
  				for ( var i = 0; i < controllers.length; i ++ ) {
  
  					var controller = controllers[ i ];
  					controller.userData.inputSource = inputSources[ i ];
  
  				}
  
  			} );
  
  		}
  
  	};
  
  	function updateCamera( camera, parent ) {
  
  		if ( parent === null ) {
  
  			camera.matrixWorld.copy( camera.matrix );
  
  		} else {
  
  			camera.matrixWorld.multiplyMatrices( parent.matrixWorld, camera.matrix );
  
  		}
  
  		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
  
  	}
  
  	this.getCamera = function ( camera ) {
  
  		if ( isPresenting() ) {
  
  			var parent = camera.parent;
  			var cameras = cameraVR.cameras;
  
  			updateCamera( cameraVR, parent );
  
  			for ( var i = 0; i < cameras.length; i ++ ) {
  
  				updateCamera( cameras[ i ], parent );
  
  			}
  
  			// update camera and its children
  
  			camera.matrixWorld.copy( cameraVR.matrixWorld );
  
  			var children = camera.children;
  
  			for ( var i = 0, l = children.length; i < l; i ++ ) {
  
  				children[ i ].updateMatrixWorld( true );
  
  			}
  
  			setProjectionFromUnion( cameraVR, cameraL, cameraR );
  
  			return cameraVR;
  
  		}
  
  		return camera;
  
  	};
  
  	this.isPresenting = isPresenting;
  
  	// Animation Loop
  
  	var onAnimationFrameCallback = null;
  
  	function onAnimationFrame( time, frame ) {
  
  		pose = frame.getDevicePose( frameOfReference );
  
  		if ( pose !== null ) {
  
  			var layer = session.baseLayer;
  			var views = frame.views;
  
  			for ( var i = 0; i < views.length; i ++ ) {
  
  				var view = views[ i ];
  				var viewport = layer.getViewport( view );
  				var viewMatrix = pose.getViewMatrix( view );
  
  				var camera = cameraVR.cameras[ i ];
  				camera.matrix.fromArray( viewMatrix ).getInverse( camera.matrix );
  				camera.projectionMatrix.fromArray( view.projectionMatrix );
  				camera.viewport.set( viewport.x, viewport.y, viewport.width, viewport.height );
  
  				if ( i === 0 ) {
  
  					cameraVR.matrix.copy( camera.matrix );
  
  				}
  
  			}
  
  		}
  
  		//
  
  		for ( var i = 0; i < controllers.length; i ++ ) {
  
  			var controller = controllers[ i ];
  
  			var inputSource = inputSources[ i ];
  
  			if ( inputSource ) {
  
  				var inputPose = frame.getInputPose( inputSource, frameOfReference );
  
  				if ( inputPose !== null ) {
  
  					if ( 'targetRay' in inputPose ) {
  
  						controller.matrix.elements = inputPose.targetRay.transformMatrix;
  
  					} else if ( 'pointerMatrix' in inputPose ) {
  
  						// DEPRECATED
  
  						controller.matrix.elements = inputPose.pointerMatrix;
  
  					}
  
  					controller.matrix.decompose( controller.position, controller.rotation, controller.scale );
  					controller.visible = true;
  
  					continue;
  
  				}
  
  			}
  
  			controller.visible = false;
  
  		}
  
  		if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );
  
  	}
  
  	var animation = new WebGLAnimation();
  	animation.setAnimationLoop( onAnimationFrame );
  
  	this.setAnimationLoop = function ( callback ) {
  
  		onAnimationFrameCallback = callback;
  
  	};
  
  	this.dispose = function () {};
  
  	// DEPRECATED
  
  	this.getStandingMatrix = function () {
  
  		console.warn( 'THREE.WebXRManager: getStandingMatrix() is no longer needed.' );
  		return new THREE.Matrix4();
  
  	};
  
  	this.submitFrame = function () {};
  
  }
  
  export { WebXRManager };
  