From 1f40184f0142735094a2d4b6af294b2ef05748d2 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 13:36:46 +0000 Subject: [PATCH] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- .lovable/plan.md | 100 +++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/.lovable/plan.md b/.lovable/plan.md index 5891c4b..7b057b0 100644 --- a/.lovable/plan.md +++ b/.lovable/plan.md @@ -1,54 +1,78 @@ +## Plano: Grab nativo dos controles Touch Plus (Quest 3) para "vestir" a peça virtual +Objetivo: permitir que o usuário **agarre a peça virtual com o grip** do Touch Plus e a movimente em 6 graus de liberdade junto com a mão (translação + rotação simultâneas), e use **as duas mãos juntas** para girar/escalar com naturalidade. Joystick atual continua existindo como ajuste fino. -## Plan: Help Button + Full Rotation Controls (X, Y, Z) +### 1. Camada de input nova: `useControllerGrab` -### 1. Add rotX and rotZ to the store +Novo hook em `src/hooks/useControllerGrab.ts` que encapsula tudo que falta hoje: -Update `src/stores/useModelStore.ts`: -- Add `rotX: number` and `rotZ: number` to the `FineTuning` interface -- Set defaults to 0 in `defaultFineTuning` +- Lê `session.inputSources` a cada frame e identifica os `XRInputSource` "left" e "right". +- Para cada controle, expõe: + - `gripValue` (0..1, analógico, vindo de `gamepad.buttons[2].value`). + - `triggerValue` (0..1, `buttons[1].value`). + - `isGrabbing` (boolean com histerese: começa em `value > 0.7`, termina em `< 0.3` — evita flicker). + - `gripPose`: matrix4 da pose do `inputSource.gripSpace` no `referenceSpace` atual, atualizada por frame via `XRFrame.getPose()`. +- Também escuta os eventos nativos `squeezestart` / `squeezeend` da `XRSession` como fallback caso o gamepad não exponha valor analógico. -### 2. Apply rotX/rotZ to the XR model +### 2. Componente `XRGrabbable` em volta da peça -Update `src/pages/XRSession.tsx` (`XRModel` component): -- Read `rotX`, `rotZ` from fineTuning -- Change `rotation={[0, rotYRad, 0]}` to `rotation={[rotXRad, rotYRad, rotZRad]}` +Novo arquivo `src/components/three/XRGrabbable.tsx`. Wrappa o `` em um `` controlado e implementa as três modalidades de grab: -Update `ControllerFineTuning`: -- Map additional controller inputs (e.g., A/B buttons or grip+joystick combos) to rotX/rotZ, or leave rotation to the HUD controls +- **One-hand grab**: quando `isGrabbing` de uma mão dispara, calcula matrix relativa `M_offset = gripPose⁻¹ · modelPose`. A cada frame, enquanto segura, faz `modelPose = gripPose · M_offset`. Resultado: a peça vira "extensão da mão", com translação + rotação simultâneas. +- **Two-hand grab** (quando ambas as mãos estão segurando): + - Pivô = ponto médio entre os dois `gripPose`. + - Rotação = orientação do vetor entre as duas mãos (mantém o cabo da viga sempre alinhado com o eixo entre os punhos). + - Escala = razão entre a distância atual e a distância no momento em que a 2ª mão agarrou (com clamp 0.1× a 10×). **Opcional**: pode ser desabilitada na HUD para usos onde a escala 1:1 é obrigatória (caso "vestir" sobre peça real). +- Ao soltar (`isGrabbing` cai), grava a pose final em `useModelStore.fineTuning` (decompõe em `posX/Y/Z`, `rotX/Y/Z`, e `scale` se two-hand estiver ativo) — assim o estado fica consistente com o joystick e os controles do HUD. -### 3. Add rotX/rotZ controls to XRHud +### 3. Joystick continua, agora modulado pelo grip analógico -Update `src/components/XRHud.tsx`: -- In the fine-tuning expanded panel, add rotation rows for X° and Z° alongside the existing Y° control (same `+/−` button pattern) +Em `ControllerFineTuning` (dentro de `XRSession.tsx`): -### 4. Create Help panel with (?) button +- Mantém o mapeamento atual mas **só ativa quando NENHUM grip está apertado** (para não brigar com o grab). +- `posStep` e `rotStep` ganham multiplicador derivado de `triggerValue` da mesma mão: trigger leve = passo fino (×0.3), trigger forte = passo rápido (×3). Resolve a queixa de "movimento mecânico". +- Mapeamento corrigido para o padrão VR (já discutido): + - Esquerdo: X horizontal / Z vertical (frente-trás). + - Direito: rotY horizontal / Y altura vertical. +- Adiciona aceleração proporcional (curva quadrática no input do thumbstick) e deadzone de 0.15. -Update `src/components/XRHud.tsx`: -- Add a `HelpCircle` (?) icon button to the main toolbar -- When pressed, open a full-height slide-out panel (same pattern as the checklist panel) containing a scrollable list of all tools and controls with descriptions: +### 4. Escala no store -Content sections: -- **Posicionar (Hit-Test)**: Tap to place model on real surfaces -- **Snap**: Auto-align to detected planes (floor, wall, table) -- **Mover (Free Move)**: Use left joystick for X/Y, right joystick for Z movement -- **Grid**: Toggle reference grid -- **Sólido / Wire / Bordas**: Render mode switching -- **Medir**: Tap two points to measure distance in mm -- **Screenshot**: Capture current AR view -- **Inspeção**: Open checklist to approve/reject items -- **Opacidade**: Adjust model transparency -- **Posição (mm)**: Fine-tune X/Y/Z in 1mm steps -- **Rotação X° / Y° / Z°**: Rotate model around each axis -- **Controladores Quest 3**: Joystick left = move X/Y, joystick right = move Z + rotate Y, grip = hold to move (when Free Move is off) +`src/stores/useModelStore.ts`: adicionar `scale: number` ao `FineTuning` (default 1). `XRModel` e `ModelViewer` aplicam `scale={[s,s,s]}` no ``. -### 5. Also apply rotX/rotZ in desktop ModelViewer (if used) +### 5. UI: feedback visual e ajuda -Check `src/components/three/ModelViewer.tsx` and update rotation there too for consistency. +- `XRGrabbable` mostra um halo sutil ao redor da peça quando `gripValue > 0.1` em qualquer mão (intensidade proporcional → o usuário sente a pressão analógica). +- `src/components/XRHud.tsx`: na seção de Ajuda (?), adicionar bloco "Agarrar a peça": + - "Aperte o grip lateral de um controle perto da peça → ela vira extensão da sua mão." + - "Agarre com os dois grips ao mesmo tempo → rotacione girando os punhos / aproxime-afaste para escalar." + - "Solte o grip para fixar." + - "Trigger modula a velocidade do joystick (leve = fino, forte = rápido)." +- Adicionar toggle "Permitir escala (two-hand)" — default OFF para preservar 1:1. -### Files to modify -- `src/stores/useModelStore.ts` — add rotX, rotZ fields -- `src/pages/XRSession.tsx` — apply 3-axis rotation, update controller mapping -- `src/components/XRHud.tsx` — add rotation controls for X/Z, add help (?) panel -- `src/components/three/ModelViewer.tsx` — apply rotX/rotZ if fineTuning is used there +### 6. Integração e ordem de aplicação +No `XRSession.tsx`, a árvore vira: +``` + (posicionamento inicial) + (grab 6DOF + two-hand) + +``` +Hit-test só atua enquanto `placementMode = true`. Quando o usuário agarra pela primeira vez, `placementMode` é desligado automaticamente. + +### Detalhes técnicos + +- Tudo via WebXR Gamepad API + `XRFrame.getPose(gripSpace, referenceSpace)`. Sem dependências novas. +- Histerese do grip evita perder o grab por tremor de mão. +- Decomposição de matrix usa `THREE.Matrix4.decompose()` para gravar de volta no store sem perder rotação composta. +- Plane snapping continua ativo durante o grab (snap acontece no `onRelease`, não a cada frame, para não travar o movimento). +- Logs `[XR][grab]` para diagnóstico (start/end, qual mão, valor analógico). + +### Arquivos afetados + +- **Novo**: `src/hooks/useControllerGrab.ts` +- **Novo**: `src/components/three/XRGrabbable.tsx` +- **Editado**: `src/pages/XRSession.tsx` — envolver modelo, ajustar `ControllerFineTuning` (mapeamento + modulação por trigger + bypass quando grip ativo) +- **Editado**: `src/stores/useModelStore.ts` — campo `scale` +- **Editado**: `src/components/three/ModelViewer.tsx` — aplicar `scale` +- **Editado**: `src/components/XRHud.tsx` — toggle "permitir escala", ajuda nova