@pmndrs/xr
Version:
VR/AR for threejs
96 lines (95 loc) • 4.4 kB
JavaScript
import { XRControllerLayoutLoader, createXRControllerState, } from './controller/index.js';
import { createXRHandState, } from './hand/index.js';
export function isXRInputSourceState(val) {
return val != null && typeof val === 'object' && 'inputSource' in val;
}
function setupEvents(session, events) {
const listener = (e) => events.push(e);
session.addEventListener('selectstart', listener);
session.addEventListener('selectend', listener);
session.addEventListener('select', listener);
session.addEventListener('squeeze', listener);
session.addEventListener('squeezestart', listener);
session.addEventListener('squeezeend', listener);
return () => {
session.removeEventListener('selectstart', listener);
session.removeEventListener('selectend', listener);
session.removeEventListener('select', listener);
session.removeEventListener('squeeze', listener);
session.removeEventListener('squeezestart', listener);
session.removeEventListener('squeezeend', listener);
};
}
let idCounter = 0;
export function createSyncXRInputSourceStates(addController, options) {
const cleanupMap = new Map();
const controllerLayoutLoader = new XRControllerLayoutLoader(options);
const idMap = new Map();
return (session, current, changes) => {
if (changes === 'remove-all') {
for (const cleanup of cleanupMap.values()) {
cleanup();
}
return current;
}
const target = [...current];
for (const { added, isPrimary, removed } of changes) {
if (removed != null) {
for (const inputSource of removed) {
const index = target.findIndex(({ inputSource: is, isPrimary: ip }) => ip === isPrimary && is === inputSource);
if (index === -1) {
continue;
}
target.splice(index, 1);
cleanupMap.get(inputSource)?.();
cleanupMap.delete(inputSource);
}
}
if (added == null) {
continue;
}
for (const inputSource of added) {
const events = [];
let cleanup = setupEvents(session, events);
const key = `${inputSource.handedness}-${inputSource.hand ? 'hand' : 'nohand'}-${inputSource.targetRayMode}-${inputSource.profiles.join(',')}`;
let id;
if ((id = idMap.get(key)) == null) {
idMap.set(key, (id = `${idCounter++}`));
}
if (inputSource.hand != null) {
target.push(createXRHandState(id, inputSource, options, events, isPrimary));
}
else {
switch (inputSource.targetRayMode) {
case 'gaze':
target.push({ id, isPrimary, type: 'gaze', inputSource, events });
break;
case 'screen':
target.push({ id, isPrimary, type: 'screenInput', inputSource, events });
break;
case 'transient-pointer':
target.push({ id, isPrimary, type: 'transientPointer', inputSource, events });
break;
case 'tracked-pointer':
let aborted = false;
const cleanupEvents = cleanup;
cleanup = () => {
cleanupEvents();
aborted = true;
};
const stateResult = createXRControllerState(id, inputSource, controllerLayoutLoader, events, isPrimary);
if (stateResult instanceof Promise) {
stateResult.then((state) => !aborted && addController(state)).catch(console.error);
}
else {
target.push(stateResult);
}
break;
}
}
cleanupMap.set(inputSource, cleanup);
}
}
return target;
};
}