Files
healer-man/src/scene/dungeonLabelVisibility.test.ts
T
2026-09-09 22:18:15 -04:00

59 lines
2.9 KiB
TypeScript

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(); }
});
});