From c3af49d3e831ac30373cff162cadf1176d76d2a9 Mon Sep 17 00:00:00 2001 From: Rodrigo Bertoldo de Sousa Martins Date: Fri, 11 Sep 2026 16:17:01 -0300 Subject: [PATCH] fix render3 --- data-pipeline/scripts/extract-manifest.mjs | 127 ++------------------- 1 file changed, 12 insertions(+), 115 deletions(-) diff --git a/data-pipeline/scripts/extract-manifest.mjs b/data-pipeline/scripts/extract-manifest.mjs index 05bb084..5318446 100644 --- a/data-pipeline/scripts/extract-manifest.mjs +++ b/data-pipeline/scripts/extract-manifest.mjs @@ -99,6 +99,18 @@ function resolveSkeletalBone(rawName) { // eixo mais longo) — bem diferente de uma estrutura anatômica de verdade // (mesmo um nervo fino tem curvas/ramificações reais). Isso não depende // de nome nenhum, então não arrisca excluir a estrutura errada. +// +// TENTATIVA DESCARTADA: chegamos a tentar ir além e remover, dentro da +// MESMA malha de uma estrutura real, só o "pedaço" desconectado que fosse +// reto/fino (pra pegar linhas de chamada soldadas junto de uma estrutura +// de verdade). Reverted: uma faixa real de pele fina e comprida (ex: bem +// em cima da coluna) tem a MESMA assinatura geométrica de uma linha de +// chamada (poucos vértices, reta, fina) depois de simplificada — não dá +// pra distinguir as duas só pela forma, e a tentativa abriu buracos de +// verdade na malha (pele sumindo, esqueleto aparecendo por baixo). Fica +// só a classificação por MALHA INTEIRA abaixo, que é mais conservadora: +// pode deixar passar algum tracinho residual, mas nunca teve o risco de +// apagar uma estrutura real. const LEADER_LINE_SOURCE_KEYS = new Set(["nervous", "regions"]); function isLeaderLineMesh(mesh) { @@ -175,120 +187,6 @@ function isLeaderLinePointCloud(points) { return p90 < axisLen * 0.1; } -// Cobre um caso que isLeaderLineMesh() sozinho não pega: quando a linha de -// chamada NÃO é um nó separado, e sim geometria extra soldada dentro do -// MESMO primitivo de uma estrutura real (ex: a malha de uma região da pele -// vem com um "espinho" fino grudado nela desde a exportação do FBX -// original). Como a maior parte da malha nesse caso é a estrutura de -// verdade (não uma linha), o teste de colinearidade da malha INTEIRA -// reprova, e o espinho sobrevive grudado — foi exatamente o que apareceu -// como tracinhos saindo do corpo (cabeça, mão, canela) mesmo depois do -// filtro por malha. A correção certa é olhar por PEDAÇO CONECTADO da -// malha (componentes que não compartilham nenhum vértice entre si), não a -// malha toda: cada pedaço geometricamente desconectado é testado -// separadamente, e só os que passam no teste de "reto e fino" são -// removidos — a estrutura real (um componente só, com volume) nunca é. -function stripLeaderLineComponents(mesh) { - try { - stripLeaderLineComponentsUnsafe(mesh); - } catch { - // Nunca deixa uma malha atípica derrubar o pipeline inteiro — na - // dúvida, mantém a estrutura como veio (mesmo comportamento de antes - // desta função existir). - } -} - -function stripLeaderLineComponentsUnsafe(mesh) { - for (const prim of mesh.listPrimitives()) { - const posAccessor = prim.getAttribute("POSITION"); - const indicesAccessor = prim.getIndices(); - if (!posAccessor || !indicesAccessor) continue; - const indices = indicesAccessor.getArray(); - const posArr = posAccessor.getArray(); - if (!indices || !posArr || indices.length < 3) continue; - const at = (vi) => [posArr[vi * 3], posArr[vi * 3 + 1], posArr[vi * 3 + 2]]; - - // Union-Find simples pra agrupar vértices que compartilham alguma face - // (= estão no mesmo pedaço fisicamente conectado da malha). - const parent = new Map(); - function find(x) { - let root = x; - while (parent.get(root) !== root) root = parent.get(root); - while (parent.get(x) !== root) { - const next = parent.get(x); - parent.set(x, root); - x = next; - } - return root; - } - function union(a, b) { - const ra = find(a), rb = find(b); - if (ra !== rb) parent.set(ra, rb); - } - - const faceCount = Math.floor(indices.length / 3); - for (let f = 0; f < faceCount; f++) { - const a = indices[f * 3], b = indices[f * 3 + 1], c = indices[f * 3 + 2]; - if (!parent.has(a)) parent.set(a, a); - if (!parent.has(b)) parent.set(b, b); - if (!parent.has(c)) parent.set(c, c); - union(a, b); - union(b, c); - } - - const facesByRoot = new Map(); - for (let f = 0; f < faceCount; f++) { - const root = find(indices[f * 3]); - if (!facesByRoot.has(root)) facesByRoot.set(root, []); - facesByRoot.get(root).push(f); - } - // Malha inteira é UM pedaço só conectado — nada pra separar/remover. - if (facesByRoot.size <= 1) continue; - - // Uma linha de chamada de verdade no app original é um segmento bem - // simples (poucos vértices) — bem menor que qualquer pedaço real de - // pele/estrutura. Sem esse limite de tamanho, uma faixa real só - // COMPRIDA (ex: a pele bem em cima da coluna, entre as costas - // esquerda e direita) também passa no teste de "reto e fino" e acaba - // sendo apagada por engano, abrindo um buraco de verdade na malha - // (foi exatamente o que aconteceu e o usuário reportou). Limitamos a - // remoção a pedaços pequenos em termos absolutos E relativos ao resto - // da malha. - const totalVerts = posAccessor.getCount(); - const MAX_LEADER_LINE_VERTS = 40; - const MAX_LEADER_LINE_SHARE = 0.15; - - const keptFaces = []; - let removedAny = false; - for (const faceList of facesByRoot.values()) { - const vertSet = new Set(); - for (const f of faceList) { - vertSet.add(indices[f * 3]); - vertSet.add(indices[f * 3 + 1]); - vertSet.add(indices[f * 3 + 2]); - } - const smallEnough = - vertSet.size <= MAX_LEADER_LINE_VERTS && vertSet.size <= totalVerts * MAX_LEADER_LINE_SHARE; - if (smallEnough && isLeaderLinePointCloud([...vertSet].map(at))) { - removedAny = true; - } else { - keptFaces.push(...faceList); - } - } - if (!removedAny || keptFaces.length === 0) continue; - - keptFaces.sort((x, y) => x - y); - const newIndices = new indices.constructor(keptFaces.length * 3); - let w = 0; - for (const f of keptFaces) { - newIndices[w++] = indices[f * 3]; - newIndices[w++] = indices[f * 3 + 1]; - newIndices[w++] = indices[f * 3 + 2]; - } - indicesAccessor.setArray(newIndices); - } -} - function classifySystem(key, rawName) { const target = SOURCE_TARGETS[key]; if (target.system) return target.system; @@ -377,7 +275,6 @@ for (const { fbx, key } of SOURCES) { for (const child of node.listChildren()) visit(child); return; } else { - if (LEADER_LINE_SOURCE_KEYS.has(key)) stripLeaderLineComponents(mesh); systemSlug = classifySystem(key, rawName); // Nomes de muscular/regiões/referências vêm com sufixo ".l"/".r"/ // ".j" (lateralidade) em vez da palavra "Left"/"Right" — sem tirar