import type { EntityPosition } from "../scene/entityStore.js"; export type Vec3 = EntityPosition; /** * World-space bounds a culler tests against. Kept as flat scalars (not tuples) so the * hot culling path reads them without allocating. Carries both a bounding sphere (cheap * broad reject) and a tight AABB (precise reject) — the standard "engine bounds" the * frustum, distance, and spatial-index tests all consume. */ export interface RenderBounds { centerX: number; centerY: number; centerZ: number; radius: number; minX: number; minY: number; minZ: number; maxX: number; maxY: number; maxZ: number; } /** * How a renderable declares its extent. AABB, bounding sphere, and 2D rectangle cover the * common cases; `point` is the degenerate zero-size default for objects that never override. * `offset` shifts the volume from the object origin (e.g. a tall model whose pivot is at its feet). */ export type BoundsSpec = { readonly kind: "sphere"; readonly radius: number; readonly offset?: Vec3; } | { readonly kind: "aabb"; readonly half: Vec3; readonly offset?: Vec3; } | { readonly kind: "rect"; readonly halfWidth: number; readonly halfDepth: number; readonly halfHeight?: number; readonly offset?: Vec3; } | { readonly kind: "point"; }; /** Conservative default: a unit sphere. Objects without explicit bounds cull as a small volume, never a point. */ export declare const DEFAULT_BOUNDS: BoundsSpec; /** @internal */ export declare function createRenderBounds(): RenderBounds; /** Resolve a spec + world position into concrete bounds. Writes into `out` when supplied (no allocation). * @internal */ export declare function resolveBounds(spec: BoundsSpec, position: Vec3, out?: RenderBounds): RenderBounds; /** @internal */ export declare function expandRenderBounds(bounds: RenderBounds, margin: number, out?: RenderBounds): RenderBounds; /** @internal */ export declare function aabbIntersects(aMinX: number, aMinY: number, aMinZ: number, aMaxX: number, aMaxY: number, aMaxZ: number, bMinX: number, bMinY: number, bMinZ: number, bMaxX: number, bMaxY: number, bMaxZ: number): boolean; /** @internal */ export declare function boundsIntersect(a: RenderBounds, b: RenderBounds): boolean; /** * BoundsSystem: caches resolved bounds per object and recomputes only when the caller's * version advances — i.e. when a transform, geometry, or the bounds spec itself changed. * The cached `RenderBounds` object is reused across recomputes, so a steady-state frame * that touches thousands of objects performs zero bounds allocations. */ export interface BoundsCache { get(id: string, version: number, spec: BoundsSpec, position: Vec3): RenderBounds; peek(id: string): RenderBounds | undefined; invalidate(id: string): void; delete(id: string): void; clear(): void; size(): number; recomputes(): number; } /** @internal */ export declare function createBoundsCache(): BoundsCache;