/**
 * Shared Narrowphase Module — 3D
 *
 * Provides contact-computing narrowphase tests and a generic collision
 * iteration pipeline for 3D collider pairs (AABB3D + Sphere).
 *
 * Mirrors the 2D narrowphase (`narrowphase.ts`) with an added Z axis.
 */
import type { SpatialIndex3D } from './spatial-hash3D';
/**
 * Contact result from a 3D narrowphase test. Normal points from A toward B.
 *
 * Narrowphase functions use this as an out-parameter: the caller owns the
 * struct, the function writes fields in place and returns `true` on hit.
 * The `onContact` callback in `detectCollisions3D` receives a shared module-
 * level instance — **subscribers must consume it synchronously and must not
 * retain the reference across frames**.
 */
export interface Contact3D {
    normalX: number;
    normalY: number;
    normalZ: number;
    /** Penetration depth (positive = overlapping) */
    depth: number;
}
/** Collider shape discriminator for the flattened BaseColliderInfo3D layout. */
export declare const AABB3D_SHAPE = 0;
export declare const SPHERE_SHAPE = 1;
export type ColliderShape3D = typeof AABB3D_SHAPE | typeof SPHERE_SHAPE;
/**
 * Minimum collider data shared by 3D collision and physics bundles.
 *
 * Flat layout (no nested sub-objects): the `shape` discriminator tells you
 * whether to read `halfWidth`/`halfHeight`/`halfDepth` (AABB3D) or `radius`
 * (Sphere). Unused fields are set to 0.
 *
 * Pool-friendly — all fields are assigned in place each frame.
 */
export interface BaseColliderInfo3D<L extends string = string> {
    entityId: number;
    x: number;
    y: number;
    z: number;
    layer: L;
    collidesWith: readonly L[];
    /**
     * Bit assigned to `layer` from the lazy layer registry. Populated by
     * `fillBaseColliderInfo3D`. Used together with `collidesWithMask` to
     * replace per-pair `Array.includes` layer checks with a single AND.
     */
    layerBit: number;
    /** OR of `getLayerBit3D` for every entry in `collidesWith`. */
    collidesWithMask: number;
    shape: ColliderShape3D;
    halfWidth: number;
    halfHeight: number;
    halfDepth: number;
    radius: number;
}
export declare const getLayerBit3D: (layer: string) => number;
export declare const getCollidesWithMask3D: (collidesWith: readonly string[]) => number;
/**
 * Populate a `BaseColliderInfo3D` slot in place from raw component data.
 * Returns `true` if the slot was filled, `false` if the entity has no
 * collider (caller should skip it).
 *
 * If an entity has both AABB3D and sphere colliders, AABB3D wins and only
 * the AABB3D offset is applied.
 */
export declare function fillBaseColliderInfo3D<L extends string>(info: BaseColliderInfo3D<L>, entityId: number, x: number, y: number, z: number, layer: L, collidesWith: readonly L[], aabb3D: {
    width: number;
    height: number;
    depth: number;
    offsetX?: number;
    offsetY?: number;
    offsetZ?: number;
} | undefined, sphere: {
    radius: number;
    offsetX?: number;
    offsetY?: number;
    offsetZ?: number;
} | undefined): boolean;
/**
 * Retrieve the optional spatialIndex3D resource, returning undefined when absent.
 * Centralizes the cross-plugin typed lookup so individual plugins don't each
 * need to import SpatialIndex3D or repeat the tryGetResource pattern.
 */
export declare function tryGetSpatialIndex3D(tryGetResource: <T>(key: string) => T | undefined): SpatialIndex3D | undefined;
/**
 * Write an AABB3D-vs-AABB3D contact into `out`. Returns `true` if the
 * shapes overlap (out was filled), `false` otherwise.
 *
 * Resolves along the axis with minimum penetration depth.
 */
export declare function computeAABB3DvsAABB3D(ax: number, ay: number, az: number, ahw: number, ahh: number, ahd: number, bx: number, by: number, bz: number, bhw: number, bhh: number, bhd: number, out: Contact3D): boolean;
/**
 * Write a sphere-vs-sphere contact into `out`. Returns `true` if the
 * spheres overlap.
 */
export declare function computeSphereVsSphere(ax: number, ay: number, az: number, ar: number, bx: number, by: number, bz: number, br: number, out: Contact3D): boolean;
/**
 * Write an AABB3D-vs-sphere contact into `out`. Returns `true` if the
 * shapes overlap.
 *
 * Uses closest-point-on-AABB to sphere center. When the sphere center
 * is inside the AABB, resolves along the axis with minimum push distance.
 */
export declare function computeAABB3DvsSphere(aabbX: number, aabbY: number, aabbZ: number, ahw: number, ahh: number, ahd: number, sphereX: number, sphereY: number, sphereZ: number, radius: number, out: Contact3D): boolean;
/**
 * Dispatch to the correct narrowphase function for the given pair and
 * write the contact into `out`. Returns `true` if the pair overlaps.
 */
export declare function computeContact3D(a: BaseColliderInfo3D, b: BaseColliderInfo3D, out: Contact3D): boolean;
/**
 * Generic 3D collision detection pipeline: brute-force or broadphase,
 * with layer filtering and contact computation.
 *
 * `count` is the number of live entries at the front of `colliders`.
 * The array itself may be a grow-only pool — only indices `[0, count)`
 * are iterated, so trailing pool slots are ignored.
 *
 * `workingMap` is a caller-owned `Map<number, I>` used by the broadphase
 * path as an entityId → collider lookup. It is cleared and repopulated on
 * each call; callers should allocate it once and pass the same instance
 * every frame.
 *
 * Uses a context parameter forwarded to the callback to avoid
 * per-frame closure allocation.
 */
export declare function detectCollisions3D<I extends BaseColliderInfo3D, C>(colliders: I[], count: number, workingMap: Map<number, I>, spatialIndex: SpatialIndex3D | undefined, onContact: (a: I, b: I, contact: Contact3D, context: C) => void, context: C): void;
