Crate big_space

source ·
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.

ReferenceFrames 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:

  1. Disable Bevy’s transform plugin: DefaultPlugins.build().disable::<TransformPlugin>()
  2. Add the FloatingOriginPlugin to your App
  3. Add the GridCell component to all spatial entities
  4. Add the FloatingOrigin component to the active camera
  5. 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§

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 ReferenceFrames, to group entities that move through space together, like entities on a planet, rotating about the planet’s axis, and, orbiting a star.
  • A helper query argument that ensures you don’t forget to handle the GridCell when you work with a Transform.

Structs§

Functions§