Expand description
This bevy
plugin makes it possible to build high-precision worlds that exceed the size of
the observable universe, with no added dependencies, while remaining largely compatible with the
rest of the Bevy ecosystem.
§Problem
Objects far from the origin suffer from reduced precision, causing rendered meshes to jitter and jiggle, and transformation calculations to encounter catastrophic cancellation.
As the camera moves farther from the origin, the scale of floats needed to describe the position of meshes and the camera get larger, which in turn means there is less precision available. Consequently, when the matrix math is done to compute the position of objects in view space, mesh vertices will be displaced due to this lost precision.
§Solution
While using the FloatingOriginPlugin
, the position of entities is now defined with the
ReferenceFrame
, GridCell
, and Transform
components. The ReferenceFrame
is a large
integer grid of cells; entities are located within this grid using the GridCell
component.
Finally, the Transform
is used to position the entity relative to the center of its
GridCell
. If an entity moves into a neighboring cell, its transform will be automatically
recomputed relative to the center of that new cell. This prevents Transforms
from ever
becoming larger than a single grid cell, and thus prevents floating point precision artifacts.
ReferenceFrame
s can also be nested. This allows you to define moving reference frames, which
can make certain use cases much simpler. For example, if you have a planet rotating, and
orbiting around its star, it would be very annoying if you had to compute this orbit and
rotation for all object on the surface in high precision. Instead, you can place the planet and
all objects on its surface in the same reference frame. The motion of the planet will be
inherited by all children in that reference frame, in high precision. Entities at the root of
the hierarchy will be implicitly placed in the RootReferenceFrame
.
The above steps are also applied to the entity marked with the FloatingOrigin
component. The
only difference is that the GridCell
of the floating origin is used when computing the
GlobalTransform
of all other entities. To an outside observer, as the floating origin camera
moves through space and reaches the limits of its GridCell
, it would appear to teleport to the
opposite side of the cell, similar to the spaceship in the game Asteroids.
The GlobalTransform
of all entities is computed relative to the floating origin’s grid cell.
Because of this, entities very far from the origin will have very large, imprecise positions.
However, this is always relative to the camera (floating origin), so these artifacts will always
be too far away to be seen, no matter where the camera moves. Because this only affects the
GlobalTransform
and not the Transform
, this also means that entities will never permanently
lose precision just because they were far from the origin at some point. The lossy calculation
only occurs when computing the GlobalTransform
of entities, the high precision GridCell
and
Transform
are unaffected.
§Getting Started
To start using this plugin:
- Disable Bevy’s transform plugin:
DefaultPlugins.build().disable::<TransformPlugin>()
- Add the
FloatingOriginPlugin
to yourApp
- Add the
GridCell
component to all spatial entities - Add the
FloatingOrigin
component to the active camera - Add the
IgnoreFloatingOrigin
component
Take a look at ReferenceFrame
component for some useful helper methods.
§Moving Entities
For the most part, you can update the position of entities normally while using this plugin, and it will automatically handle the tricky bits. However, there is one big caveat:
Avoid setting position absolutely, instead prefer applying a relative delta
Instead of:
transform.translation = a_huge_imprecise_position;
do:
let delta = new_pos - old_pos;
transform.translation += delta;
§Absolute Position
If you are updating the position of an entity with absolute positions, and the position exceeds the bounds of the entity’s grid cell, the floating origin plugin will recenter that entity into its new cell. Every time you update that entity, you will be fighting with the plugin as it constantly recenters your entity. This can especially cause problems with camera controllers which may not expect the large discontinuity in position as an entity moves between cells.
The other reason to avoid this is you will likely run into precision issues! This plugin exists because single precision is limited, and the larger the position coordinates get, the less precision you have.
However, if you have something that must not accumulate error, like the orbit of a planet, you
can instead do the orbital calculation (position as a function of time) to compute the absolute
position of the planet with high precision, then directly compute the GridCell
and
Transform
of that entity using ReferenceFrame::translation_to_grid
. If the star
this planet is orbiting around is also moving through space, note that you can add/subtract grid
cells. This means you can do each calculation in the reference frame of the moving body, and sum
up the computed translations and grid cell offsets to get a more precise result.
Re-exports§
pub use grid_cell::GridCell;
pub use propagation::IgnoreFloatingOrigin;
Modules§
- Provides a camera controller compatible with the floating origin plugin.
- Contains tools for debugging the floating origin.
- Contains the grid cell implementation
- Contains the
GridPrecision
trait and its implementations. - Propagates transforms through the entity hierarchy.
- Adds the concept of hierarchical, nesting
ReferenceFrame
s, to group entities that move through space together, like entities on a planet, rotating about the planet’s axis, and, orbiting a star.
Structs§
- Marks the entity to use as the floating origin. All other entities will be positioned relative to this entity’s
GridCell
. - Add this plugin to your
App
for floating origin functionality. - Minimal bundle needed to position an entity in floating origin space.
Functions§
- If an entity’s transform becomes larger than the specified limit, it is relocated to the nearest grid cell to reduce the size of the transform.
- Update the
GlobalTransform
of entities with aGridCell
, using theReferenceFrame
the entity belongs to.