adiciona modelo real
This commit is contained in:
1 parent
71d13753c9
commit
3a659a52c7
18 files changed
+612
-176
No files matched your search
@@ -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();
|
||||
|
||||
Reference in new issue
Block a user