retrabalho muscular
This commit is contained in:
1 parent
e3521ca494
commit
086befc148
1 file changed
+87
-8
@@ -34,6 +34,78 @@ function hexToRgb(hex) {
|
||||
return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255, 1];
|
||||
}
|
||||
|
||||
function hexToHsl(hex) {
|
||||
const [r, g, b] = hexToRgb(hex);
|
||||
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
const l = (max + min) / 2;
|
||||
if (max === min) return [0, 0, l];
|
||||
const d = max - min;
|
||||
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
let h;
|
||||
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
||||
else if (max === g) h = ((b - r) / d + 2) / 6;
|
||||
else h = ((r - g) / d + 4) / 6;
|
||||
return [h, s, l];
|
||||
}
|
||||
|
||||
function hslToHex(h, s, l) {
|
||||
h = ((h % 1) + 1) % 1; // normaliza pra [0,1) mesmo se ficou negativo
|
||||
s = Math.min(1, Math.max(0, s));
|
||||
l = Math.min(1, Math.max(0, l));
|
||||
const hue2rgb = (p, q, t) => {
|
||||
if (t < 0) t += 1;
|
||||
if (t > 1) t -= 1;
|
||||
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
||||
if (t < 1 / 2) return q;
|
||||
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
||||
return p;
|
||||
};
|
||||
let r, g, b;
|
||||
if (s === 0) {
|
||||
r = g = b = l;
|
||||
} else {
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
const toHex = (v) => Math.round(v * 255).toString(16).padStart(2, "0");
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
||||
}
|
||||
|
||||
// Hash determinístico simples (não precisa ser criptográfico, só estável
|
||||
// entre execuções) — usado pra variar a cor de cada músculo sempre do
|
||||
// MESMO jeito, sem depender de sorte/aleatoriedade a cada rodada do
|
||||
// pipeline.
|
||||
function stringHash(s) {
|
||||
let h = 0;
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
h = (h * 31 + s.charCodeAt(i)) | 0;
|
||||
}
|
||||
return h >>> 0;
|
||||
}
|
||||
|
||||
// O sistema muscular real do Z-Anatomy tem uma limitação: no tronco
|
||||
// (peitoral, oblíquos, reto do abdome, serrátil), a malha em si é um
|
||||
// "casco" externo liso, sem o relevo 3D que separa um músculo do outro
|
||||
// (confirmado comparando com a exportação bruta, antes de qualquer
|
||||
// processamento nosso — não é algo que a simplificação introduziu). Com
|
||||
// UMA cor lisa pro sistema inteiro (como todo outro sistema usa), músculos
|
||||
// vizinhos ficam visualmente indistinguíveis um do outro ali, mesmo sendo
|
||||
// objetos e nomes diferentes por baixo — "parece tudo agrupado". Damos a
|
||||
// cada músculo (mesma cor nos dois lados esquerdo/direito, já que é o
|
||||
// MESMO músculo) uma variação pequena e determinística de matiz/luminosidade
|
||||
// em torno da cor-base do sistema, só o bastante pra criar um limite visível
|
||||
// entre estruturas vizinhas sem fugir da identidade de cor do sistema.
|
||||
function muscleColorVariant(baseHex, variantKey) {
|
||||
const [h, s, l] = hexToHsl(baseHex);
|
||||
const hash = stringHash(variantKey);
|
||||
const hueShift = (((hash % 21) - 10) / 360) * 2; // até ±10° de matiz
|
||||
const lightShift = (((hash >> 8) % 21) - 10) / 100; // até ±10% de luminosidade
|
||||
return hslToHex(h + hueShift, s, Math.min(0.85, Math.max(0.25, l + lightShift)));
|
||||
}
|
||||
|
||||
const COMBINING_MARKS = new RegExp("[\\u0300-\\u036f]", "g");
|
||||
|
||||
function slugify(s) {
|
||||
@@ -267,17 +339,23 @@ for (const { fbx, key } of SOURCES) {
|
||||
}
|
||||
|
||||
// Um material por sistema alvo deste arquivo (recoloração consistente,
|
||||
// independente de como a FBX/Unity original colorizava as malhas).
|
||||
// independente de como a FBX/Unity original colorizava as malhas) — ou,
|
||||
// quando variantKey é passado (sistema muscular, ver muscleColorVariant),
|
||||
// um material por músculo, com uma variação de cor em torno da cor-base
|
||||
// do sistema.
|
||||
const materialCache = new Map();
|
||||
function materialFor(systemSlug) {
|
||||
if (materialCache.has(systemSlug)) return materialCache.get(systemSlug);
|
||||
function materialFor(systemSlug, variantKey) {
|
||||
const cacheKey = variantKey ? `${systemSlug}::${variantKey}` : systemSlug;
|
||||
if (materialCache.has(cacheKey)) return materialCache.get(cacheKey);
|
||||
const sys = systemBySlug.get(systemSlug);
|
||||
const baseHex = sys?.colorHex ?? "#cccccc";
|
||||
const colorHex = variantKey ? muscleColorVariant(baseHex, variantKey) : baseHex;
|
||||
const mat = doc
|
||||
.createMaterial(systemSlug)
|
||||
.setBaseColorFactor(hexToRgb(sys?.colorHex ?? "#cccccc"))
|
||||
.createMaterial(cacheKey)
|
||||
.setBaseColorFactor(hexToRgb(colorHex))
|
||||
.setRoughnessFactor(0.55)
|
||||
.setMetallicFactor(0.05);
|
||||
materialCache.set(systemSlug, mat);
|
||||
materialCache.set(cacheKey, mat);
|
||||
return mat;
|
||||
}
|
||||
|
||||
@@ -294,7 +372,7 @@ for (const { fbx, key } of SOURCES) {
|
||||
const uniqueMeshName = n === 1 ? rawName : `${rawName} (${n})`;
|
||||
node.setName(uniqueMeshName);
|
||||
|
||||
let systemSlug, namePt, nameEn, description;
|
||||
let systemSlug, namePt, nameEn, description, muscleColorKey;
|
||||
|
||||
if (key === "skeletal") {
|
||||
const resolved = resolveSkeletalBone(rawName);
|
||||
@@ -352,9 +430,10 @@ for (const { fbx, key } of SOURCES) {
|
||||
: key === "nervous"
|
||||
? NERVE_DESCRIPTIONS[base.toLowerCase()] ?? ""
|
||||
: "";
|
||||
if (key === "muscular") muscleColorKey = base.toLowerCase();
|
||||
}
|
||||
|
||||
const material = materialFor(systemSlug);
|
||||
const material = materialFor(systemSlug, muscleColorKey);
|
||||
for (const prim of mesh.listPrimitives()) prim.setMaterial(material);
|
||||
|
||||
const sys = systemBySlug.get(systemSlug);
|
||||
|
||||
Reference in new issue
Block a user