Files
i-want-to-heal-mmo/scripts/prune_party_animations.mjs
T
2026-07-12 13:49:46 -04:00

48 lines
2.5 KiB
JavaScript

import { mkdir, stat } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { NodeIO } from "@gltf-transform/core";
import { EXTMeshoptCompression, EXTTextureWebP, KHRMeshQuantization } from "@gltf-transform/extensions";
import { MeshoptDecoder, MeshoptEncoder } from "meshoptimizer";
const rootDirectory = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const playerDirectory = path.join(rootDirectory, "game_assets/models/claudecraft/chars/players");
const outputDirectory = path.join(rootDirectory, ".tmp/pruned-party-models");
const writeInPlace = process.argv.includes("--write");
const REQUIRED_CLIPS = {
druid: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "2H_Melee_Attack_Chop"],
knight: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "1H_Melee_Attack_Chop"],
ranger: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "2H_Ranged_Shoot"],
mage: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "Spellcast_Shoot"],
rogue: ["Death_A", "Hit_A", "Idle", "Running_A", "Walking_A", "Spellcasting", "Dualwield_Melee_Attack_Chop"],
};
await MeshoptEncoder.ready;
const io = new NodeIO()
.registerExtensions([EXTMeshoptCompression, EXTTextureWebP, KHRMeshQuantization])
.registerDependencies({
"meshopt.decoder": MeshoptDecoder,
"meshopt.encoder": MeshoptEncoder,
});
if (!writeInPlace) await mkdir(outputDirectory, { recursive: true });
for (const [modelName, requiredClips] of Object.entries(REQUIRED_CLIPS)) {
const inputPath = path.join(playerDirectory, `${modelName}.glb`);
const document = await io.read(inputPath);
const animations = document.getRoot().listAnimations();
const available = new Set(animations.map((animation) => animation.getName()));
const missing = requiredClips.filter((clip) => !available.has(clip));
if (missing.length) throw new Error(`${modelName}.glb is missing required clips: ${missing.join(", ")}`);
for (const animation of animations) {
if (!requiredClips.includes(animation.getName())) animation.dispose();
}
const outputPath = writeInPlace ? inputPath : path.join(outputDirectory, `${modelName}.glb`);
await io.write(outputPath, document);
const sourceSize = (await stat(inputPath)).size;
const outputSize = (await stat(outputPath)).size;
console.log(`${path.basename(outputPath)}: ${sourceSize}${outputSize} bytes (${requiredClips.length} clips)`);
}