@shopware-ag/dive
Version:
Shopware Spatial Framework
193 lines (153 loc) • 5.41 kB
text/typescript
import { Matrix4, Quaternion, Vector3 } from 'three';
import { DIVERenderer } from '../../../renderer/Renderer';
export class DIVEWebXROrigin {
private _renderer: DIVERenderer;
private _session: XRSession;
private _requesting: boolean;
private _initialized: boolean;
private _referenceSpaceBuffer: XRReferenceSpace | null;
private _hitTestSource: XRHitTestSource | null;
private _entityTypes: XRHitTestTrackableType[];
private _hitTestResultBuffer: XRHitTestResult[];
private _raycastHitCounter: number = 0;
private _originSet: Promise<void>;
private _originSetResolve: (value: void) => void = () => {};
public get originSet(): Promise<void> {
return this._originSet;
}
private _matrix: Matrix4;
public get matrix(): Matrix4 {
return this._matrix;
}
private set matrix(value: Matrix4) {
this._matrix = value;
this._matrix.decompose(this._position, this._quaternion, this._scale);
}
private _position: Vector3;
public get position(): Vector3 {
return this._position;
}
private _quaternion: Quaternion;
public get quaternion(): Quaternion {
return this._quaternion;
}
private _scale: Vector3;
public get scale(): Vector3 {
return this._scale;
}
constructor(
session: XRSession,
renderer: DIVERenderer,
entityTypes?: XRHitTestTrackableType[],
) {
this._renderer = renderer;
this._session = session;
this._originSet = new Promise<void>((resolve) => {
this._originSetResolve = resolve;
});
this._requesting = false;
this._initialized = false;
this._referenceSpaceBuffer = null;
this._hitTestSource = null;
this._entityTypes = entityTypes || ['plane'];
this._hitTestResultBuffer = [];
// set up promises and executors
this._matrix = new Matrix4();
this._position = new Vector3();
this._quaternion = new Quaternion();
this._scale = new Vector3();
// when origin is set, decompose matrix into position, quaternion and scale
this._originSet.then(() => {
// decompose matrix into position, quaternion and scale
this._matrix.decompose(
this._position,
this._quaternion,
this._scale,
);
});
}
public async Init(): Promise<this> {
if (this._initialized) {
return Promise.resolve(this);
}
if (!this._session) {
console.error(
'DIVEWebXROrigin: No session set in Init()! Aborting initialization...',
);
return Promise.reject();
}
if (this._requesting) {
console.error(
'DIVEWebXROrigin: Currently initializing! Aborting initialization...',
);
return Promise.reject();
}
this._requesting = true;
const referenceSpace =
await this._session.requestReferenceSpace('viewer');
this._hitTestSource =
(await this._session.requestHitTestSource!({
space: referenceSpace,
entityTypes: this._entityTypes,
})) || null;
this._requesting = false;
if (!this._hitTestSource) {
return Promise.reject();
}
this._initialized = true;
return Promise.resolve(this);
}
public Dispose(): void {
this._initialized = false;
this._requesting = false;
this._hitTestSource?.cancel();
this._hitTestSource = null;
this._hitTestResultBuffer = [];
this._matrix = new Matrix4();
this._position = new Vector3();
this._quaternion = new Quaternion();
this._scale = new Vector3();
}
public Update(frame: XRFrame): void {
if (!this._initialized) return;
if (!this._hitTestSource) {
throw new Error(
'DIVEWebXRRaycaster: Critical Error: HitTestSource not available but WebXROrigin is initialized!',
);
}
// get hit test results
this._hitTestResultBuffer = frame.getHitTestResults(
this._hitTestSource,
);
if (this._hitTestResultBuffer.length > 0) {
// hit found
this._referenceSpaceBuffer = this._renderer.xr.getReferenceSpace();
// if there is no reference space, hit will be counted as lost for this frame
if (!this._referenceSpaceBuffer) {
this.onHitLost();
return;
}
const pose = this._hitTestResultBuffer[0].getPose(
this._referenceSpaceBuffer,
);
if (!pose) {
this.onHitLost();
return;
}
this.onHitFound(pose);
} else {
this.onHitLost();
}
}
private onHitFound(pose: XRPose): void {
this._raycastHitCounter++;
this.matrix.fromArray(pose.transform.matrix);
// we have to wait for a certain amount of frames to make sure the origin is set
if (this._raycastHitCounter > 50) {
this._originSetResolve();
}
}
private onHitLost(): void {
this._raycastHitCounter = 0;
}
}