98 lines
2.8 KiB
JavaScript
98 lines
2.8 KiB
JavaScript
import { Document, NodeIO } from "@gltf-transform/core";
|
|
import { readFileSync, mkdirSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { buildGeometry } from "./geometry.mjs";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const manifestPath = path.resolve(__dirname, "../seed/manifest.json");
|
|
const outDir = path.resolve(__dirname, "../seed/models");
|
|
mkdirSync(outDir, { recursive: true });
|
|
|
|
const { structures } = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
|
|
function eulerToQuat([x, y, z]) {
|
|
const cx = Math.cos(x / 2), sx = Math.sin(x / 2);
|
|
const cy = Math.cos(y / 2), sy = Math.sin(y / 2);
|
|
const cz = Math.cos(z / 2), sz = Math.sin(z / 2);
|
|
return [
|
|
sx * cy * cz - cx * sy * sz,
|
|
cx * sy * cz + sx * cy * sz,
|
|
cx * cy * sz - sx * sy * cz,
|
|
cx * cy * cz + sx * sy * sz,
|
|
];
|
|
}
|
|
|
|
function hexToRgb(hex) {
|
|
const n = parseInt(hex.replace("#", ""), 16);
|
|
return [((n >> 16) & 255) / 255, ((n >> 8) & 255) / 255, (n & 255) / 255, 1];
|
|
}
|
|
|
|
const bySystem = new Map();
|
|
for (const s of structures) {
|
|
if (!bySystem.has(s.system)) bySystem.set(s.system, []);
|
|
bySystem.get(s.system).push(s);
|
|
}
|
|
|
|
const systemColors = {
|
|
esqueletico: "#e8e2d0",
|
|
muscular: "#b5433a",
|
|
nervoso: "#f2c14e",
|
|
cardiovascular: "#c1121f",
|
|
respiratorio: "#4f7cff",
|
|
tegumentar: "#e0a96d",
|
|
};
|
|
|
|
for (const [systemSlug, items] of bySystem) {
|
|
const doc = new Document();
|
|
const buffer = doc.createBuffer();
|
|
const scene = doc.createScene(systemSlug);
|
|
doc.getRoot().setDefaultScene(scene);
|
|
|
|
const material = doc
|
|
.createMaterial(systemSlug)
|
|
.setBaseColorFactor(hexToRgb(systemColors[systemSlug] ?? "#cccccc"))
|
|
.setRoughnessFactor(0.6)
|
|
.setMetallicFactor(0.05);
|
|
|
|
for (const item of items) {
|
|
const geo = buildGeometry(item.shape);
|
|
const posAccessor = doc
|
|
.createAccessor()
|
|
.setType("VEC3")
|
|
.setArray(geo.positions)
|
|
.setBuffer(buffer);
|
|
const normAccessor = doc
|
|
.createAccessor()
|
|
.setType("VEC3")
|
|
.setArray(geo.normals)
|
|
.setBuffer(buffer);
|
|
const idxAccessor = doc
|
|
.createAccessor()
|
|
.setType("SCALAR")
|
|
.setArray(geo.indices)
|
|
.setBuffer(buffer);
|
|
|
|
const prim = doc
|
|
.createPrimitive()
|
|
.setAttribute("POSITION", posAccessor)
|
|
.setAttribute("NORMAL", normAccessor)
|
|
.setIndices(idxAccessor)
|
|
.setMaterial(material);
|
|
|
|
const mesh = doc.createMesh(item.slug).addPrimitive(prim);
|
|
const node = doc
|
|
.createNode(item.slug)
|
|
.setMesh(mesh)
|
|
.setTranslation(item.position)
|
|
.setRotation(eulerToQuat(item.rotation ?? [0, 0, 0]));
|
|
|
|
scene.addChild(node);
|
|
}
|
|
|
|
const io = new NodeIO();
|
|
const outFile = path.join(outDir, `${systemSlug}.glb`);
|
|
await io.write(outFile, doc);
|
|
console.log(`✓ ${outFile} (${items.length} estruturas)`);
|
|
}
|