Field Attribute & Schema Integration
Declare a component field as spatially indexed, validated against schema rules the moment the component is registered.
Status: โ Implemented ยท Visibility: Public ยท Level: ๐ต Core ยท Category: Spatial
๐ฏ What it solves
A spatial index needs to know, before a single entity exists, which field holds an entity's bounds, how much
slack to give that field before a tree mutation is needed, whether it belongs in the static or dynamic tree, and
whether a coarse occupancy filter is worth maintaining. Wiring this up imperatively โ manual tree construction,
manual field-offset bookkeeping, ad-hoc checks scattered through game code โ is exactly the kind of boilerplate
Typhon's schema reflection already removes for secondary indexes and foreign keys. [SpatialIndex] extends that
same declarative path to spatial fields, and pushes every configuration mistake to startup instead of runtime.
โ๏ธ How it works (in brief)
Decorate one field of a supported geometry type (AABB2F/AABB3F/BSphere2F/BSphere3F, plus their f64
equivalents) with [SpatialIndex(margin, cellSize)], optionally setting Mode and Category. At component
registration the engine checks the field's type, the component's storage mode, and that no second
[SpatialIndex] field exists on the struct โ any violation throws immediately, before the schema is built. On
success, the attribute's values flow into the component's DBComponentDefinition.SpatialField and into the
runtime state that backs the R-Tree, the back-pointer segment, and โ when cellSize > 0 โ the Layer-1
occupancy hashmap.
๐ป Usage
[Component("Game.Ship", revision: 1, StorageMode = StorageMode.SingleVersion)]
public struct ShipComponent
{
[Field] public String64 Name;
// margin/cellSize are constructor args; Mode/Category are named properties.
[Field] [SpatialIndex(margin: 5.0f, Mode = SpatialMode.Dynamic, Category = 1u << 2)]
public AABB3F Bounds;
}
dbe.RegisterComponentFromAccessor<ShipComponent>(); // throws here on any violation, not later
// Inspecting the reflected metadata:
DBComponentDefinition def = dbe.DBD.GetComponent("Game.Ship", revision: 1);
DBComponentDefinition.Field spatial = def.SpatialField;
float margin = spatial.SpatialMargin;
SpatialFieldType type = spatial.SpatialFieldType; // AABB3F
[SpatialIndex] arg |
Default | Effect |
|---|---|---|
margin (ctor) |
required | Fat-AABB enlargement in world units โ see Spatial R-Tree Index |
cellSize (ctor) |
0 |
0 = no Layer-1 hashmap; >0 enables a coarse occupancy filter at that cell size |
Mode |
SpatialMode.Dynamic |
Static bulk-loads once and is skipped by per-tick/commit maintenance; Dynamic gets fat-AABB updates |
Category |
uint.MaxValue |
Archetype-level bitmask consumed by the cluster broadphase to skip whole clusters |
โ ๏ธ Guarantees & limits
- Exactly 8 supported field types:
AABB2F/AABB3F/BSphere2F/BSphere3Fand theirf64(...D) equivalents โ any other field type throwsInvalidOperationExceptionat registration. - At most one
[SpatialIndex]field per component โ a second one throws at registration, not at first use. marginmust be โฅ 0 โ a negative value throws at registration.- Not supported on
StorageMode.Transientโ registering a[SpatialIndex]field on a Transient component throwsInvalidOperationException. - Validation runs once, at
RegisterComponentFromAccessor/RegisterComponentByTypetime, before anyUnitOfWorkexists โ by the time the schema is usable, the spatial configuration is already known-good. BSphere*fields are accepted directly โ the engine converts to an enclosing AABB internally for indexing; the component's stored field stays a sphere, unchanged.- The attribute only declares configuration; it does not itself maintain the tree โ see the sub-feature below for when the tree picks up a write, and Spatial R-Tree Index for the index structure it configures.
๐งช Tests
- SpatialFieldTypeTests โ
[SpatialIndex]reflection (margin/cellSize),FieldType.FromTypemapping for all 8 supported types,SpatialFieldInfo.ToVariant/IsSphere - SpatialEcsIntegrationTests โ schema validation at registration (
Schema_TransientWithSpatialIndex_Throws,Schema_ValidSpatialField_CreatesSpatialIndex,Schema_NoSpatialField_NullSpatialIndex,Schema_CellSizeZero_NoHashmap)
๐ Related
- Source: src/Typhon.Engine/Spatial/internals/SpatialIndexState.cs, src/Typhon.Engine/Spatial/public/SpatialFieldInfo.cs, src/Typhon.Schema.Definition/Attributes.cs
- Sub-features: Storage-Mode Compatibility (SingleVersion / Versioned)
- Sibling: Spatial R-Tree Index โ the index structure this attribute configures
- Overview: Spatial Architecture Overview โ how this fits with the separate spatial grid