@threlte/xr
Version:
Tools to more easily create VR and AR experiences with Threlte
81 lines (80 loc) • 3.3 kB
JavaScript
import { Raycaster, Vector3 } from 'three';
import { currentWritable, observe } from '@threlte/core';
import { defaultComputeFunction } from './compute.js';
import { injectPointerControlsPlugin } from './plugin.svelte.js';
import { setupPointerControls } from './setup.svelte.js';
import { getControlsContext, getHandContext, setControlsContext, setHandContext, setInternalContext } from './context.js';
import { pointerIntersection, pointerState } from '../../internal/state.svelte.js';
const aggregateStates = new Map();
export const pointerControls = (handedness, options) => {
if (getControlsContext() === undefined) {
injectPointerControlsPlugin();
setInternalContext();
setControlsContext({ interactiveObjects: [] });
}
const context = getControlsContext();
const aggregateState = aggregateStates.get(handedness) ??
(() => {
const state = {
enabled: currentWritable(options?.enabled ?? true),
hovered: new Map()
};
aggregateStates.set(handedness, state);
return state;
})();
const { enabled, hovered } = aggregateState;
let controllerContext = getHandContext(handedness, 'controller');
let handContext = getHandContext(handedness, 'hand');
const syncSharedState = () => {
hovered.clear();
for (const [id, event] of controllerContext.hovered) {
hovered.set(`controller:${id}`, event);
}
for (const [id, event] of handContext.hovered) {
hovered.set(`hand:${id}`, event);
}
// Shared handedness-level pointer visuals are currently controller-only:
// <Controller /> renders the cursor/ray from these globals, while hand
// pointer events are dispatched independently without a matching visual.
pointerState[handedness].hovering = controllerContext.pointerOverTarget.current;
pointerIntersection[handedness] = controllerContext.currentIntersection;
};
const createContext = (sourceType) => ({
hand: handedness,
sourceType,
enabled,
pointer: currentWritable(new Vector3()),
pointerOverTarget: currentWritable(false),
lastEvent: undefined,
initialClick: [0, 0, 0],
initialHits: [],
hovered: new Map(),
currentIntersection: undefined,
raycaster: new Raycaster(),
syncSharedState,
compute: options?.compute ?? defaultComputeFunction,
filter: options?.filter
});
const setupContexts = [];
if (controllerContext === undefined) {
controllerContext = createContext('controller');
setHandContext(handedness, 'controller', controllerContext);
setupContexts.push(controllerContext);
}
if (handContext === undefined) {
handContext = createContext('hand');
setHandContext(handedness, 'hand', handContext);
setupContexts.push(handContext);
}
for (const setupContext of setupContexts) {
setupPointerControls(context, setupContext, options?.fixedStep);
}
observe.pre(() => [enabled], ([nextEnabled]) => {
pointerState[handedness].enabled = nextEnabled;
});
syncSharedState();
return {
enabled,
hovered
};
};