adiciona modelo real

This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-09-07 21:30:18 -03:00
1 parent 71d13753c9
commit 3a659a52c7
18 files changed
+612 -176

No files matched your search

+45 -8
View File
@@ -1,46 +1,83 @@
import { useEffect, useRef } from "react";
import { useEffect, useMemo, useRef } from "react";
import { useGLTF } from "@react-three/drei";
import * as THREE from "three";
import type { ThreeEvent } from "@react-three/fiber";
import { modelUrl } from "../../lib/api.js";
import { modelUrl, type Structure } from "../../lib/api.js";
interface Props {
modelFile: string;
// Estruturas do banco cujo modelFile é este arquivo — pode conter mais de
// um sistema (ex: o glb "visceral-organs" tem estruturas de "respiratorio"
// e de "visceral" ao mesmo tempo).
structuresInFile: Structure[];
visibleSystems: Set<string>;
selectedSlug: string | null;
onSelect: (slug: string) => void;
clippingPlanes: THREE.Plane[];
}
const HIGHLIGHT = new THREE.Color("#22d3ee");
const BLACK = new THREE.Color(0x000000);
export function SystemModel({ modelFile, selectedSlug, onSelect, clippingPlanes }: Props) {
export function SystemModel({
modelFile,
structuresInFile,
visibleSystems,
selectedSlug,
onSelect,
clippingPlanes,
}: Props) {
const { scene } = useGLTF(modelUrl(modelFile));
const clonedRef = useRef<THREE.Group>();
// Nome original do nó (como veio do .glb), capturado uma única vez no
// clone — necessário porque o efeito abaixo reescreve mesh.name para o
// slug da estrutura, e precisa continuar encontrando a estrutura certa
// em execuções seguintes (toggle de camadas, seleção, corte etc.).
const originalNames = useRef<WeakMap<THREE.Object3D, string>>(new WeakMap());
if (!clonedRef.current) {
const clone = scene.clone(true);
clone.traverse((obj) => {
const mesh = obj as THREE.Mesh;
if (mesh.isMesh && mesh.material) {
mesh.material = (mesh.material as THREE.MeshStandardMaterial).clone();
if (mesh.isMesh) {
originalNames.current.set(mesh, mesh.name);
if (mesh.material) {
mesh.material = (mesh.material as THREE.MeshStandardMaterial).clone();
}
}
});
clonedRef.current = clone;
}
const root = clonedRef.current!;
const byMeshName = useMemo(
() => new Map(structuresInFile.map((s) => [s.meshName, s])),
[structuresInFile],
);
useEffect(() => {
root.traverse((obj) => {
const mesh = obj as THREE.Mesh;
if (!mesh.isMesh) return;
const originalName = originalNames.current.get(mesh) ?? mesh.name;
const structure = byMeshName.get(originalName);
if (!structure) {
// Malha sem correspondência no manifesto (não catalogada) — oculta.
mesh.visible = false;
return;
}
mesh.name = structure.slug;
mesh.visible = visibleSystems.has(structure.system.slug);
const mat = mesh.material as THREE.MeshStandardMaterial;
mat.clippingPlanes = clippingPlanes;
mat.clipShadows = true;
const isSelected = mesh.name === selectedSlug;
mat.emissive = isSelected ? HIGHLIGHT : new THREE.Color(0x000000);
const isSelected = structure.slug === selectedSlug;
mat.emissive = isSelected ? HIGHLIGHT : BLACK;
mat.emissiveIntensity = isSelected ? 0.6 : 0;
});
}, [root, selectedSlug, clippingPlanes]);
}, [root, byMeshName, visibleSystems, selectedSlug, clippingPlanes]);
const handleClick = (e: ThreeEvent<MouseEvent>) => {
e.stopPropagation();