c11d7ab93a
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
3.6 KiB
3.6 KiB
Objetivo
Eliminar a "briga de matrizes" entre XRHitTestPlacement (pai) e XRGrabbable (filho) e adicionar Fixar/Desfixar a peça no espaço, para o fluxo de alinhar quina-com-quina sobre o objeto real.
O que muda
1. Reparent on grab/release (causa-raiz)
No XRGrabbable, no instante em que o grab começa, o grupo do modelo é transferido para a Scene preservando a transform de mundo. Enquanto está "na mão", o pai (XRHitTestPlacement) não consegue mais influenciar a matriz — fim do drift/salto.
- Início do grab (one-hand ou two-hand): guardar
originalParente chamarscene.attach(group). - Fim do grab (todas as mãos soltaram):
originalParent.attach(group)para devolver, mantendo a pose final. - Vale para os dois modos (single e dual). A captura de offset/startGroupWorld passa a acontecer depois do attach, garantindo math consistente.
2. Lock / Unlock (fixar peça)
Nova flag global isLocked no useModelStore:
- Quando
true:useControllerGrabignora o grip (não inicia grab) eXRGrabbablemostra um halo dourado discreto sinalizando "travado". - Quando travado, a peça permanece reparentada na Scene com a pose congelada — não volta para o
XRHitTestPlacement(impede que o reticle reposicione). - Botão "🔒 Fixar / 🔓 Liberar" no XR HUD (ao lado dos toggles existentes) e atalho
Lno DevKit (desktop).
3. Logs DevKit
[grab] attach→scene (hand=left|right|dual)[grab] release→<parentName>[lock] ON/[lock] OFF
Visíveis na aba Logs do DevPanel, úteis tanto no desktop quanto refletidos no overlay quando o testador usar o Quest.
4. Smoke test
Estender src/devkit/SmokeTest.tsx com um caso que:
- Cria um group filho de outro group, simula attach→scene, valida que
matrixWorldantes/depois é igual (preserve transform). - Toggle de lock impede
useControllerGrabde marcarisGrabbing=true.
Arquivos afetados
src/components/three/XRGrabbable.tsx— reparent on start/end, halo de lock.src/hooks/useControllerGrab.ts— respeitarisLocked(early return).src/stores/useModelStore.ts—isLocked: boolean+setLocked.src/components/XRHud.tsx— botão Fixar/Liberar.src/devkit/FakeControllers.tsx— atalhoL.src/devkit/SmokeTest.tsx— novos asserts.src/pages/XRSession.tsx— passar aScenepara oXRGrabbable(viauseThreeinterno; sem mudanças na árvore JSX).
Detalhes técnicos
THREE.Object3D.attach(child)preserva world-transform automaticamente (decompõe parent.matrixWorld⁻¹ × child.matrixWorld). É a primitiva certa, nãoadd.- O re-attach final usa o parent original capturado no grab start, não o parent atual — protege contra mudanças de árvore React entre frames.
- O
applyWorldMatrixToLocalcontinua igual, mas como o parent passa a ser aScene(matrixWorld = identidade), o cálculo fica trivial e estável. isLockednão persiste em localStorage por enquanto (decisão de sessão).
Fora de escopo (fica para depois)
- Snap visual quina-a-quina (destacar vértice mais próximo do controlador durante o grab).
- Migração para
RayGrabnativo. - Persistir lock + pose entre sessões.
Como validar
- Desktop + DevKit:
?devkit=1→ SimXR ON →Qsegura com a esquerda → mover → soltar. Não pode haver salto. ApertarL→ tentar segurar → não deve agarrar. - Quest 3 (testador): pegar peça, mover, soltar — pose final sem drift. Apertar Fixar no HUD → tentar pegar → não move.
- Aba Logs do DevPanel mostra a sequência
attach→scene/release→<parent>em todo grab.