Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
|||||||
|
import { useRef } from 'react';
|
||||||
|
import { useFrame, useThree } from '@react-three/fiber';
|
||||||
|
import * as THREE from 'three';
|
||||||
|
import { useModelStore } from '@/stores/useModelStore';
|
||||||
|
|
||||||
|
const TRIG_ON = 0.7;
|
||||||
|
const TRIG_OFF = 0.3;
|
||||||
|
const MAX_RAY = 10; // meters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right-controller trigger driven measurement for AR.
|
||||||
|
*
|
||||||
|
* When `measureMode` is on and the user pulls the right trigger, casts a ray
|
||||||
|
* forward from the controller (target-ray space). The first mesh hit becomes
|
||||||
|
* a measurement point. Two pulls = one measurement (handled by the store).
|
||||||
|
*
|
||||||
|
* Uses hysteresis so a single trigger pull adds exactly one point.
|
||||||
|
*/
|
||||||
|
export function XRControllerMeasure() {
|
||||||
|
const { scene, gl } = useThree();
|
||||||
|
const triggered = useRef(false);
|
||||||
|
const raycaster = useRef(new THREE.Raycaster());
|
||||||
|
const tmpOrigin = useRef(new THREE.Vector3());
|
||||||
|
const tmpDir = useRef(new THREE.Vector3());
|
||||||
|
const tmpQuat = useRef(new THREE.Quaternion());
|
||||||
|
|
||||||
|
useFrame((_state, _dt, frame: XRFrame | undefined) => {
|
||||||
|
const measureMode = useModelStore.getState().measureMode;
|
||||||
|
if (!measureMode || !frame) return;
|
||||||
|
const session = frame.session;
|
||||||
|
const refSpace = gl.xr.getReferenceSpace();
|
||||||
|
if (!session || !refSpace) return;
|
||||||
|
|
||||||
|
// Find right controller
|
||||||
|
let right: XRInputSource | null = null;
|
||||||
|
for (const src of session.inputSources) {
|
||||||
|
if (src.handedness === 'right') { right = src; break; }
|
||||||
|
}
|
||||||
|
if (!right) return;
|
||||||
|
|
||||||
|
const gp = right.gamepad;
|
||||||
|
const trigBtn = gp?.buttons?.[0];
|
||||||
|
const trigVal = trigBtn ? (trigBtn.value || (trigBtn.pressed ? 1 : 0)) : 0;
|
||||||
|
|
||||||
|
// Rising edge with hysteresis
|
||||||
|
if (!triggered.current && trigVal > TRIG_ON) {
|
||||||
|
triggered.current = true;
|
||||||
|
|
||||||
|
const raySpace = right.targetRaySpace ?? right.gripSpace;
|
||||||
|
if (!raySpace) return;
|
||||||
|
const pose = frame.getPose(raySpace, refSpace);
|
||||||
|
if (!pose) return;
|
||||||
|
|
||||||
|
const m = new THREE.Matrix4().fromArray(pose.transform.matrix);
|
||||||
|
tmpOrigin.current.setFromMatrixPosition(m);
|
||||||
|
tmpQuat.current.setFromRotationMatrix(m);
|
||||||
|
// Controller aims along -Z in WebXR target-ray convention
|
||||||
|
tmpDir.current.set(0, 0, -1).applyQuaternion(tmpQuat.current).normalize();
|
||||||
|
|
||||||
|
raycaster.current.set(tmpOrigin.current, tmpDir.current);
|
||||||
|
raycaster.current.far = MAX_RAY;
|
||||||
|
|
||||||
|
const hits = raycaster.current.intersectObjects(scene.children, true);
|
||||||
|
const hit = hits.find((h) => {
|
||||||
|
const o = h.object;
|
||||||
|
if (!(o instanceof THREE.Mesh)) return false;
|
||||||
|
if (o.userData.__edgeLine) return false;
|
||||||
|
if (o.geometry instanceof THREE.SphereGeometry) return false;
|
||||||
|
if (o.geometry instanceof THREE.RingGeometry) return false;
|
||||||
|
if (o.geometry instanceof THREE.PlaneGeometry) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (hit) {
|
||||||
|
useModelStore.getState().addMeasurePoint({
|
||||||
|
x: hit.point.x, y: hit.point.y, z: hit.point.z,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (triggered.current && trigVal < TRIG_OFF) {
|
||||||
|
triggered.current = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user