🚀 Auto-deploy: melhoria no snap e medição AR em 30/05/2026 10:42:44

This commit is contained in:
2026-05-30 10:42:44 +00:00
parent b6912bd070
commit 54a3f83375
3 changed files with 149 additions and 5 deletions
+119 -2
View File
@@ -1,6 +1,6 @@
import { Suspense, useRef, useMemo, useEffect, type ReactNode, useState } from 'react';
import { Canvas, useThree, useFrame } from '@react-three/fiber';
import { OrbitControls, useGLTF, Grid, Html, Line, PerspectiveCamera, OrthographicCamera } from '@react-three/drei';
import { OrbitControls, useGLTF, Grid, Html, Line, PerspectiveCamera, OrthographicCamera, PointerLockControls, Sky, Environment } from '@react-three/drei';
import { Loader2 } from 'lucide-react';
import * as THREE from 'three';
import { useModelStore, type SceneModel } from '@/stores/useModelStore';
@@ -1330,10 +1330,124 @@ function PositionDragHandler() {
function WalkControls() {
const walkMode = useModelStore(s => s.walkMode);
const setWalkMode = useModelStore(s => s.setWalkMode);
const gridY = useModelStore(s => s.gridY);
const { camera } = useThree();
const controlsRef = useRef<any>(null);
const keys = useRef({ w: false, a: false, s: false, d: false, shift: false });
const initialized = useRef(false);
useEffect(() => {
if (walkMode && !initialized.current) {
camera.position.set(0, gridY + 1.7, 5);
// Give the camera an initial rotation looking forward
camera.rotation.set(0, 0, 0);
initialized.current = true;
} else if (!walkMode) {
initialized.current = false;
}
}, [walkMode, camera, gridY]);
useEffect(() => {
if (walkMode) {
// Small timeout to allow canvas to render and be clickable
const timer = setTimeout(() => {
try { controlsRef.current?.lock(); } catch (e) {}
}, 100);
return () => clearTimeout(timer);
}
}, [walkMode]);
useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.code === 'KeyW') keys.current.w = true;
if (e.code === 'KeyA') keys.current.a = true;
if (e.code === 'KeyS') keys.current.s = true;
if (e.code === 'KeyD') keys.current.d = true;
if (e.code === 'ShiftLeft' || e.code === 'ShiftRight') keys.current.shift = true;
};
const onKeyUp = (e: KeyboardEvent) => {
if (e.code === 'KeyW') keys.current.w = false;
if (e.code === 'KeyA') keys.current.a = false;
if (e.code === 'KeyS') keys.current.s = false;
if (e.code === 'KeyD') keys.current.d = false;
if (e.code === 'ShiftLeft' || e.code === 'ShiftRight') keys.current.shift = false;
};
window.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
return () => {
window.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
};
}, []);
useFrame((_, dt) => {
if (!walkMode) return;
const speed = keys.current.shift ? 6.0 : 2.5;
const dir = new THREE.Vector3();
// Front vector (ignoring pitch to walk purely horizontally)
const front = new THREE.Vector3(0, 0, -1).applyQuaternion(camera.quaternion);
front.y = 0;
if (front.lengthSq() > 0.001) front.normalize();
// Right vector
const right = new THREE.Vector3(1, 0, 0).applyQuaternion(camera.quaternion);
right.y = 0;
if (right.lengthSq() > 0.001) right.normalize();
if (keys.current.w) dir.add(front);
if (keys.current.s) dir.sub(front);
if (keys.current.a) dir.sub(right);
if (keys.current.d) dir.add(right);
if (dir.lengthSq() > 0) {
dir.normalize();
camera.position.addScaledVector(dir, speed * dt);
}
// Always stick to exactly 1.7m above ground
camera.position.y = gridY + 1.7;
});
if (!walkMode) return null;
return (
<PointerLockControls
ref={controlsRef}
onUnlock={() => {
setWalkMode(false);
}}
/>
);
}
function WalkEnvironment() {
const walkMode = useModelStore(s => s.walkMode);
const gridY = useModelStore(s => s.gridY);
if (!walkMode) return null;
return (
<group>
{/* Realistic blue sky with some clouds/haze */}
<Sky sunPosition={[100, 20, 100]} turbidity={0.1} rayleigh={0.5} mieCoefficient={0.005} mieDirectionalG={0.8} />
{/* Background HDRI provides distant trees, mountains, houses on the horizon */}
<Environment preset="park" background />
{/* Infinite walkable grass floor */}
<mesh rotation={[-Math.PI / 2, 0, 0]} position={[0, gridY - 0.01, 0]} receiveShadow>
<planeGeometry args={[1000, 1000]} />
<meshStandardMaterial color="#4ade80" roughness={0.9} metalness={0.1} />
</mesh>
</group>
);
}
export function ModelViewerCanvas({ url }: ModelViewerProps) {
const positionMode = useModelStore((s) => s.positionMode);
const measureMode = useModelStore((s) => s.measureMode);
const selectionMode = useModelStore((s) => s.selectionMode);
const walkMode = useModelStore((s) => s.walkMode);
return (
<Canvas
camera={{ position: [2, 2, 2], fov: 50, near: 0.001, far: 1000 }}
@@ -1376,6 +1490,9 @@ export function ModelViewerCanvas({ url }: ModelViewerProps) {
<VisibilityApplier />
<SceneRefCapture />
<ViewCubeAnimator />
<WalkControls />
<WalkEnvironment />
<OrbitControls
makeDefault
@@ -1385,7 +1502,7 @@ export function ModelViewerCanvas({ url }: ModelViewerProps) {
maxDistance={50}
minZoom={5}
maxZoom={5000}
enabled={!positionMode}
enabled={!positionMode && !walkMode}
ref={(c: any) => {
mainControlsRef.current = c;
}}