@pmndrs/xr
Version:
VR/AR for threejs
83 lines (82 loc) • 3.08 kB
JavaScript
import { Matrix4, Quaternion, Vector3 } from 'three';
import { getSpaceFromAncestors } from './internals.js';
import { toDOMPointInit } from './utils.js';
const matrixHelper = new Matrix4();
const vectorHelper = new Vector3();
const scaleHelper = new Vector3();
const quaternionHelper = new Quaternion();
export async function createXRHitTestSource(store, session, relativeTo, trackableType = ['point', 'plane', 'mesh']) {
if (typeof relativeTo === 'string') {
relativeTo = await session.requestReferenceSpace(relativeTo);
}
const entityTypes = Array.isArray(trackableType) ? trackableType : [trackableType];
//necassary data for request and compute hit test results
let options;
let baseSpace;
let object;
const state = store.getState();
if (relativeTo instanceof XRSpace) {
//configure for request and compute hit test results
options = { space: relativeTo, entityTypes };
object = state.origin;
}
else {
//compute space
const space = getSpaceFromAncestors(relativeTo, state.origin, state.originReferenceSpace, matrixHelper);
if (space == null) {
return undefined;
}
//compute offset ray
matrixHelper.decompose(vectorHelper, quaternionHelper, scaleHelper);
const point = toDOMPointInit(vectorHelper);
vectorHelper.set(0, 0, -1).applyQuaternion(quaternionHelper);
const offsetRay = new XRRay(point, toDOMPointInit(vectorHelper, 0));
//configure for request and compute hit test results
object = relativeTo;
options = { space, offsetRay, entityTypes };
baseSpace = space;
}
const source = await session?.requestHitTestSource?.(options);
if (source == null) {
return undefined;
}
return {
source,
getWorldMatrix: computeWorldMatrixFromXRHitTestResult.bind(null, store, baseSpace, object),
};
}
export async function requestXRHitTest(store, relativeTo, trackableType) {
const session = store.getState().session;
if (session == null) {
return;
}
const sourceData = await createXRHitTestSource(store, session, relativeTo, trackableType);
if (sourceData == null) {
return undefined;
}
const { source, getWorldMatrix } = sourceData;
const frame = await store.requestFrame();
const results = frame.getHitTestResults?.(source) ?? [];
source.cancel();
if (results == null) {
return undefined;
}
return { results, getWorldMatrix };
}
function computeWorldMatrixFromXRHitTestResult(store, baseSpace, object, target, result) {
baseSpace ??= store.getState().originReferenceSpace;
if (baseSpace == null) {
return false;
}
const pose = result.getPose(baseSpace);
if (pose == null) {
return false;
}
//target = ObjectMatrixWorld? * HitTestMatrix
target.fromArray(pose.transform.matrix);
if (object != null) {
object.updateWorldMatrix(true, false);
target.premultiply(object.matrixWorld);
}
return true;
}