🚀 Auto-deploy: melhoria no snap e medição AR em 24/05/2026 17:12:43
This commit is contained in:
@@ -75,6 +75,7 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
|
|||||||
const dual = useRef<DualGrabRecord | null>(null);
|
const dual = useRef<DualGrabRecord | null>(null);
|
||||||
const haloRef = useRef<THREE.Mesh>(null);
|
const haloRef = useRef<THREE.Mesh>(null);
|
||||||
const everGrabbed = useRef(false);
|
const everGrabbed = useRef(false);
|
||||||
|
const pendingReset = useRef(false);
|
||||||
|
|
||||||
// Reset scale to 1 (position/rotation kept) whenever resetScale() is called.
|
// Reset scale to 1 (position/rotation kept) whenever resetScale() is called.
|
||||||
const scaleResetNonce = useModelStore((s) => s.scaleResetNonce);
|
const scaleResetNonce = useModelStore((s) => s.scaleResetNonce);
|
||||||
@@ -88,9 +89,18 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
|
|||||||
|
|
||||||
const { gl } = useThree();
|
const { gl } = useThree();
|
||||||
|
|
||||||
useFrame((_state, dt) => {
|
useFrame((_state) => {
|
||||||
const group = groupRef.current;
|
const group = groupRef.current;
|
||||||
if (!group) return;
|
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 session = gl.xr.getSession();
|
||||||
const snap: ControllerGrabSnapshot = grab.current;
|
const snap: ControllerGrabSnapshot = grab.current;
|
||||||
const L = snap.left;
|
const L = snap.left;
|
||||||
@@ -121,21 +131,38 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
|
|||||||
if (!active) return;
|
if (!active) return;
|
||||||
|
|
||||||
const ft = { ...active.fineTuning };
|
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;
|
// 1. ESCALA DO MODELO: Lê o fator de escala do filho imediato para converter a translação do mundo real
|
||||||
ft.rotY += (group.rotation.y * 180) / Math.PI;
|
const child = group.children[0];
|
||||||
ft.rotZ += (group.rotation.z * 180) / Math.PI;
|
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;
|
ft.scale = (ft.scale ?? 1) * group.scale.x;
|
||||||
|
|
||||||
useModelStore.getState().setFineTuning(ft);
|
useModelStore.getState().setFineTuning(ft);
|
||||||
|
|
||||||
group.position.set(0, 0, 0);
|
// Sinaliza reset pendente para o próximo frame
|
||||||
group.rotation.set(0, 0, 0);
|
pendingReset.current = true;
|
||||||
group.scale.set(1, 1, 1);
|
|
||||||
|
|
||||||
triggerHaptic(session, 'left', 0.25, 12);
|
triggerHaptic(session, 'left', 0.25, 12);
|
||||||
triggerHaptic(session, 'right', 0.25, 12);
|
triggerHaptic(session, 'right', 0.25, 12);
|
||||||
@@ -184,23 +211,7 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
|
|||||||
.multiply(tToOrigin)
|
.multiply(tToOrigin)
|
||||||
.multiply(d.startGroupWorld);
|
.multiply(d.startGroupWorld);
|
||||||
|
|
||||||
// Decompose target world matrix
|
applyWorldMatrixToLocal(group, m);
|
||||||
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);
|
|
||||||
return;
|
return;
|
||||||
} else if (dual.current) {
|
} else if (dual.current) {
|
||||||
console.log('[XR][grab] ◆ TWO-HAND end');
|
console.log('[XR][grab] ◆ TWO-HAND end');
|
||||||
@@ -234,9 +245,7 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta
|
|||||||
targetWorldPos.applyMatrix4(invParent);
|
targetWorldPos.applyMatrix4(invParent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amortecimento suave da translação
|
group.position.copy(targetWorldPos);
|
||||||
const t = Math.min(1.0, dt * 18);
|
|
||||||
group.position.lerp(targetWorldPos, t);
|
|
||||||
} else if (single.current) {
|
} else if (single.current) {
|
||||||
console.log('[XR][grab] ● ONE-HAND end');
|
console.log('[XR][grab] ● ONE-HAND end');
|
||||||
commitGrabTransforms();
|
commitGrabTransforms();
|
||||||
|
|||||||
Reference in New Issue
Block a user