import type { Ray, Vector, World } from "@dimforge/rapier3d-compat"; import { interactionGroups } from "@react-three/rapier"; interface LabelRapierApi { readonly Ray: new (origin: Vector, direction: Vector) => Ray; readonly QueryFilterFlags: { readonly ONLY_FIXED: number; readonly EXCLUDE_SENSORS: number; }; } const LABEL_OCCLUSION_GROUPS = interactionGroups(0, 0); const LABEL_SURFACE_MARGIN = 0.08; /** Uses Rapier's spatial index, bounded to the camera-to-label segment. */ export function hasDungeonLabelLineOfSight( rapier: LabelRapierApi, world: World, camera: Vector, label: Vector, ): boolean { const dx = label.x - camera.x; const dy = label.y - camera.y; const dz = label.z - camera.z; const distance = Math.hypot(dx, dy, dz); if (!Number.isFinite(distance)) return false; if (distance <= LABEL_SURFACE_MARGIN) return true; const ray = new rapier.Ray(camera, { x: dx / distance, y: dy / distance, z: dz / distance }); return !world.castRay( ray, distance - LABEL_SURFACE_MARGIN, true, rapier.QueryFilterFlags.ONLY_FIXED | rapier.QueryFilterFlags.EXCLUDE_SENSORS, LABEL_OCCLUSION_GROUPS, ); }