UNPKG

@shopware-ag/dive

Version:

Shopware Spatial Framework

177 lines (144 loc) 5.36 kB
import { Vector3 } from 'three'; import DIVEOrbitControls from '../../controls/OrbitControls'; import { type DIVERenderer } from '../../renderer/Renderer'; import { type DIVEScene } from '../../scene/Scene'; import { Overlay } from './overlay/Overlay'; import { DIVEWebXRController } from './controller/WebXRController'; export class DIVEWebXR { // general members private static _renderer: DIVERenderer; private static _scene: DIVEScene; private static _controller: DIVEOrbitControls; // camera reset members private static _cameraPosition: Vector3; private static _cameraTarget: Vector3; // render loop members private static _renderCallbackId: string | null = null; // setup members private static _session: XRSession | null = null; private static _referenceSpaceType: XRReferenceSpaceType = 'local'; private static _overlay: Overlay | null = null; private static _options = { requiredFeatures: [ 'local', 'hit-test', ], optionalFeatures: [ 'light-estimation', 'local-floor', 'dom-overlay', 'depth-sensing', ], depthSensing: { usagePreference: ['gpu-optimized'], dataFormatPreference: [], }, domOverlay: { root: {} as HTMLElement }, }; private static _xrController: DIVEWebXRController | null = null; public static async Launch( renderer: DIVERenderer, scene: DIVEScene, controller: DIVEOrbitControls, ): Promise<void> { this._renderer = renderer; this._scene = scene; this._controller = controller; // setting camera reset values this._cameraPosition = this._controller.object.position.clone(); this._cameraTarget = this._controller.target.clone(); if (!navigator.xr) { console.error('WebXR not supported'); return Promise.reject(); } // setup current instance this._renderer.xr.enabled = true; this._scene.InitXR(renderer); // creating overlay if (!DIVEWebXR._overlay) { const overlay = new Overlay(); DIVEWebXR._overlay = overlay; } DIVEWebXR._options.domOverlay = { root: DIVEWebXR._overlay.Element }; // request session const session = await navigator.xr.requestSession( 'immersive-ar', this._options, ); session.addEventListener('end', () => { this._onSessionEnded(); }); // build up session renderer.xr.setReferenceSpaceType(this._referenceSpaceType); await renderer.xr.setSession(session); DIVEWebXR._overlay.Element.style.display = ''; this._session = session; // add end session event listener DIVEWebXR._overlay.CloseButton.addEventListener('click', () => this.End(), ); // start session await this._onSessionStarted(); return Promise.resolve(); } public static Update(_time: DOMHighResTimeStamp, frame: XRFrame): void { if (!this._session) return; if (this._xrController) { this._xrController.Update(frame); } } public static End(): void { if (!this._session) return; this._session.end(); } private static async _onSessionStarted(): Promise<void> { if (!this._session) return; // add update callback to render loop this._renderCallbackId = this._renderer.AddPreRenderCallback( (time: DOMHighResTimeStamp, frame: XRFrame) => { this.Update(time, frame); }, ); this._xrController = new DIVEWebXRController( this._session, this._renderer, this._scene, ); await this._xrController.Init().catch(() => { this.End(); }); return Promise.resolve(); } private static _onSessionEnded(): void { if (!this._session) return; if (this._xrController) { this._xrController.Dispose(); } // remove Update() callback if (this._renderCallbackId) { this._renderer.RemovePreRenderCallback(this._renderCallbackId); this._renderCallbackId = null; } // disable XR on renderer to restore canvas rendering this._renderer.xr.enabled = false; // resize renderer const canvasWrapper = this._renderer.domElement.parentElement; if (canvasWrapper) { const { clientWidth, clientHeight } = canvasWrapper; this._renderer.OnResize(clientWidth, clientHeight); // resize camera this._controller.object.OnResize(clientWidth, clientHeight); } // reset camera this._controller.object.position.copy(this._cameraPosition); this._controller.target.copy(this._cameraTarget); // reset camera values this._cameraPosition.set(0, 0, 0); this._cameraTarget.set(0, 0, 0); // dispose xr scene this._scene.DisposeXR(); this._session.removeEventListener('end', this._onSessionEnded); DIVEWebXR._overlay!.Element.style.display = 'none'; this._session = null; } }