Adicionou HUD debug XR
X-Lovable-Edit-ID: edt-a2cd647c-77d0-480c-b086-792055f18208 Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
+97
-21
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useRef, useMemo, useCallback, useState } from 'react';
|
import { useEffect, useRef, useMemo, useCallback, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
import { Canvas, useFrame, useThree } from '@react-three/fiber';
|
||||||
import { useGLTF, Grid, Html, Line } from '@react-three/drei';
|
import { useGLTF, Grid, Html, Line, OrbitControls, Text } from '@react-three/drei';
|
||||||
import { XR, createXRStore, useXR, XROrigin } from '@react-three/xr';
|
import { XR, createXRStore, useXR, XROrigin } from '@react-three/xr';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { useModelStore } from '@/stores/useModelStore';
|
import { useModelStore } from '@/stores/useModelStore';
|
||||||
@@ -338,6 +338,59 @@ function XRGrid() {
|
|||||||
|
|
||||||
// ImageTrackingAnchor removed — replaced by XRHitTestPlacement
|
// 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<THREE.Group>(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 (
|
||||||
|
<group ref={groupRef}>
|
||||||
|
<mesh position={[0, 0, -0.001]}>
|
||||||
|
<planeGeometry args={[0.32, 0.10]} />
|
||||||
|
<meshBasicMaterial color="#000000" transparent opacity={0.55} depthWrite={false} />
|
||||||
|
</mesh>
|
||||||
|
<Text
|
||||||
|
fontSize={0.012}
|
||||||
|
color="#22c55e"
|
||||||
|
anchorX="left"
|
||||||
|
anchorY="top"
|
||||||
|
position={[-0.155, 0.045, 0]}
|
||||||
|
maxWidth={0.30}
|
||||||
|
lineHeight={1.25}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
|
</group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ─── XRSession Page ────────────────────────────────────
|
// ─── XRSession Page ────────────────────────────────────
|
||||||
const XRSession = () => {
|
const XRSession = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -422,7 +475,7 @@ const XRSession = () => {
|
|||||||
<div className="flex-1 relative">
|
<div className="flex-1 relative">
|
||||||
<Canvas
|
<Canvas
|
||||||
gl={{ antialias: true, alpha: true, powerPreference: 'high-performance', preserveDrawingBuffer: true }}
|
gl={{ antialias: true, alpha: true, powerPreference: 'high-performance', preserveDrawingBuffer: true }}
|
||||||
camera={{ position: [0, 1.6, 0], fov: 50, near: 0.01, far: 100 }}
|
camera={{ position: [1.5, 1.2, 1.5], fov: 50, near: 0.01, far: 100 }}
|
||||||
className="!bg-transparent"
|
className="!bg-transparent"
|
||||||
onCreated={({ gl, invalidate }) => {
|
onCreated={({ gl, invalidate }) => {
|
||||||
gl.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
|
gl.setPixelRatio(Math.min(window.devicePixelRatio, 1.5));
|
||||||
@@ -438,28 +491,51 @@ const XRSession = () => {
|
|||||||
|
|
||||||
<XROrigin />
|
<XROrigin />
|
||||||
|
|
||||||
<XRHitTestPlacement
|
{inXR ? (
|
||||||
placementMode={placementMode}
|
<>
|
||||||
snapToPlanes={snapToPlanes}
|
{/* In XR: hit-test placement → grab wrapper → model */}
|
||||||
onPlace={() => {
|
<XRHitTestPlacement
|
||||||
setPlacementMode(false);
|
placementMode={placementMode}
|
||||||
toast.success('Modelo posicionado na superfície!');
|
snapToPlanes={snapToPlanes}
|
||||||
}}
|
onPlace={() => {
|
||||||
>
|
setPlacementMode(false);
|
||||||
<XRGrabbable
|
toast.success('Modelo posicionado na superfície!');
|
||||||
allowScale={allowScale}
|
}}
|
||||||
onGrabStart={() => {
|
>
|
||||||
if (placementMode) setPlacementMode(false);
|
<XRGrabbable
|
||||||
}}
|
allowScale={allowScale}
|
||||||
>
|
onGrabStart={() => {
|
||||||
<XRModel url={model.url} />
|
if (placementMode) setPlacementMode(false);
|
||||||
</XRGrabbable>
|
}}
|
||||||
</XRHitTestPlacement>
|
>
|
||||||
|
<XRModel url={model.url} />
|
||||||
|
</XRGrabbable>
|
||||||
|
</XRHitTestPlacement>
|
||||||
|
|
||||||
|
<XRMeasurementOverlay />
|
||||||
|
<ControllerFineTuning freeMove={freeMove} />
|
||||||
|
<XRDebugHud />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{/* Desktop preview (no headset): show model with OrbitControls */}
|
||||||
|
<group position={[0, 0, 0]}>
|
||||||
|
<XRModel url={model.url} />
|
||||||
|
</group>
|
||||||
|
<XRMeasurementOverlay />
|
||||||
|
<OrbitControls
|
||||||
|
makeDefault
|
||||||
|
enableDamping
|
||||||
|
dampingFactor={0.1}
|
||||||
|
minDistance={0.05}
|
||||||
|
maxDistance={50}
|
||||||
|
target={[0, 0, 0]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<XRMeasurementOverlay />
|
|
||||||
<XRSnapHandler />
|
<XRSnapHandler />
|
||||||
<XRGrid />
|
<XRGrid />
|
||||||
<ControllerFineTuning freeMove={freeMove} />
|
|
||||||
</XR>
|
</XR>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user