From 36fe6cc7b3e51cd95d6569c16d475f0f4e7f573b Mon Sep 17 00:00:00 2001 From: Rodrigo Bertoldo de Sousa Martins Date: Tue, 8 Sep 2026 14:06:02 -0300 Subject: [PATCH] fix5 --- .../src/components/viewer/SystemModel.tsx | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/viewer/SystemModel.tsx b/frontend/src/components/viewer/SystemModel.tsx index 58a922e..4bf0815 100644 --- a/frontend/src/components/viewer/SystemModel.tsx +++ b/frontend/src/components/viewer/SystemModel.tsx @@ -29,21 +29,21 @@ export function SystemModel({ }: Props) { const { scene } = useGLTF(modelUrl(modelFile)); const clonedRef = useRef(); - // 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.). + // Nome original de CADA objeto (não só malhas) como veio do .glb, + // capturado uma única vez no clone. Precisamos disso porque quando um + // nó do glTF tem mais de um material, o GLTFLoader do three.js cria um + // Group com o nome do nó e sub-malhas (children) com nomes DIFERENTES + // (gerados pelo loader) — sem isso, a maioria das estruturas com mais + // de um material nunca batia com o manifesto e ficava sempre oculta. const originalNames = useRef>(new WeakMap()); if (!clonedRef.current) { const clone = scene.clone(true); clone.traverse((obj) => { + originalNames.current.set(obj, obj.name); const mesh = obj as THREE.Mesh; - if (mesh.isMesh) { - originalNames.current.set(mesh, mesh.name); - if (mesh.material) { - mesh.material = (mesh.material as THREE.MeshStandardMaterial).clone(); - } + if (mesh.isMesh && mesh.material) { + mesh.material = (mesh.material as THREE.MeshStandardMaterial).clone(); } }); clonedRef.current = clone; @@ -55,12 +55,26 @@ export function SystemModel({ [structuresInFile], ); + // Sobe pelos pais até achar um nome (do próprio objeto ou de um + // ancestral) que bata com alguma estrutura do manifesto. + function resolveStructure(obj: THREE.Object3D): Structure | undefined { + let node: THREE.Object3D | null = obj; + while (node) { + const name = originalNames.current.get(node); + if (name) { + const structure = byMeshName.get(name); + if (structure) return structure; + } + node = node.parent; + } + return undefined; + } + 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); + const structure = resolveStructure(mesh); if (!structure) { // Malha sem correspondência no manifesto (não catalogada) — oculta. mesh.visible = false; @@ -77,6 +91,7 @@ export function SystemModel({ mat.emissive = isSelected ? HIGHLIGHT : BLACK; mat.emissiveIntensity = isSelected ? 0.6 : 0; }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [root, byMeshName, visibleSystems, selectedSlug, clippingPlanes]); const handleClick = (e: ThreeEvent) => {