import { fileURLToPath } from "node:url"; import { beforeAll, describe, expect, it } from "vitest"; import { NodeIO } from "@gltf-transform/core"; import { ALL_EXTENSIONS } from "@gltf-transform/extensions"; import { getBounds } from "@gltf-transform/functions"; import { MeshoptDecoder } from "meshoptimizer"; import { TEXTURE_SIZE, WEAPON_SOURCES } from "../tools/assets/weapons.mjs"; describe.each(WEAPON_SOURCES.map((source) => [source.id, source]))("%s.glb", (id, source) => { const file = fileURLToPath(new URL(`../public/models/weapons/${id}.glb`, import.meta.url)); let document; beforeAll(async () => { await MeshoptDecoder.ready; const io = new NodeIO() .registerExtensions(ALL_EXTENSIONS) .registerDependencies({ "meshopt.decoder": MeshoptDecoder }); document = await io.read(file); }, 30000); it("hat ein Mesh mit einem Primitive und einem Material", () => { const root = document.getRoot(); expect(root.listMeshes()).toHaveLength(1); expect(root.listMeshes()[0].listPrimitives()).toHaveLength(1); expect(root.listMaterials()).toHaveLength(1); }); // Bruch: Aufbereitung übersprungen — rohe Geometrie und PNG-Texturen bis 4096 px im Spiel. it("nutzt meshopt und WebP", () => { const used = document.getRoot().listExtensionsUsed().map((extension) => extension.extensionName); expect(used).toEqual(expect.arrayContaining(["EXT_meshopt_compression", "EXT_texture_webp"])); }); it("hat drei Texturen bis 1024 px", () => { const textures = document.getRoot().listTextures(); expect(textures).toHaveLength(3); for (const texture of textures) expect(Math.max(...texture.getSize())).toBeLessThanOrEqual(TEXTURE_SIZE); }); // Bruch: falsche Einheit — die Waffe wäre hundertmal zu groß oder zu klein in der Hand. it("ist so lang wie eine echte Waffe", () => { const { min, max } = getBounds(document.getRoot().listScenes()[0]); const length = Math.max(...max.map((value, axis) => value - min[axis])); expect(length).toBeGreaterThanOrEqual(source.length[0]); expect(length).toBeLessThanOrEqual(source.length[1]); }); });