From e457ab2073ab95e4ed1d33ff0b5a03c9aab5a8fc Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:25:34 +0000 Subject: [PATCH 1/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/pages/XRSession.tsx | 53 ----------------------------------------- 1 file changed, 53 deletions(-) diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index 56e29f9..576aae9 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -374,59 +374,6 @@ function XRGrid() { // ImageTrackingAnchor removed — replaced by XRHitTestPlacement -// ─── XR Debug HUD (floats in front of viewer) ────────── -// Shows live grip/trigger values and isGrabbing state per hand. -// Essential for diagnosing grab issues without external monitoring. -function XRDebugHud() { - const session = useXR((s) => s.session); - const groupRef = useRef(null); - const { camera } = useThree(); - const [text, setText] = useState('XR aguardando inputs...'); - - useFrame(() => { - if (!groupRef.current) return; - // Anchor the HUD to a fixed offset in front of the camera - const offset = new THREE.Vector3(-0.25, -0.18, -0.6).applyQuaternion(camera.quaternion); - groupRef.current.position.copy(camera.position).add(offset); - groupRef.current.quaternion.copy(camera.quaternion); - - if (!session) return; - const sources = Array.from(session.inputSources); - const lines: string[] = [`inputs: ${sources.length}`]; - for (const src of sources) { - const gp = src.gamepad; - const grip = gp?.buttons[2]; - const trig = gp?.buttons[1]; - const gv = grip ? (grip.value || (grip.pressed ? 1 : 0)) : 0; - const tv = trig ? (trig.value || (trig.pressed ? 1 : 0)) : 0; - const grabbing = gv > 0.7 ? 'GRAB' : gv > 0.3 ? '...' : ' '; - lines.push(`${src.handedness.padEnd(5)} grip:${gv.toFixed(2)} trg:${tv.toFixed(2)} ${grabbing}`); - } - setText(lines.join('\n')); - }); - - return ( - - - - - - - {text} - - - ); -} - - // ─── XRSession Page ──────────────────────────────────── const XRSession = () => { const navigate = useNavigate(); From 5035a0c6b46fac6152a24d2ac2ec13fa2548ec6d Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:25:46 +0000 Subject: [PATCH 2/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/pages/XRSession.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index 576aae9..dd65883 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -516,7 +516,7 @@ const XRSession = () => { - + {/* In-world HUD — visible inside passthrough where DOM overlays cannot reach */} Date: Thu, 14 May 2026 18:26:06 +0000 Subject: [PATCH 3/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/pages/XRSession.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index dd65883..4607bce 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -485,12 +485,15 @@ const XRSession = () => { {effectiveInXR ? ( <> - {/* In XR (or SimXR): grab wrapper → ACTIVE model. Other models render as static background. */} - + {/* In XR (or SimXR): all models share the same placement origin so + switching active doesn't make others appear to "disappear". + Only the ACTIVE model receives grab transforms. */} {simXR ? ( + { if (placementMode) setPlacementMode(false); }} > @@ -505,8 +508,10 @@ const XRSession = () => { toast.success('Modelo posicionado na superfície!'); }} > + { if (placementMode) setPlacementMode(false); }} > From 479d8bd28691e4c30e25e045e58a5c58a94e842c Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:26:18 +0000 Subject: [PATCH 4/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/XRGrabbable.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/three/XRGrabbable.tsx b/src/components/three/XRGrabbable.tsx index 2c10a61..e72cf7a 100644 --- a/src/components/three/XRGrabbable.tsx +++ b/src/components/three/XRGrabbable.tsx @@ -6,6 +6,8 @@ import { useControllerGrab, ControllerGrabSnapshot } from '@/hooks/useController interface XRGrabbableProps { /** Allow uniform scaling via two-hand grab (distance between hands) */ allowScale?: boolean; + /** When true, the active model is locked — grab is ignored entirely */ + lockedActive?: boolean; /** Called the first time the user grabs (e.g. to exit placement mode) */ onGrabStart?: () => void; children: ReactNode; From 5f306ed69ad447603d3a3f72289336a42987d4db Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:26:29 +0000 Subject: [PATCH 5/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/XRGrabbable.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/three/XRGrabbable.tsx b/src/components/three/XRGrabbable.tsx index e72cf7a..03ace6e 100644 --- a/src/components/three/XRGrabbable.tsx +++ b/src/components/three/XRGrabbable.tsx @@ -47,7 +47,7 @@ const _tmpQuat = new THREE.Quaternion(); * On release the group keeps its last pose (we don't write to the store — * the manual fine-tuning sliders remain available as additional offsets). */ -export function XRGrabbable({ allowScale = false, onGrabStart, children }: XRGrabbableProps) { +export function XRGrabbable({ allowScale = false, lockedActive = false, onGrabStart, children }: XRGrabbableProps) { const groupRef = useRef(null); const grab = useControllerGrab(); @@ -63,6 +63,14 @@ export function XRGrabbable({ allowScale = false, onGrabStart, children }: XRGra const L = snap.left; const R = snap.right; + // Locked: ignore all grab input — release any in-progress grabs and bail. + if (lockedActive) { + if (single.current) single.current = null; + if (dual.current) dual.current = null; + if (haloRef.current) haloRef.current.visible = false; + return; + } + const lActive = L.isGrabbing && L.hasPose; const rActive = R.isGrabbing && R.hasPose; From 15fa8606f78f87d4d82789aeade297b823f4ff11 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:26:39 +0000 Subject: [PATCH 6/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/pages/XRSession.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index 4607bce..23b0d01 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -378,6 +378,10 @@ function XRGrid() { const XRSession = () => { const navigate = useNavigate(); const { model, anchorMode, setAnchorMode } = useModelStore(); + const isActiveLocked = useModelStore((s) => { + const a = s.models.find(m => m.id === s.activeModelId); + return !!a?.locked; + }); const [inXR, setInXR] = useState(false); const [freeMove, setFreeMove] = useState(true); const [placementMode, setPlacementMode] = useState(true); // start in placement mode From 1e19cfca59561865fe6b94ec7e55c004f977111e Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:26:49 +0000 Subject: [PATCH 7/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/pages/XRSession.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/XRSession.tsx b/src/pages/XRSession.tsx index 23b0d01..7abd1b7 100644 --- a/src/pages/XRSession.tsx +++ b/src/pages/XRSession.tsx @@ -172,9 +172,13 @@ function XRActiveModel() { function ControllerFineTuning({ freeMove }: { freeMove: boolean }) { const { setFineTuning } = useModelStore(); const session = useXR((s) => s.session); + const isActiveLocked = useModelStore((s) => { + const a = s.models.find(m => m.id === s.activeModelId); + return !!a?.locked; + }); useFrame(() => { - if (!session) return; + if (!session || isActiveLocked) return; const inputSources = session.inputSources; if (!inputSources) return; From e9227f51bf9c3aa4b765c314210567f6f3368290 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:27:18 +0000 Subject: [PATCH 8/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/XRHudInWorld.tsx | 38 ++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/components/three/XRHudInWorld.tsx b/src/components/three/XRHudInWorld.tsx index a202b7a..95a26a2 100644 --- a/src/components/three/XRHudInWorld.tsx +++ b/src/components/three/XRHudInWorld.tsx @@ -53,6 +53,10 @@ export function XRHudInWorld(props: XRHudInWorldProps) { const leftCtrl = useXRInputSourceState('controller', 'left'); const [open, setOpen] = useState(true); const [tab, setTab] = useState('scene'); + /** When pinned, the panel stays fixed in world space and does NOT follow the head. */ + const [pinned, setPinned] = useState(true); + /** Forces re-anchor on next frame (used by "trazer" / unpin→pin). */ + const reanchorRequested = useRef(true); const panelRef = useRef(null); const wristRef = useRef(null); @@ -70,14 +74,29 @@ export function XRHudInWorld(props: XRHudInWorldProps) { lastABtn.current = pressed; } - // Floating panel ~70cm in front of head, yaw-locked, damped + // Floating panel: + // • If pinned and already anchored once → don't move it (fixed in space). + // • If unpinned → continuously follow head with damping. + // • On (re)anchor request → snap target once and stop. if (panelRef.current && open) { const fwd = new THREE.Vector3(0, -0.05, -0.7).applyQuaternion(camera.quaternion); - targetPos.current.copy(camera.position).add(fwd); + const wantPos = new THREE.Vector3().copy(camera.position).add(fwd); const euler = new THREE.Euler().setFromQuaternion(camera.quaternion, 'YXZ'); - targetQuat.current.setFromEuler(new THREE.Euler(0, euler.y, 0, 'YXZ')); - panelRef.current.position.lerp(targetPos.current, 0.12); - panelRef.current.quaternion.slerp(targetQuat.current, 0.18); + const wantQuat = new THREE.Quaternion().setFromEuler(new THREE.Euler(0, euler.y, 0, 'YXZ')); + + if (reanchorRequested.current) { + targetPos.current.copy(wantPos); + targetQuat.current.copy(wantQuat); + panelRef.current.position.copy(wantPos); + panelRef.current.quaternion.copy(wantQuat); + reanchorRequested.current = false; + } else if (!pinned) { + targetPos.current.copy(wantPos); + targetQuat.current.copy(wantQuat); + panelRef.current.position.lerp(targetPos.current, 0.12); + panelRef.current.quaternion.slerp(targetQuat.current, 0.18); + } + // pinned + already anchored → leave panel where it is. } // Wrist toggle follows left controller @@ -102,7 +121,14 @@ export function XRHudInWorld(props: XRHudInWorldProps) { setOpen((v) => !v)} /> {open && ( - + setPinned((v) => !v)} + onRecenter={() => { reanchorRequested.current = true; }} + {...props} + /> )} From f4bc29299109333e3ae610c03bad843804472191 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 18:27:36 +0000 Subject: [PATCH 9/9] Changes Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com> --- src/components/three/XRHudInWorld.tsx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/components/three/XRHudInWorld.tsx b/src/components/three/XRHudInWorld.tsx index 95a26a2..ce1921b 100644 --- a/src/components/three/XRHudInWorld.tsx +++ b/src/components/three/XRHudInWorld.tsx @@ -135,7 +135,15 @@ export function XRHudInWorld(props: XRHudInWorldProps) { ); } -function FloatingPanel({ tab, setTab, ...p }: { tab: Tab; setTab: (t: Tab) => void } & XRHudInWorldProps) { +function FloatingPanel({ + tab, setTab, pinned, onTogglePin, onRecenter, ...p +}: { + tab: Tab; + setTab: (t: Tab) => void; + pinned: boolean; + onTogglePin: () => void; + onRecenter: () => void; +} & XRHudInWorldProps) { const tabs: { id: Tab; label: string; icon: string }[] = [ { id: 'scene', label: 'Cena', icon: '🧩' }, { id: 'tools', label: 'Ferram.', icon: '🛠' }, @@ -150,10 +158,14 @@ function FloatingPanel({ tab, setTab, ...p }: { tab: Tab; setTab: (t: Tab) => vo anchorX="left" anchorY="middle"> TrackSteelXR · HUD AR - - Botão A (esq) abre/fecha - + {/* Pin / Recenter controls (replace the previous static "A button" hint) */} + + + + {tabs.map((t, i) => (