From f01c0f6d015c6c3a7eff327904b2febabc06768a Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Sun, 24 May 2026 17:12:43 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Auto-deploy:=20melhoria=20no=20s?= =?UTF-8?q?nap=20e=20medi=C3=A7=C3=A3o=20AR=20em=2024/05/2026=2017:12:43?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/three/XRGrabbable.tsx | 69 ++++++++++++++++------------ 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/src/components/three/XRGrabbable.tsx b/src/components/three/XRGrabbable.tsx index 88a08ef..809480f 100644 --- a/src/components/three/XRGrabbable.tsx +++ b/src/components/three/XRGrabbable.tsx @@ -75,6 +75,7 @@ export function XRGrabbable({ allowScale = true, lockedActive = false, onGrabSta const dual = useRef(null); const haloRef = useRef(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();