fix render3
This commit is contained in:
1 parent
1404db1866
commit
c3af49d3e8
1 file changed
+12
-115
@@ -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
|
||||
|
||||
Reference in new issue
Block a user