Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -504,9 +504,138 @@ function GridAutoFollower() {
|
|||||||
}, 100);
|
}, 100);
|
||||||
return () => clearTimeout(id);
|
return () => clearTimeout(id);
|
||||||
}, [models, scene]);
|
}, [models, scene]);
|
||||||
|
/** Mouse drag handler for desktop "Posicionar" mode: translates/rotates the active model. */
|
||||||
|
function PositionDragHandler() {
|
||||||
|
const { camera, gl, scene } = useThree();
|
||||||
|
const positionMode = useModelStore((s) => s.positionMode);
|
||||||
|
const activeId = useModelStore((s) => s.activeModelId);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!positionMode) {
|
||||||
|
gl.domElement.style.cursor = 'grab';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gl.domElement.style.cursor = 'move';
|
||||||
|
|
||||||
|
const canvas = gl.domElement;
|
||||||
|
let dragging = false;
|
||||||
|
let button = 0;
|
||||||
|
let shiftKey = false;
|
||||||
|
let lastX = 0;
|
||||||
|
let lastY = 0;
|
||||||
|
let pixelsPerWorldUnit = 1;
|
||||||
|
|
||||||
|
const computePixelScale = () => {
|
||||||
|
const st = useModelStore.getState();
|
||||||
|
const active = st.models.find((m) => m.id === st.activeModelId);
|
||||||
|
if (!active) return;
|
||||||
|
// Find the model's group position (approx world): use renderFactor + fineTuning pos
|
||||||
|
const factor = st.scaleRatio?.factor ?? 1;
|
||||||
|
const wp = new THREE.Vector3(
|
||||||
|
active.fineTuning.posX * factor,
|
||||||
|
active.fineTuning.posY * factor,
|
||||||
|
active.fineTuning.posZ * factor,
|
||||||
|
);
|
||||||
|
const dist = camera.position.distanceTo(wp);
|
||||||
|
const perspCam = camera as THREE.PerspectiveCamera;
|
||||||
|
const fov = (perspCam.fov ?? 50) * (Math.PI / 180);
|
||||||
|
const worldPerPixel = (2 * dist * Math.tan(fov / 2)) / canvas.clientHeight;
|
||||||
|
pixelsPerWorldUnit = worldPerPixel; // world units per pixel
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDown = (e: PointerEvent) => {
|
||||||
|
const st = useModelStore.getState();
|
||||||
|
if (!st.positionMode || !st.activeModelId) return;
|
||||||
|
dragging = true;
|
||||||
|
button = e.button;
|
||||||
|
shiftKey = e.shiftKey;
|
||||||
|
lastX = e.clientX;
|
||||||
|
lastY = e.clientY;
|
||||||
|
computePixelScale();
|
||||||
|
(e.target as Element).setPointerCapture?.(e.pointerId);
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
const onMove = (e: PointerEvent) => {
|
||||||
|
if (!dragging) return;
|
||||||
|
const st = useModelStore.getState();
|
||||||
|
const active = st.models.find((m) => m.id === st.activeModelId);
|
||||||
|
if (!active) return;
|
||||||
|
|
||||||
|
const dx = e.clientX - lastX;
|
||||||
|
const dy = e.clientY - lastY;
|
||||||
|
lastX = e.clientX;
|
||||||
|
lastY = e.clientY;
|
||||||
|
shiftKey = e.shiftKey;
|
||||||
|
|
||||||
|
const ft = active.fineTuning;
|
||||||
|
const factor = st.scaleRatio?.factor ?? 1;
|
||||||
|
|
||||||
|
if (button === 2) {
|
||||||
|
// Right button: rotate
|
||||||
|
const sens = 0.4; // deg per pixel
|
||||||
|
if (shiftKey) {
|
||||||
|
useModelStore.getState().setFineTuning({ rotZ: ft.rotZ + dx * sens });
|
||||||
|
} else {
|
||||||
|
useModelStore.getState().setFineTuning({
|
||||||
|
rotY: ft.rotY + dx * sens,
|
||||||
|
rotX: ft.rotX + dy * sens,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (button === 1 || (button === 0 && shiftKey)) {
|
||||||
|
// Middle or Shift+left: depth (Z in camera space)
|
||||||
|
const worldDelta = dy * pixelsPerWorldUnit;
|
||||||
|
const camDir = new THREE.Vector3();
|
||||||
|
camera.getWorldDirection(camDir);
|
||||||
|
const move = camDir.multiplyScalar(worldDelta).divideScalar(factor);
|
||||||
|
useModelStore.getState().setFineTuning({
|
||||||
|
posX: ft.posX + move.x,
|
||||||
|
posY: ft.posY + move.y,
|
||||||
|
posZ: ft.posZ + move.z,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Left: translate in camera plane
|
||||||
|
const right = new THREE.Vector3();
|
||||||
|
const up = new THREE.Vector3();
|
||||||
|
camera.matrixWorld.extractBasis(right, up, new THREE.Vector3());
|
||||||
|
const worldDX = dx * pixelsPerWorldUnit;
|
||||||
|
const worldDY = -dy * pixelsPerWorldUnit;
|
||||||
|
const move = right.multiplyScalar(worldDX).add(up.multiplyScalar(worldDY)).divideScalar(factor);
|
||||||
|
useModelStore.getState().setFineTuning({
|
||||||
|
posX: ft.posX + move.x,
|
||||||
|
posY: ft.posY + move.y,
|
||||||
|
posZ: ft.posZ + move.z,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onUp = (e: PointerEvent) => {
|
||||||
|
dragging = false;
|
||||||
|
(e.target as Element).releasePointerCapture?.(e.pointerId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onContext = (e: MouseEvent) => e.preventDefault();
|
||||||
|
|
||||||
|
canvas.addEventListener('pointerdown', onDown);
|
||||||
|
canvas.addEventListener('pointermove', onMove);
|
||||||
|
canvas.addEventListener('pointerup', onUp);
|
||||||
|
canvas.addEventListener('pointercancel', onUp);
|
||||||
|
canvas.addEventListener('contextmenu', onContext);
|
||||||
|
return () => {
|
||||||
|
canvas.removeEventListener('pointerdown', onDown);
|
||||||
|
canvas.removeEventListener('pointermove', onMove);
|
||||||
|
canvas.removeEventListener('pointerup', onUp);
|
||||||
|
canvas.removeEventListener('pointercancel', onUp);
|
||||||
|
canvas.removeEventListener('contextmenu', onContext);
|
||||||
|
gl.domElement.style.cursor = 'grab';
|
||||||
|
};
|
||||||
|
}, [positionMode, activeId, camera, gl, scene]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function ModelViewerCanvas({ url }: ModelViewerProps) {
|
export function ModelViewerCanvas({ url }: ModelViewerProps) {
|
||||||
return (
|
return (
|
||||||
<Canvas
|
<Canvas
|
||||||
|
|||||||
Reference in New Issue
Block a user