<!-- NEW3 — TEST 3 · THE RIVER'S JOURNEY · part 407 -->
pointing
		 * tasks like UI interaction.
		 *
		 * @param {number} index - The index of the controller.
		 * @return {Group} A group representing the `target ray` space.
		 */
		this.getController = function ( index ) {

			let controller = controllers[ index ];

			if ( controller === undefined ) {

				controller = new WebXRController();
				controllers[ index ] = controller;

			}

			return controller.getTargetRaySpace();

		};

		/**
		 * Returns a group representing the `grip` space of the XR controller.
		 * Use this space for visualizing 3D objects that support the user in pointing
		 * tasks like UI interaction.
		 *
		 * Note: If you want to show something in the user's hand AND offer a
		 * pointing ray at the same time, you'll want to attached the handheld object
		 * to the group returned by `getControllerGrip()` and the ray to the
		 * group returned by `getController()`. The idea is to have two
		 * different groups in two different coordinate spaces for the same WebXR
		 * controller.
		 *
		 * @param {number} index - The index of the controller.
		 * @return {Group} A group representing the `grip` space.
		 */
		this.getControllerGrip = function ( index ) {

			let controller = controllers[ index ];

			if ( controller === undefined ) {

				controller = new WebXRController();
				controllers[ index ] = controller;

			}

			return controller.getGripSpace();

		};

		/**
		 * Returns a group representing the `hand` space of the XR controller.
		 * Use this space for visualizing 3D objects that support the user in pointing
		 * tasks like UI interaction.
		 *
		 * @param {number} index - The index of the controller.
		 * @return {Group} A group representing the `hand` space.
		 */
		this.getHand = function ( index ) {

			let controller = controllers[ index ];

			if ( controller === undefined ) {

				controller = new WebXRController();
				controllers[ index ] = controller;

			}

			return controller.getHandSpace();

		};

		//

		function onSessionEvent( event ) {

			const controllerIndex = controllerInputSources.indexOf( event.inputSource );

			if ( controllerIndex === -1 ) {

				return;

			}

			const controller = controllers[ controllerIndex ];

			if ( controller !== undefined ) {

				controller.update( event.inputSource, event.frame, customReferenceSpace || referenceSpace );
				controller.dispatchEvent( { type: event.type, data: event.inputSource } );

			}

		}

		function onSessionEnd() {

			session.removeEventListener( 'select', onSessionEvent );
			session.removeEventListener( 'selectstart', onSessionEvent );
			session.removeEventListener( 'selectend', onSessionEvent );
			session.removeEventListener( 'squeeze', onSessionEvent );
			session.removeEventListener( 'squeezestart', onSessionEvent );
			session.removeEventListener( 'squeezeend', onSessionEvent );
			session.removeEventListener( 'end', onSessionEnd );
			session.removeEventListener( 'inputsourceschange', onInputSourcesChange );

			for ( let i = 0; i < controllers.length; i ++ ) {

				const inputSource = controllerInputSources[ i ];

				if ( inputSource === null ) continue;

				controllerInputSources[ i ] = null;

				controllers[ i ].disconnect( inputSource );

			}

			_currentDepthNear = null;
			_currentDepthFar = null;

			depthSensing.reset();
			for ( const key in cameraAccessTextures ) {

				delete cameraAccessTextures[ key ];

			}

			// restore framebuffer/rendering state

			renderer.setRenderTarget( initialRenderTarget );

			glBaseLayer = null;
			glProjLayer = null;
			glBinding = null;
			session = null;
			newRenderTarget = null;

			//

			animation.stop();

			scope.isPresenting = false;

			renderer.setPixelRatio( currentPixelRatio );
			renderer.setSize( currentSize.width, currentSize.height, false );

			if ( currentCameraSettings !== null ) {

				const camera = currentCameraSettings.camera;

				camera.fov = currentCameraSettings.fov;
				camera.zoom = currentCameraSettings.zoom;
				camera.updateProjectionMatrix();

				currentCameraSettings = null;

			}

			scope.dispatchEvent( { type: 'sessionend' } );

		}

		/**
		 * Sets the framebuffer scale factor.
		 *
		 * This method can not be used during a XR session.
		 *
		 * @param {number} value - The framebuffer scale factor.
		 */
		this.setFramebufferScaleFactor = function ( value ) {

			framebufferScaleFactor = value;

			if ( scope.isPresenting === true ) {

				warn( 'WebXRManager: Cannot change framebuffer scale while presenting.' );

			}

		};

		/**
		 * Sets the reference space type. Can be used to configure a spatial relationship with the user's physical
		 * environment. Depending on how the user moves in 3D space, setting an appropriate reference space can
		 * improve tracking. Default is `local-floor`. Valid values can be found here
		 * https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpace#reference_space_types.
		 *
		 * This method can not be u