From b270ca0bfd2b4617961982be9ac5d75fd3d4dd53 Mon Sep 17 00:00:00 2001 From: admtracksteel Date: Thu, 28 May 2026 21:29:49 +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=2028/05/2026=2021:29:49?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/components/three/ControllerLocomotion.tsx | 87 +++++++++++++++---- src/pages/Index.tsx | 2 +- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 6e247ff..d344a8c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vite_react_shadcn_ts", "private": true, - "version": "1.0.4", + "version": "1.1.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/three/ControllerLocomotion.tsx b/src/components/three/ControllerLocomotion.tsx index bc8f9a1..b8b7a80 100644 --- a/src/components/three/ControllerLocomotion.tsx +++ b/src/components/three/ControllerLocomotion.tsx @@ -2,15 +2,17 @@ import { useRef, RefObject } from 'react'; import { useFrame, useThree } from '@react-three/fiber'; import { useXR } from '@react-three/xr'; import * as THREE from 'three'; +import { getAllModelLocalGroups } from '@/lib/modelTransforms'; /** * Quest 3 thumbstick locomotion (rig-relative). * * Mapeamento (thumbstick = axes[2]/axes[3] em xr-standard): - * • Para frente (axes[1] < -0.7), libera (axis volta a ~0) → teletransporta - * para o ponto onde o usuário está olhando (raycast da câmera). Se não - * houver geometria atingida, dá um passo de 0.5 m na direção do olhar. + * • Para frente (axes[1] < -0.7) → exibe mira no modelo em tempo real. + * Ao soltar (axis volta a ~0) → teletransporta para a mira. + * Se não houver modelo atingido pelo olhar, projeta mira a 0.5m. * • Para trás (axes[1] > 0.7) → passo de 0.3 m oposto ao olhar. + * Mapeado diretamente para facilitar pequenos ajustes. * • Lateral (|axes[0]| > 0.7) → snap-rotate ±30° no eixo Y do rig. * * NÃO lê grip ou trigger — esses são responsabilidade exclusiva de @@ -28,24 +30,29 @@ const STEP_BACK = 0.3; const STEP_FWD_NO_HIT = 0.5; export function ControllerLocomotion({ rigRef }: Props) { - const { scene, camera } = useThree(); + const { camera } = useThree(); const session = useXR((s) => s.session); const fwdPushed = useRef(false); const backLatched = useRef(false); const rotLatched = useRef<0 | -1 | 1>(0); const raycaster = useRef(new THREE.Raycaster()); + const reticleRef = useRef(null); useFrame(() => { const rig = rigRef.current; - if (!rig || !session) return; + const reticle = reticleRef.current; + if (!rig || !session) { + if (reticle && reticle.visible) reticle.visible = false; + return; + } let stickX = 0; let stickY = 0; for (const src of session.inputSources) { const gp = src.gamepad; if (!gp || gp.axes.length < 4) continue; - // Prefer left stick for locomotion + // Preferencia pelo stick esquerdo para locomoção if (src.handedness === 'left') { stickX = gp.axes[2] ?? 0; stickY = gp.axes[3] ?? 0; @@ -87,30 +94,78 @@ export function ControllerLocomotion({ rigRef }: Props) { // ── Forward → teleport on release ───────────────────────────── if (stickY < -DEAD) { fwdPushed.current = true; - } else if (fwdPushed.current && stickY > -RELEASE) { - fwdPushed.current = false; - // Cast from camera forward to find target on real/virtual geometry + + // Realiza raycast a partir da direção que o usuário está olhando (câmera) const origin = new THREE.Vector3(); camera.getWorldPosition(origin); const dir = new THREE.Vector3(); camera.getWorldDirection(dir); raycaster.current.set(origin, dir); raycaster.current.far = 30; - const hits = raycaster.current.intersectObjects(scene.children, true); + + // Filtra o raycast para atingir exclusivamente as peças 3D carregadas + const modelsObjects = getAllModelLocalGroups(); + const hits = raycaster.current.intersectObjects(modelsObjects, true); const hit = hits.find((h) => { const o = h.object; if (!(o instanceof THREE.Mesh)) return false; if (o.userData.__edgeLine) return false; return true; }); + const target = hit ? hit.point.clone() : origin.clone().add(dir.multiplyScalar(STEP_FWD_NO_HIT)); - const camWorld = new THREE.Vector3(); - camera.getWorldPosition(camWorld); - const delta = new THREE.Vector3().subVectors(target, camWorld); - delta.y = 0; // preserva altura do usuário - rig.position.add(delta); + + if (reticle) { + // Converte o ponto de mundo para o espaço local do rig + const localTarget = target.clone(); + rig.worldToLocal(localTarget); + reticle.position.copy(localTarget); + + if (hit && hit.face) { + const normalWorld = hit.face.normal.clone(); + const nm = new THREE.Matrix3().getNormalMatrix(hit.object.matrixWorld); + normalWorld.applyMatrix3(nm).normalize(); + + // Converte normal do mundo para o espaço local do rig para orientar o retículo + const rigWorldQuat = new THREE.Quaternion(); + rig.getWorldQuaternion(rigWorldQuat); + const normalLocal = normalWorld.clone().applyQuaternion(rigWorldQuat.invert()); + + const quaternion = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 1, 0), normalLocal); + reticle.quaternion.copy(quaternion); + } else { + reticle.rotation.set(-Math.PI / 2, 0, 0); + } + reticle.visible = true; + } + } else if (fwdPushed.current && stickY > -RELEASE) { + fwdPushed.current = false; + if (reticle && reticle.visible) { + reticle.visible = false; + + // Recupera o ponto projetado e calcula o deslocamento em relação à câmera + const localTarget = reticle.position.clone(); + const targetWorld = localTarget.clone(); + rig.localToWorld(targetWorld); + + const camWorld = new THREE.Vector3(); + camera.getWorldPosition(camWorld); + + const delta = new THREE.Vector3().subVectors(targetWorld, camWorld); + delta.y = 0; // preserva altura do rig do usuário + rig.position.add(delta); + } + } else { + if (reticle && reticle.visible) { + reticle.visible = false; + } } }); - return null; + return ( + + + + + ); } diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index f1bea15..2cc728b 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -337,7 +337,7 @@ const Index = () => { {/* Footer */}

- TrackSteelXR v1.05 — Q.C. Inspection + TrackSteelXR v1.10 — Q.C. Inspection

{showLogs && (