forked from EXT/VR180-Web-Player
This commit is contained in:
125
src/vr180player/xr/hand-aim.ts
Normal file
125
src/vr180player/xr/hand-aim.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
export type VectorLike = {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
|
||||
export type PalmAimInput = {
|
||||
handedness?: string | null;
|
||||
indexMetacarpal?: VectorLike | null;
|
||||
middleMetacarpal?: VectorLike | null;
|
||||
pinkyMetacarpal?: VectorLike | null;
|
||||
ringMetacarpal?: VectorLike | null;
|
||||
wrist?: VectorLike | null;
|
||||
};
|
||||
|
||||
export type PalmAimRay = {
|
||||
direction: VectorLike;
|
||||
origin: VectorLike;
|
||||
};
|
||||
|
||||
const MIN_AXIS_LENGTH_SQ = 0.000001;
|
||||
const PALM_SURFACE_OFFSET_METERS = 0.035;
|
||||
|
||||
export function computePalmAimRay(input: PalmAimInput): PalmAimRay | null {
|
||||
const { indexMetacarpal, pinkyMetacarpal, wrist } = input;
|
||||
if (!indexMetacarpal || !pinkyMetacarpal || !wrist) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const knuckleCenter = averageVectors([
|
||||
indexMetacarpal,
|
||||
input.middleMetacarpal,
|
||||
input.ringMetacarpal,
|
||||
pinkyMetacarpal
|
||||
]);
|
||||
if (!knuckleCenter) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fingerAxis = normalize(subtract(knuckleCenter, wrist));
|
||||
const acrossPalmAxis = normalize(subtract(pinkyMetacarpal, indexMetacarpal));
|
||||
if (!fingerAxis || !acrossPalmAxis) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const direction = normalize(getPalmNormal(fingerAxis, acrossPalmAxis, input.handedness));
|
||||
if (!direction) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const palmCenter = lerp(wrist, knuckleCenter, 0.62);
|
||||
const origin = add(palmCenter, scale(direction, PALM_SURFACE_OFFSET_METERS));
|
||||
return { direction, origin };
|
||||
}
|
||||
|
||||
function getPalmNormal(fingerAxis: VectorLike, acrossPalmAxis: VectorLike, handedness?: string | null): VectorLike {
|
||||
if (handedness === 'left') {
|
||||
return cross(acrossPalmAxis, fingerAxis);
|
||||
}
|
||||
|
||||
return cross(fingerAxis, acrossPalmAxis);
|
||||
}
|
||||
|
||||
function averageVectors(vectors: Array<VectorLike | null | undefined>): VectorLike | null {
|
||||
const usableVectors = vectors.filter(Boolean) as VectorLike[];
|
||||
if (usableVectors.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const total = usableVectors.reduce(
|
||||
(sum, vector) => add(sum, vector),
|
||||
{ x: 0, y: 0, z: 0 }
|
||||
);
|
||||
return scale(total, 1 / usableVectors.length);
|
||||
}
|
||||
|
||||
function normalize(vector: VectorLike): VectorLike | null {
|
||||
const lengthSq = vector.x * vector.x + vector.y * vector.y + vector.z * vector.z;
|
||||
if (lengthSq < MIN_AXIS_LENGTH_SQ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const length = Math.sqrt(lengthSq);
|
||||
return scale(vector, 1 / length);
|
||||
}
|
||||
|
||||
function add(a: VectorLike, b: VectorLike): VectorLike {
|
||||
return {
|
||||
x: a.x + b.x,
|
||||
y: a.y + b.y,
|
||||
z: a.z + b.z
|
||||
};
|
||||
}
|
||||
|
||||
function subtract(a: VectorLike, b: VectorLike): VectorLike {
|
||||
return {
|
||||
x: a.x - b.x,
|
||||
y: a.y - b.y,
|
||||
z: a.z - b.z
|
||||
};
|
||||
}
|
||||
|
||||
function scale(vector: VectorLike, scalar: number): VectorLike {
|
||||
return {
|
||||
x: vector.x * scalar,
|
||||
y: vector.y * scalar,
|
||||
z: vector.z * scalar
|
||||
};
|
||||
}
|
||||
|
||||
function lerp(a: VectorLike, b: VectorLike, amount: number): VectorLike {
|
||||
return {
|
||||
x: a.x + (b.x - a.x) * amount,
|
||||
y: a.y + (b.y - a.y) * amount,
|
||||
z: a.z + (b.z - a.z) * amount
|
||||
};
|
||||
}
|
||||
|
||||
function cross(a: VectorLike, b: VectorLike): VectorLike {
|
||||
return {
|
||||
x: a.y * b.z - a.z * b.y,
|
||||
y: a.z * b.x - a.x * b.z,
|
||||
z: a.x * b.y - a.y * b.x
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user