2.8 KiB
0008. Split-coordinate entity positions
- Status: Accepted
- Date: 2026-07-07
Context
In a procedurally generated voxel world, you simply cannot represent an entity's position using a single, global, single-precision floating-point vector (f32 or Vec3). As you move further away from the origin, the physical spacing between representable floating-point numbers inherently increases. Eventually, this leads to aggressive spatial jitter, physics instability, and horrible rendering artifacts.
We could postpone this issue by switching to double-precision floats (f64), but that comes with serious downsides. It doubles our data size and isn't uniformly or natively supported on GPUs, which overwhelmingly expect f32 for vertices and transforms.
We need a spatial representation that perfectly maintains sub-millimeter precision universally across a theoretically unbounded world, without coupling our core simulation state directly to GPU limitations or paying the heavy cost of f64 everywhere.
Decision
The engine strictly uses a split-coordinate representation for entity positions, entirely encapsulated by the EntityPos type. An entity's absolute position is defined by two discrete components:
- A
chunkanchor (ChunkPos): The integer coordinates of the exact chunk that currently contains the entity. - A
localoffset (Vec3): A standard single-precision floating-point vector that describes the entity's exact position relative to the chunk's minimum corner.
When an entity moves, we apply that movement strictly to the local offset. Afterward, a normalization step (EntityPos::renormalize) checks if the offset overflowed beyond the chunk's boundaries. If it did, it carries that overflow directly into the integer chunk anchor, guaranteeing that the local offset always remains strictly within the bounding box of a single chunk ([0.0, CHUNK_SIZE)).
Consequences
- Uniform Precision: Entities maintain exact
f32precision regardless of how far they travel from the world origin, purely because the active floating-point magnitude is strictly bounded by the size of a single chunk. - Rendering Stability: The renderer can safely compute relative matrices by temporarily defining the camera's current chunk as the absolute origin. This allows the GPU to process all vertex data and transforms in standard
f32without any spatial jitter whatsoever. - Math Complexity: Code that manipulates spatial positions (like physics integration and distance checks) inherently becomes more complex. You can no longer just subtract two global vectors to get a distance; your logic must handle both the chunk offset and the local offset simultaneously.
- Serialization:
EntityPossafely serializes as a compound struct, ensuring that our save files never lose coordinate precision for extremely distant entities.