fix render3

This commit is contained in:
Rodrigo Bertoldo de Sousa Martins committed 2026-09-11 16:17:01 -03:00
1 parent 1404db1866
commit c3af49d3e8
1 file changed
+12 -115
+12 -115
View File
@@ -99,6 +99,18 @@ function resolveSkeletalBone(rawName) {
// eixo mais longo) — bem diferente de uma estrutura anatômica de verdade // 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 // (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. // 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"]); const LEADER_LINE_SOURCE_KEYS = new Set(["nervous", "regions"]);
function isLeaderLineMesh(mesh) { function isLeaderLineMesh(mesh) {
@@ -175,120 +187,6 @@ function isLeaderLinePointCloud(points) {
return p90 < axisLen * 0.1; 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) { function classifySystem(key, rawName) {
const target = SOURCE_TARGETS[key]; const target = SOURCE_TARGETS[key];
if (target.system) return target.system; if (target.system) return target.system;
@@ -377,7 +275,6 @@ for (const { fbx, key } of SOURCES) {
for (const child of node.listChildren()) visit(child); for (const child of node.listChildren()) visit(child);
return; return;
} else { } else {
if (LEADER_LINE_SOURCE_KEYS.has(key)) stripLeaderLineComponents(mesh);
systemSlug = classifySystem(key, rawName); systemSlug = classifySystem(key, rawName);
// Nomes de muscular/regiões/referências vêm com sufixo ".l"/".r"/ // Nomes de muscular/regiões/referências vêm com sufixo ".l"/".r"/
// ".j" (lateralidade) em vez da palavra "Left"/"Right" — sem tirar // ".j" (lateralidade) em vez da palavra "Left"/"Right" — sem tirar