This page is the high-level map of how GridForge is put together.
If you only need one sentence, it is this:
GridForge is a deterministic voxel-grid system where GridWorld owns one
isolated world's runtime state, VoxelGrid owns per-grid state, Voxel is the
core mutable cell model, ScanCell accelerates occupant queries, and managers
plus tracers provide mutation and query workflows on top of that state.
GridForge is organized as a small set of cooperating layers:
| Layer | Main Types | Primary Responsibility |
|---|---|---|
| World coordination | GridWorld |
World lifecycle, registration, adaptive top-level lookup, world events |
| Configuration and identity | GridConfiguration, GridConfigurationKey, BoundsKey, ObstacleToken, OccupantTicket, VoxelIndex, WorldVoxelIndex |
Configuration and geometry values plus exact transient runtime identity |
| Per-grid storage | VoxelGrid, Voxel, ScanCell, dense/sparse storage strategies |
Core spatial data, local lookup, grid neighbors, occupancy and obstacle state |
| Mutation services | GridObstacleManager, GridOccupantManager, Blocker |
Safe state changes, events, and higher-level world-space mutations |
| Query services | GridScanManager, GridTracer |
Radius scans, filtered retrieval, line tracing, coverage enumeration |
| Extension and diagnostics | IVoxelOccupant, IVoxelPartition, PartitionProvider, GridForgeLogger, GridDiagnostics |
Domain integration, metadata hooks, logging, diagnostic cell projection, and dirty tracking |
| Path | Architectural Role |
|---|---|
src/GridForge/Configuration |
Grid creation inputs and bounds identity |
src/GridForge/Grids/Managers |
World orchestration and mutation/query manager APIs |
src/GridForge/Grids/Nodes |
Concrete runtime storage types: Voxel and ScanCell |
src/GridForge/Grids/Storage |
Dense and sparse physical voxel storage strategies |
src/GridForge/Grids/Topology |
Per-grid topology metrics, snapping, dimensions, and world/index projection |
src/GridForge/Grids/Support |
Pooled resources and event payload types |
src/GridForge/Spatial |
Shared coordinate, direction, occupant, and partition abstractions |
src/GridForge/Blockers |
World-space obstacle application on top of tracer coverage |
src/GridForge/Diagnostics |
Engine-agnostic diagnostic descriptors, topology geometry, and dirty adapter sessions |
src/GridForge/Support |
Cross-cutting query result groupings like GridVoxelSet |
src/GridForge/Utility |
Tracing and logging infrastructure |
Most operations in the library follow the same broad path:
world-space input
-> optional snap / normalize against a GridWorld
-> world-level candidate grid lookup
-> per-grid voxel or scan-cell lookup
-> query or mutation
-> version / event / cache updates
- active/inactive lifecycle for one world instance
- that world's optional ordinary-lookup cell size
- the active grid bucket
- exact-bounds duplicate tracking
- the adaptive index used for coarse grid lookup
- world versioning and grid-level events
- a process-unique runtime world token
- world-local grid generations
- active-gated allocation of process-unique obstacle registrations
- one grid's snapped bounds and dimensions
- its per-grid topology metrics
- its dense or sparse physical voxel storage
- its scan-cell overlay for configured voxels
- the set of active scan cells
- neighboring grid relationships
- per-grid obstacle and occupancy summary state
- per-grid versioning
- local and world-scoped identity
- cell-level occupancy state
- cell-level obstacle state
- attached partitions
- boundary awareness
- topology-aware neighbor query behavior
- a grid-local cell key
- occupant-plus-generation buckets grouped by voxel
- generation-aware tickets used for O(1) retrieval and stale-slot rejection
- fast "is there anything here?" state for scan-oriented queries
GridForge is not architected as "a single process-wide world." It is architected as "one or more isolated worlds, each of which may own many grids."
That is why GridWorld sits above everything else:
- it maps snapped bounds to a reusable world-local grid slot
- it maintains an adaptive coarse index for candidate grids
- it links neighboring grids when overlap is valid
- it resolves world-space and world-scoped voxel identities back to active runtime objects
GridConfiguration
-> GridWorld normalization and snapped bounds key
-> duplicate check
-> pooled VoxelGrid rent
-> VoxelGrid.Initialize(...)
-> topology-specific dimension calculation
-> dense or sparse physical voxel storage initialization
-> scan-cell storage for configured voxels
-> adaptive world-index registration
-> neighbor linking
-> world add notification
Grid topology is a per-grid strategy. GridConfiguration.TopologyKind selects
rectangular-prism or hex-prism cells, and GridConfiguration.TopologyMetrics
stores the deterministic cell geometry for that grid.
The topology layer owns:
- bounds normalization and snapping
- dimensions and topology-local index ranges
- world-position to
VoxelIndexprojection VoxelIndexto world-position projection- scan-cell key projection
- neighbor offsets and boundary ranges
Storage remains separate. Dense and sparse storage decide which physical voxels exist after topology has mapped world-space input to a local index or coverage range.
Hex-prism grids use axial XZ coordinates: VoxelIndex.x = q,
VoxelIndex.z = r, and VoxelIndex.y = layer. FlatTop and PointyTop change
only the fixed-point projection. Mixed rectangular/hex grids can live in one
GridWorld; direct mixed voxel bridging is exposed as a contact query over
world-space voxel footprint AABBs rather than as rectangular or hex direction
slots.
GridForge uses two different query scales:
Handled through the world's adaptive top-level index.
Handled locally through voxels and scan cells inside a VoxelGrid.
That split is one of the library's most important performance decisions.
| Mutation Type | Main Entry Point | State Touched |
|---|---|---|
| Obstacles | GridObstacleManager |
Process-unique voxel obstacle tokens/counts, grid obstacle count, grid version, events |
| Occupants | GridOccupantManager |
Voxel occupant counts, generation-aware scan-cell buckets, active scan cells, events |
| Region blockers | Blocker / BoundsBlocker / AreaBlocker |
Traced coverage across one or more grids, obstacle application/removal |
| Partitions | Voxel.TryAddPartition(...) |
Typed metadata or behavior attached directly to a voxel |
GridForge uses both events and version numbers to express change.
- grid add/remove/reset notifications inside a world
- grid change notifications after meaningful mutations
- voxel obstacle and occupant notifications
- blocker apply/remove notifications
- tracking world-level and grid-level mutation history
- helping dependent systems know when cached interpretations may be stale
- tagging voxel state with the grid version it was created or last synchronized against
GridWorld.OnChangeCommitted is the storage-neutral feed for systems that must
reconcile grid state without rereading mutable live objects. Every grid
lifecycle, sparse-presence, and obstacle mutation receives a monotonically
increasing world-local GridChangeStamp. The matching exact obstacle event and
generic committed event carry the same cause ID and immutable post-mutation
counts. Handlers run after the mutation locks are released, and reentrant
mutations are queued and delivered in ascending sequence order.
Consumers that install addressed metadata subscribe once to
GridWorld.OnChangeCommitted, then capture requested addresses with
TryCaptureNavigationBaseline(...) inside their deterministic maintenance
protocol. The request supplies the exact normalized GridConfigurationKey and
a strictly ascending, unique span of topology-local VoxelIndex values. The
baseline returns only those addresses, the exact active grid generation,
sparse presence, obstacle count, the grid-local GridLastChangeSequence, and
the world CapturedChangeSequence used by the feed. The grid's last-change
sequence changes only with that grid and lets a chunked consumer detect
target-grid mutation without restarting for unrelated world churn. Apply
baseline state first, then only matching committed events whose sequence is
greater than that captured change sequence. Neither baseline capture nor the
committed feed enumerates unrelated grids or unrequested physical voxels.
GridIndex is included for diagnostics; the world token, grid generation, and
normalized configuration key are the identity boundary.
Consumers that already own one committed-change subscription and reconcile at
a deterministic maintenance boundary can use
ExecuteNavigationMaintenanceSnapshot(...) to detach their fixed event prefix
and capture all required address baselines against one frozen world state. Keep
the callback short and non-mutating; it runs while GridForge excludes grid and
voxel mutations. TryCaptureNavigationBaseline(...) is safe inside that
callback and does not recurse through the world locks.
Exact cross-grid composition uses the same gate through
GridWorld.BeginBoundaryContacts(...) and
GridWorld.AdvanceBoundaryContacts(...). Grid registration maintains an exact
cell-prism-envelope BVH plus reciprocal sorted incident pair rows. A two-level
source bitset gives the cursor canonical pair discovery without scanning
recyclable grid slots, and target-envelope-derived address ranges avoid whole-
grid source scans. Upper/lower directory visits, pair selection, source
addresses, and exact target probes are all charged to the caller's candidate
budget. The cursor retains only scalar/value state between chunks and rejects
mixed committed generations as Stale.
The key-filtered begin path reads only the selected grid's reciprocal incident
rows, merging incoming then outgoing pairs in canonical order; row and pair
fetches remain separately probe-debited, independent of unrelated pair count.
Its value-only GridBoundaryContact output captures both canonical
configuration keys, and GridBoundaryContactRunStamp identifies the committed
world revision shared by a multi-cursor batch.
Bounded address-space coverage uses GridWorld.TryBeginCoveredAddresses(...)
and GridWorld.AdvanceCoveredAddresses(...). The caller supplies a canonical
list of exact grid generations and independent lookup, address, and output
ceilings. The cursor validates and copies that list before producing results,
then enumerates topology-configured addresses in deterministic X/Y/Z order.
This deliberately includes physically absent sparse addresses so higher layers
can apply their own semantic or materialization rules. Any world or grid
generation mismatch makes the entire run Stale; the cursor retains only
value state and supports allocation-free reuse after its fixed capacity is
constructed.
Neighbor handling is split into two related but distinct problems:
VoxelGridtracks same-topology neighboring grids by topology-local neighbor slot, and each slot can contain more than one grid.Voxel.GetNeighborsInto(...)asks which physical voxels touch the source voxel, withVoxelNeighborScopeselecting source-grid, same-topology grid, mixed-topology grid, or all contacts.Voxel.TryGetNeighbor(...)exposes exact directed lookup throughRectangularDirectionandHexDirectionoverloads.- Rectangular full-neighbor lookup covers 26 directions. Hex full-neighbor
lookup covers 20 directions, with
Primary,Planar,Vertical, layer, and vertical-diagonal subsets exposed through the direction utilities. - Hex direction names describe axial offsets (
QPositive,RNegative, etc.) rather than world compass directions so pointy-top and flat-top grids share one unambiguous API. Voxel.GetRectangularNeighborsInto(...)andVoxel.GetHexNeighborsInto(...)fill caller-owned storage with direction-labeled same-topology results.
VoxelGrid.Neighbors remains a same-topology grid-slot accelerator, but public
contact discovery is resolved by VoxelNeighborResolver. Contact queries use
the world's adaptive index, derive a topology-aware candidate range per target
grid, and final-filter by fixed-point AABB overlap. This avoids ambiguous
direction slots, keeps sparse target grids configured-only, and reflects grid
load/unload or sparse mutation without per-voxel neighbor caches.
GridTracer is the architectural bridge between world-space geometry and
cell-level data.
It turns:
- lines into voxel coverage
- bounds into voxel coverage
- bounds into scan-cell coverage
That same utility underpins blockers, custom coverage queries, and scan-region enumeration.
Rectangular coverage uses rectangular index ranges. Hex-prism line tracing uses axial/cube interpolation and deterministic rounding; hex bounds coverage uses a conservative candidate range followed by cell-center reach checks. Callers still use the same tracer APIs for both topologies.