Double Precision

Double Precision

This SDK is designed to handle coordinate systems spanning hundreds or thousands of kilometers. At this scale, standard 32-bit floating-point numbers (used by most game engines, including Bevy) lack sufficient precision to represent small local offsets, leading to “jittering” in rendered objects far from the origin.

To solve this, the SDK implements a double-precision transform system combined with a floating origin technique.

Transform64

Transform64 is the high-precision counterpart to Bevy’s Transform. It uses 64-bit floats (f64) for translation and double-precision quaternions for rotation.

pub struct Transform64 {
    pub translation: DVec3,
    pub rotation: DQuat,
    pub scale: DVec3,
}

When you add a Transform64 to an entity, the propagation system automatically calculates the correct world-space position. Since GPUs are optimized for 32-bit floats, we do not render in 64-bit space directly. Instead, we use a Floating Origin.

Floating Origin

The Floating Origin is a global DVec3 resource that defines the “center” of the 32-bit world. Typically, this origin is kept near the active camera.

As the camera moves through the 64-bit world, the SDK periodically updates the Floating Origin. By keeping the Floating Origin close to the camera, the relative distance to visible objects remains small enough to be accurately represented by 32-bit floats, eliminating jitter.

Usage in Plugins

In most cases, you should use Transform64 for any object that has a fixed position in the world.

Spawning an Entity

commands.spawn((
    Transform64::from_xyz(1234567.89, 9876543.21, 100.0),
    // ... other components
));

Moving an Object

fn move_system(mut query: Query<&mut Transform64>) {
    for mut transform in query.iter_mut() {
        transform.translation.x += 1.0;
    }
}

Technical Details

  • GlobalTransform64: Similar to Bevy’s GlobalTransform, this component stores the absolute world-space transform after parent-child propagation.

  • Update Pipeline: The FloatingOriginPlugin manages the propagation of 64-bit transforms. It runs in the PostUpdate and PostStartup schedules, specifically within the TransformSystem::TransformPropagate set. This ensures that 64-bit world positions are always computed before they are converted to 32-bit relative positions for rendering. Every frame, the system calculates the relative position of every object:

    $$ \text{Local 32-bit Position} = \text{World 64-bit Position} - \text{Floating Origin} $$

    This computation happens in the transform_64_propagation system, which runs after parent-child propagation. It updates Bevy’s standard GlobalTransform (f32) with values relative to the current FloatingOrigin.

  • Custom Frontends: While the SDK provides Transform64 as a high-level API, you can put your own frontend in front of GlobalTransform64. If your application has specific coordinate needs (e.g., specialized GIS coordinates), you can compute GlobalTransform64 yourself and the floating origin system will still correctly handle the 32-bit relative rendering.

  • Precision Guard: Using Transform64 ensures that even if your project is located at real-world coordinates (e.g., UTM coordinates in the millions), models will render smoothly.

Custom Semantics

The separation between Transform64 and GlobalTransform64 allows you to add your own semantics to the transform system. Because the floating origin system only cares about the final GlobalTransform64 when computing the relative f32 GlobalTransform for Bevy’s renderer, you can bypass the standard propagation by manually managing GlobalTransform64.

This is particularly useful for:

  • Implementing custom parent-child relationships.
  • Mapping high-precision GIS coordinates directly to world space.
  • Handling complex movement logic where f32 precision is insufficient even for local offsets.