import RAPIER from "@dimforge/rapier3d-compat"; import { beforeAll, describe, expect, it } from "vitest"; import { DUNGEON_PHYSICAL_COLLISION_GROUPS, DUNGEON_SIGHT_BLOCKER_COLLISION_GROUPS } from "./dungeonLineOfSight"; import { hasDungeonLabelLineOfSight } from "./dungeonLabelVisibility"; beforeAll(async () => { await RAPIER.init(); }); const CAMERA = { x: 0, y: 2, z: 0 }; const LABEL = { x: 4, y: 2, z: 0 }; describe("dungeon label occlusion", () => { it("hides labels behind solid dungeon walls using the physical spatial index", () => { const world = new RAPIER.World({ x: 0, y: 0, z: 0 }); try { world.createCollider(RAPIER.ColliderDesc.cuboid(0.1, 3, 3) .setTranslation(2, 2, 0).setCollisionGroups(DUNGEON_PHYSICAL_COLLISION_GROUPS)); world.step(); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, LABEL)).toBe(false); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, { x: 1, y: 2, z: 0 })).toBe(true); } finally { world.free(); } }); it("occludes across floors and ceilings at the exact camera and label heights", () => { const world = new RAPIER.World({ x: 0, y: 0, z: 0 }); try { world.createCollider(RAPIER.ColliderDesc.cuboid(5, 0.1, 5) .setTranslation(0, 1, 0).setCollisionGroups(DUNGEON_PHYSICAL_COLLISION_GROUPS)); world.step(); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, { x: 0, y: 0, z: 0 })).toBe(false); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, LABEL)).toBe(true); } finally { world.free(); } }); it("ignores actors, trigger volumes, and the duplicate combat sight layer", () => { const world = new RAPIER.World({ x: 0, y: 0, z: 0 }); try { const actor = world.createRigidBody(RAPIER.RigidBodyDesc.kinematicPositionBased()); world.createCollider(RAPIER.ColliderDesc.cuboid(0.1, 3, 3).setTranslation(1, 2, 0), actor); world.createCollider(RAPIER.ColliderDesc.cuboid(0.1, 3, 3).setTranslation(2, 2, 0) .setSensor(true).setCollisionGroups(DUNGEON_PHYSICAL_COLLISION_GROUPS)); world.createCollider(RAPIER.ColliderDesc.cuboid(0.1, 3, 3).setTranslation(3, 2, 0) .setSensor(true).setCollisionGroups(DUNGEON_SIGHT_BLOCKER_COLLISION_GROUPS)); world.step(); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, LABEL)).toBe(true); } finally { world.free(); } }); it("does not query past a label or hide one at the camera position", () => { const world = new RAPIER.World({ x: 0, y: 0, z: 0 }); try { world.createCollider(RAPIER.ColliderDesc.cuboid(0.1, 3, 3).setTranslation(5, 2, 0)); world.step(); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, LABEL)).toBe(true); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, CAMERA)).toBe(true); expect(hasDungeonLabelLineOfSight(RAPIER, world, CAMERA, { x: NaN, y: 2, z: 0 })).toBe(false); } finally { world.free(); } }); });