ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
99 lines (98 loc) • 4.17 kB
TypeScript
/**
* Spatial Index 3D Plugin for ECSpresso
*
* Provides a uniform-grid spatial hash for broadphase collision detection
* and proximity queries in 3D. Rebuilds the grid each frame from entity
* transforms. Replaces O(n²) brute-force with O(n·d) where d = local density.
*
* Standalone usage: queryBox / queryRadius for proximity queries.
* Automatic acceleration: collision3D and physics3D plugins detect the
* spatialIndex3D resource at runtime and use it for broadphase when present.
*/
import type { Transform3DComponentTypes } from './transform3D';
import { type SpatialIndex3D } from '../../utils/spatial-hash3D';
/**
* 3D axis-aligned bounding box collider component.
* Defined here (spatial layer) so collision3D can import rather than redefine.
*/
export interface AABB3DCollider {
width: number;
height: number;
depth: number;
offsetX?: number;
offsetY?: number;
offsetZ?: number;
}
/**
* Sphere collider component.
* Defined here (spatial layer) so collision3D can import rather than redefine.
*/
export interface SphereCollider {
radius: number;
offsetX?: number;
offsetY?: number;
offsetZ?: number;
}
export interface Spatial3DColliderComponentTypes {
aabb3DCollider: AABB3DCollider;
sphereCollider: SphereCollider;
}
export interface SpatialIndex3DResourceTypes {
spatialIndex3D: SpatialIndex3D;
}
type SpatialIndex3DComponentTypes = Transform3DComponentTypes & Spatial3DColliderComponentTypes;
export type SpatialIndex3DPhase = 'fixedUpdate' | 'postUpdate';
export interface SpatialIndex3DPluginOptions<G extends string = 'spatialIndex3D'> {
/** Cell size for the spatial hash grid (default: 64) */
cellSize?: number;
/** System group name (default: 'spatialIndex3D') */
systemGroup?: G;
/** Priority for rebuild systems (default: 2000, before collision) */
priority?: number;
/**
* Phases to register rebuild systems in (default: ['fixedUpdate', 'postUpdate']).
*
* When both phases are registered, the `postUpdate` rebuild is
* automatically skipped on cycles where:
* 1. The `fixedUpdate` rebuild already ran this cycle, AND
* 2. No entity hierarchy exists.
*
* In flat-hierarchy scenes `transform3d-propagation` copies
* `localTransform3D → worldTransform3D` unchanged, so the postUpdate
* grid would duplicate the fixedUpdate one. The skip is automatic
* and re-engages once any parent relationship is set, or whenever
* `fixedUpdate` doesn't run that cycle (sub-fixed-DT frames).
*
* Edge case: if you write to `worldTransform3D` directly between
* phases without using `transform3d-propagation` or entity hierarchy,
* the auto-skip will leave your writes unindexed. Set
* `phases: ['postUpdate']` explicitly to bypass the auto-skip.
*/
phases?: ReadonlyArray<SpatialIndex3DPhase>;
}
/**
* Create a 3D spatial index plugin for ECSpresso.
*
* Provides a uniform-grid spatial hash that accelerates 3D collision detection.
* When installed alongside the collision3D or physics3D plugins, they
* automatically use the spatial index for broadphase instead of O(n²)
* brute-force.
*
* Also provides proximity query methods for game logic (e.g. "find all
* enemies within 200 units").
*
* @example
* ```typescript
* const ecs = ECSpresso.create()
* .withPlugin(createTransform3DPlugin())
* .withPlugin(createCollision3DPlugin({ layers }))
* .withPlugin(createSpatialIndex3DPlugin({ cellSize: 128 }))
* .build();
*
* // Proximity query in a system:
* const si = ecs.getResource('spatialIndex3D');
* const nearby = si.queryRadius(playerX, playerY, playerZ, 200);
* ```
*/
export declare function createSpatialIndex3DPlugin<G extends string = 'spatialIndex3D'>(options?: SpatialIndex3DPluginOptions<G>): import("ecspresso").Plugin<import("ecspresso").WithResources<import("ecspresso").WithComponents<import("ecspresso").EmptyConfig, SpatialIndex3DComponentTypes>, SpatialIndex3DResourceTypes>, import("ecspresso").EmptyConfig, "spatial-index3D-rebuild-fixedUpdate" | "spatial-index3D-rebuild-postUpdate", G, never, never>;
export {};