import { NodeIO } from "@gltf-transform/core"; import { BufferAttribute, BufferGeometry } from "three"; import { describe, expect, it } from "vitest"; import { buildPartyNavigationGraph, findNearestPartyNavigationSurface, findPartyNavigationPath, } from "./partyNavigation"; const PASPERS_ENTRANCE = [-0.181739, 0.349067, -11.472769] as const; const BLACKHORN_NISORN = [80.518555, 0.448065, -115.173169] as const; describe("shipping Pasper's Shrine party navigation", () => { it("routes from the entrance to the first source-authored boss", async () => { const document = await new NodeIO().read( "src/assets/game/dungeons/paspers-shrine/paspers-shrine-navigation.glb", ); const primitive = document.getRoot().listMeshes()[0]?.listPrimitives()[0]; const positions = primitive?.getAttribute("POSITION")?.getArray(); const indices = primitive?.getIndices()?.getArray(); if (!positions || !indices) throw new Error("Pasper navigation asset is not indexed."); const geometry = new BufferGeometry(); geometry.setAttribute("position", new BufferAttribute(new Float32Array(positions), 3)); geometry.setIndex(new BufferAttribute(new Uint32Array(indices), 1)); const graph = buildPartyNavigationGraph(geometry); const approach = findNearestPartyNavigationSurface(graph, BLACKHORN_NISORN, 6); expect(approach?.distance).toBeGreaterThan(5); expect(approach?.distance).toBeLessThan(6); const route = findPartyNavigationPath(graph, PASPERS_ENTRANCE, approach!.position, { maxStartSnapDistance: 3, maxEndSnapDistance: 5, simplifyTolerance: 0.01, }); geometry.dispose(); expect(route).not.toBeNull(); expect(route!.length).toBeGreaterThan(1); }); });