🚀 Auto-deploy: melhoria no snap e medição AR em 24/05/2026 17:12:43

This commit is contained in:
2026-05-24 17:12:43 +00:00
parent 06556c7a45
commit f01c0f6d01
+39 -30
View File
@@ -75,6 +75,7 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
const dual = useRef<DualGrabRecord | null>(null);
const haloRef = useRef<THREE.Mesh>(null);
const everGrabbed = useRef(false);
const pendingReset = useRef(false);
// Reset scale to 1 (position/rotation kept) whenever resetScale() is called.
const scaleResetNonce = useModelStore((s) => s.scaleResetNonce);
@@ -88,9 +89,18 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
const { gl } = useThree();
useFrame((_state, dt) => {
useFrame((_state) => {
const group = groupRef.current;
if (!group) return;
if (pendingReset.current) {
group.position.set(0, 0, 0);
group.rotation.set(0, 0, 0);
group.scale.set(1, 1, 1);
pendingReset.current = false;
return;
}
const session = gl.xr.getSession();
const snap: ControllerGrabSnapshot = grab.current;
const L = snap.left;
@@ -121,21 +131,38 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
if (!active) return;
const ft = { ...active.fineTuning };
ft.posX += group.position.x;
ft.posY += group.position.y;
ft.posZ += group.position.z;
ft.rotX += (group.rotation.x * 180) / Math.PI;
ft.rotY += (group.rotation.y * 180) / Math.PI;
ft.rotZ += (group.rotation.z * 180) / Math.PI;
// 1. ESCALA DO MODELO: Lê o fator de escala do filho imediato para converter a translação do mundo real
const child = group.children[0];
const finalScaleFactor = child ? child.scale.x : 1.0;
ft.posX += group.position.x / finalScaleFactor;
ft.posY += group.position.y / finalScaleFactor;
ft.posZ += group.position.z / finalScaleFactor;
// 2. ROTAÇÃO TRIDIMENSIONAL: Combina quaternions de rotação para evitar gimbal lock ao soltar
const currentQuat = new THREE.Quaternion().setFromEuler(
new THREE.Euler(
(active.fineTuning.rotX * Math.PI) / 180,
(active.fineTuning.rotY * Math.PI) / 180,
(active.fineTuning.rotZ * Math.PI) / 180,
'XYZ'
)
);
const finalQuat = group.quaternion.clone().multiply(currentQuat);
const finalEuler = new THREE.Euler().setFromQuaternion(finalQuat, 'XYZ');
ft.rotX = (finalEuler.x * 180) / Math.PI;
ft.rotY = (finalEuler.y * 180) / Math.PI;
ft.rotZ = (finalEuler.z * 180) / Math.PI;
// 3. ESCALA LOCAL DO GRAB: Multiplica a escala anterior
ft.scale = (ft.scale ?? 1) * group.scale.x;
useModelStore.getState().setFineTuning(ft);
group.position.set(0, 0, 0);
group.rotation.set(0, 0, 0);
group.scale.set(1, 1, 1);
// Sinaliza reset pendente para o próximo frame
pendingReset.current = true;
triggerHaptic(session, 'left', 0.25, 12);
triggerHaptic(session, 'right', 0.25, 12);
@@ -184,23 +211,7 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
.multiply(tToOrigin)
.multiply(d.startGroupWorld);
// Decompose target world matrix
const targetPos = new THREE.Vector3();
const targetQuat = new THREE.Quaternion();
const targetScale = new THREE.Vector3();
let parentLocalMatrix = m;
if (group.parent) {
group.parent.updateMatrixWorld();
parentLocalMatrix = new THREE.Matrix4().copy(group.parent.matrixWorld).invert().multiply(m);
}
parentLocalMatrix.decompose(targetPos, targetQuat, targetScale);
// Apply smooth interpolation (anti-jitter dampening)
const t = Math.min(1.0, dt * 18);
group.position.lerp(targetPos, t);
group.quaternion.slerp(targetQuat, t);
group.scale.lerp(targetScale, t);
applyWorldMatrixToLocal(group, m);
return;
} else if (dual.current) {
console.log('[XR][grab] ◆ TWO-HAND end');
@@ -234,9 +245,7 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
targetWorldPos.applyMatrix4(invParent);
}
// Amortecimento suave da translação
const t = Math.min(1.0, dt * 18);
group.position.lerp(targetWorldPos, t);
group.position.copy(targetWorldPos);
} else if (single.current) {
console.log('[XR][grab] ● ONE-HAND end');
commitGrabTransforms();