mirror of
https://github.com/eliasstepanik/big_space_with_trim.git
synced 2026-01-16 04:38:28 +00:00
Final tidying
This commit is contained in:
parent
280a493dcb
commit
19d6670145
@ -8,7 +8,6 @@ description = "A floating origin plugin for bevy"
|
||||
|
||||
[dependencies]
|
||||
bevy = { version = "0.9", default_features = false }
|
||||
bevy_rapier3d = { version = "0.19", optional = true }
|
||||
bevy_polyline = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
@ -17,7 +16,3 @@ bevy = { version = "0.9", default_features = false, features = [
|
||||
"bevy_winit",
|
||||
"x11",
|
||||
] }
|
||||
|
||||
|
||||
[features]
|
||||
rapier = ["bevy_rapier3d"]
|
||||
|
||||
135
examples/debug.rs
Normal file
135
examples/debug.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use bevy::prelude::*;
|
||||
use big_space::{FloatingOrigin, GridCell};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.build().disable::<TransformPlugin>())
|
||||
.add_plugin(big_space::FloatingOriginPlugin::<i64>::new(0.5, 0.01))
|
||||
.add_plugin(big_space::debug::FloatingOriginDebugPlugin::<i64>::default())
|
||||
.insert_resource(ClearColor(Color::BLACK))
|
||||
.add_startup_system(setup)
|
||||
.add_system(movement)
|
||||
.add_system(rotation)
|
||||
.run()
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Mover<const N: usize>;
|
||||
|
||||
fn movement(
|
||||
time: Res<Time>,
|
||||
mut q: ParamSet<(
|
||||
Query<&mut Transform, With<Mover<1>>>,
|
||||
Query<&mut Transform, With<Mover<2>>>,
|
||||
Query<&mut Transform, With<Mover<3>>>,
|
||||
)>,
|
||||
) {
|
||||
let delta_translation = |offset: f32| -> Vec3 {
|
||||
let t_1 = time.elapsed_seconds() + offset;
|
||||
let dt = time.delta_seconds();
|
||||
let t_0 = t_1 - dt;
|
||||
let pos =
|
||||
|t: f32| -> Vec3 { Vec3::new(t.cos() * 2.0, t.sin() * 2.0, (t * 1.3).sin() * 2.0) };
|
||||
let p0 = pos(t_0);
|
||||
let p1 = pos(t_1);
|
||||
let dp = p1 - p0;
|
||||
dp
|
||||
};
|
||||
|
||||
q.p0().single_mut().translation += delta_translation(20.0);
|
||||
q.p1().single_mut().translation += delta_translation(251.0);
|
||||
q.p2().single_mut().translation += delta_translation(812.0);
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Rotator;
|
||||
|
||||
fn rotation(time: Res<Time>, mut query: Query<&mut Transform, With<Rotator>>) {
|
||||
for mut transform in &mut query {
|
||||
transform.rotate_x(3.0 * time.delta_seconds());
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
let mesh_handle = meshes.add(
|
||||
shape::Icosphere {
|
||||
radius: 0.1,
|
||||
..default()
|
||||
}
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
let matl_handle = materials.add(StandardMaterial {
|
||||
base_color: Color::YELLOW,
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
mesh: mesh_handle.clone(),
|
||||
material: matl_handle.clone(),
|
||||
transform: Transform::from_xyz(0.0, 0.0, 1.0),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
Mover::<1>,
|
||||
));
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
mesh: mesh_handle.clone(),
|
||||
material: matl_handle.clone(),
|
||||
transform: Transform::from_xyz(1.0, 0.0, 0.0),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
Mover::<2>,
|
||||
));
|
||||
commands
|
||||
.spawn((
|
||||
PbrBundle {
|
||||
mesh: mesh_handle.clone(),
|
||||
material: matl_handle.clone(),
|
||||
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
Rotator,
|
||||
Mover::<3>,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn(PbrBundle {
|
||||
mesh: mesh_handle,
|
||||
material: matl_handle,
|
||||
transform: Transform::from_xyz(0.0, 0.0, 1.0),
|
||||
..default()
|
||||
});
|
||||
});
|
||||
|
||||
// light
|
||||
commands.spawn((
|
||||
PointLightBundle {
|
||||
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
||||
point_light: PointLight {
|
||||
intensity: 10_000f32,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
));
|
||||
|
||||
// camera
|
||||
commands.spawn((
|
||||
Camera3dBundle {
|
||||
transform: Transform::from_xyz(0.0, 0.0, 8.0)
|
||||
.looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
FloatingOrigin,
|
||||
));
|
||||
}
|
||||
190
examples/demo.rs
190
examples/demo.rs
@ -1,127 +1,27 @@
|
||||
use bevy::prelude::*;
|
||||
use big_space::{FloatingOrigin, GridCell};
|
||||
use bevy::{
|
||||
math::Vec3A,
|
||||
prelude::*,
|
||||
render::primitives::{Aabb, Sphere},
|
||||
};
|
||||
use big_space::{camera::CameraController, FloatingOrigin, GridCell};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.build().disable::<TransformPlugin>())
|
||||
.add_plugin(big_space::FloatingOriginPlugin::<i64>::new(0.5, 0.01))
|
||||
.add_plugin(big_space::debug::FloatingOriginDebugPlugin::<i64>::default())
|
||||
.add_plugin(big_space::FloatingOriginPlugin::<i128>::default())
|
||||
.add_plugin(big_space::debug::FloatingOriginDebugPlugin::<i128>::default())
|
||||
.add_plugin(big_space::camera::CameraControllerPlugin)
|
||||
.insert_resource(ClearColor(Color::BLACK))
|
||||
.add_startup_system(setup)
|
||||
.add_system(movement)
|
||||
.add_system(rotation)
|
||||
.add_system(cursor_grab_system)
|
||||
.run()
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Mover<const N: usize>;
|
||||
|
||||
fn movement(
|
||||
time: Res<Time>,
|
||||
mut q: ParamSet<(
|
||||
Query<&mut Transform, With<Mover<1>>>,
|
||||
Query<&mut Transform, With<Mover<2>>>,
|
||||
Query<&mut Transform, With<Mover<3>>>,
|
||||
)>,
|
||||
) {
|
||||
let delta_translation = |offset: f32| -> Vec3 {
|
||||
let t_1 = time.elapsed_seconds() + offset;
|
||||
let dt = time.delta_seconds();
|
||||
let t_0 = t_1 - dt;
|
||||
let pos =
|
||||
|t: f32| -> Vec3 { Vec3::new(t.cos() * 2.0, t.sin() * 2.0, (t * 1.3).sin() * 2.0) };
|
||||
let p0 = pos(t_0);
|
||||
let p1 = pos(t_1);
|
||||
let dp = p1 - p0;
|
||||
dp
|
||||
};
|
||||
|
||||
q.p0().single_mut().translation += delta_translation(20.0);
|
||||
q.p1().single_mut().translation += delta_translation(251.0);
|
||||
q.p2().single_mut().translation += delta_translation(812.0);
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Rotator;
|
||||
|
||||
fn rotation(time: Res<Time>, mut query: Query<&mut Transform, With<Rotator>>) {
|
||||
for mut transform in &mut query {
|
||||
transform.rotate_x(3.0 * time.delta_seconds());
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
let mesh_handle = meshes.add(
|
||||
shape::Icosphere {
|
||||
radius: 0.1,
|
||||
..default()
|
||||
}
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
let matl_handle = materials.add(StandardMaterial {
|
||||
base_color: Color::YELLOW,
|
||||
..default()
|
||||
});
|
||||
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
mesh: mesh_handle.clone(),
|
||||
material: matl_handle.clone(),
|
||||
transform: Transform::from_xyz(0.0, 0.0, 1.0),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
Mover::<1>,
|
||||
));
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
mesh: mesh_handle.clone(),
|
||||
material: matl_handle.clone(),
|
||||
transform: Transform::from_xyz(1.0, 0.0, 0.0),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
Mover::<2>,
|
||||
));
|
||||
commands
|
||||
.spawn((
|
||||
PbrBundle {
|
||||
mesh: mesh_handle.clone(),
|
||||
material: matl_handle.clone(),
|
||||
transform: Transform::from_xyz(0.0, 1.0, 0.0),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
Rotator,
|
||||
Mover::<3>,
|
||||
))
|
||||
.with_children(|parent| {
|
||||
parent.spawn(PbrBundle {
|
||||
mesh: mesh_handle,
|
||||
material: matl_handle,
|
||||
transform: Transform::from_xyz(0.0, 0.0, 1.0),
|
||||
..default()
|
||||
});
|
||||
});
|
||||
|
||||
// light
|
||||
commands.spawn((
|
||||
PointLightBundle {
|
||||
transform: Transform::from_xyz(4.0, 8.0, 4.0),
|
||||
point_light: PointLight {
|
||||
intensity: 10_000f32,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
));
|
||||
|
||||
// camera
|
||||
commands.spawn((
|
||||
Camera3dBundle {
|
||||
@ -129,7 +29,73 @@ fn setup(
|
||||
.looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y),
|
||||
..default()
|
||||
},
|
||||
GridCell::<i64>::default(),
|
||||
FloatingOrigin,
|
||||
GridCell::<i128>::default(), // All spatial entities need this component
|
||||
FloatingOrigin, // Important: marks this as the entity to use as teh floating origin
|
||||
CameraController {
|
||||
max_speed: 10e12,
|
||||
smoothness: 0.9,
|
||||
..default()
|
||||
}, // Built-in camera controller
|
||||
));
|
||||
|
||||
let mesh_handle = meshes.add(
|
||||
shape::Icosphere {
|
||||
radius: 0.5,
|
||||
subdivisions: 16,
|
||||
}
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
let matl_handle = materials.add(StandardMaterial {
|
||||
base_color: Color::YELLOW,
|
||||
unlit: false,
|
||||
..default()
|
||||
});
|
||||
|
||||
let mut translation = Vec3::ZERO;
|
||||
for i in 1..100i128 {
|
||||
let j = i.pow(10) as f32 + 1.0;
|
||||
translation.x += j;
|
||||
commands.spawn((
|
||||
PbrBundle {
|
||||
mesh: mesh_handle.clone(),
|
||||
material: matl_handle.clone(),
|
||||
transform: Transform::from_scale(Vec3::splat(j)).with_translation(translation),
|
||||
..default()
|
||||
},
|
||||
Aabb::from(Sphere {
|
||||
center: Vec3A::ZERO,
|
||||
radius: j / 2.0,
|
||||
}),
|
||||
GridCell::<i128>::default(),
|
||||
));
|
||||
}
|
||||
|
||||
// light
|
||||
commands.spawn((DirectionalLightBundle {
|
||||
directional_light: DirectionalLight {
|
||||
illuminance: 100_000.0,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
},));
|
||||
}
|
||||
|
||||
fn cursor_grab_system(
|
||||
mut windows: ResMut<Windows>,
|
||||
btn: Res<Input<MouseButton>>,
|
||||
key: Res<Input<KeyCode>>,
|
||||
) {
|
||||
let window = windows.get_primary_mut().unwrap();
|
||||
|
||||
use bevy::window::CursorGrabMode;
|
||||
if btn.just_pressed(MouseButton::Left) {
|
||||
window.set_cursor_grab_mode(CursorGrabMode::Locked);
|
||||
window.set_cursor_visibility(false);
|
||||
}
|
||||
|
||||
if key.just_pressed(KeyCode::Escape) {
|
||||
window.set_cursor_grab_mode(CursorGrabMode::None);
|
||||
window.set_cursor_visibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ fn main() {
|
||||
///
|
||||
/// This plugin can function much further from the origin without any issues. Try setting this to:
|
||||
/// 10_000_000_000_000_000_000_000_000_000_000_000_000
|
||||
const DISTANCE: f32 = 20_000_000.0;
|
||||
const DISTANCE: f32 = 10_000_000.0;
|
||||
|
||||
/// Move the floating origin back to the "true" origin when the user presses the spacebar to emulate
|
||||
/// disabling the plugin. Normally you would make your active camera the floating origin to avoid
|
||||
|
||||
146
src/camera.rs
Normal file
146
src/camera.rs
Normal file
@ -0,0 +1,146 @@
|
||||
//! Provides a camera controller compatible with the floating origin plugin.
|
||||
|
||||
use bevy::{
|
||||
input::mouse::MouseMotion, prelude::*, render::primitives::Aabb, transform::TransformSystem,
|
||||
utils::HashMap,
|
||||
};
|
||||
|
||||
/// Adds the `big_space` camera controller
|
||||
pub struct CameraControllerPlugin;
|
||||
impl Plugin for CameraControllerPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<CameraInput>()
|
||||
.add_system(default_camera_inputs.before(camera_controller))
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
camera_controller.after(TransformSystem::TransformPropagate),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-camera settings for the `big_space` floating origin camera controller.
|
||||
#[derive(Clone, Debug, Reflect, Component)]
|
||||
pub struct CameraController {
|
||||
/// Smoothness of motion, from `0.0` to `1.0`.
|
||||
pub smoothness: f32,
|
||||
/// Maximum possible speed.
|
||||
pub max_speed: f32,
|
||||
/// Whether the camera should slow down when approaching an entity's [`Aabb`].
|
||||
pub slow_near_objects: bool,
|
||||
}
|
||||
impl Default for CameraController {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
smoothness: 0.2,
|
||||
max_speed: 10e8,
|
||||
slow_near_objects: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Input state used to command camera motion. Reset every time the values are read to update the
|
||||
/// camera. Allows you to map any input to camera motions. Uses aircraft principle axes conventions.
|
||||
#[derive(Clone, Debug, Default, Reflect, Resource)]
|
||||
pub struct CameraInput {
|
||||
/// Z-negative
|
||||
pub forward: f32,
|
||||
/// Y-positive
|
||||
pub up: f32,
|
||||
/// X-positive
|
||||
pub right: f32,
|
||||
/// Positive = right wing down
|
||||
pub roll: f32,
|
||||
/// Positive = nose up
|
||||
pub pitch: f32,
|
||||
/// Positive = nose right
|
||||
pub yaw: f32,
|
||||
}
|
||||
|
||||
impl CameraInput {
|
||||
/// Reset the controller back to zero to ready fro the next frame.
|
||||
pub fn reset(&mut self) {
|
||||
*self = CameraInput::default();
|
||||
}
|
||||
|
||||
/// Returns the desired velocity transform.
|
||||
pub fn target_velocity(&self, speed: f32, dt: f32) -> Transform {
|
||||
let mut new_transform = Transform::from_rotation(Quat::from_euler(
|
||||
EulerRot::XYZ,
|
||||
self.pitch * dt,
|
||||
self.yaw * dt,
|
||||
self.roll * dt,
|
||||
));
|
||||
|
||||
let delta = Vec3::new(self.right, self.up, self.forward) * speed * dt;
|
||||
|
||||
new_transform.translation = delta;
|
||||
new_transform
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides sensible keyboard and mouse input defaults
|
||||
pub fn default_camera_inputs(
|
||||
keyboard: Res<Input<KeyCode>>,
|
||||
mut mouse_move: EventReader<MouseMotion>,
|
||||
mut cam: ResMut<CameraInput>,
|
||||
) {
|
||||
keyboard.pressed(KeyCode::W).then(|| cam.forward -= 1.0);
|
||||
keyboard.pressed(KeyCode::S).then(|| cam.forward += 1.0);
|
||||
keyboard.pressed(KeyCode::A).then(|| cam.right -= 1.0);
|
||||
keyboard.pressed(KeyCode::D).then(|| cam.right += 1.0);
|
||||
keyboard.pressed(KeyCode::Space).then(|| cam.up += 1.0);
|
||||
keyboard.pressed(KeyCode::LControl).then(|| cam.up -= 1.0);
|
||||
keyboard.pressed(KeyCode::Q).then(|| cam.roll += 1.0);
|
||||
keyboard.pressed(KeyCode::E).then(|| cam.roll -= 1.0);
|
||||
if let Some(total_mouse_motion) = mouse_move.iter().map(|e| e.delta).reduce(|sum, i| sum + i) {
|
||||
cam.pitch += total_mouse_motion.y * -0.1;
|
||||
cam.yaw += total_mouse_motion.x * -0.1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Uses [`CameraInput`] state to update the camera position.
|
||||
pub fn camera_controller(
|
||||
time: Res<Time>,
|
||||
mut input: ResMut<CameraInput>,
|
||||
mut camera: Query<(
|
||||
Entity,
|
||||
&mut Transform,
|
||||
&GlobalTransform,
|
||||
&mut CameraController,
|
||||
)>,
|
||||
objects: Query<(&GlobalTransform, &Aabb)>,
|
||||
mut velocities: Local<HashMap<Entity, Transform>>,
|
||||
) {
|
||||
let (entity, mut cam_transform, cam_global_transform, controller) = camera.single_mut();
|
||||
|
||||
let speed = if controller.slow_near_objects {
|
||||
let mut nearest_object = f32::MAX;
|
||||
for (transform, aabb) in &objects {
|
||||
let distance = (transform.translation() + Vec3::from(aabb.center)
|
||||
- cam_global_transform.translation())
|
||||
.length()
|
||||
- aabb.half_extents.max_element();
|
||||
nearest_object = nearest_object.min(distance);
|
||||
}
|
||||
nearest_object.abs().clamp(1.0, controller.max_speed)
|
||||
} else {
|
||||
controller.max_speed
|
||||
};
|
||||
|
||||
let lerp_val = 1.0 - controller.smoothness.clamp(0.0, 0.99999999); // The lerp factor
|
||||
|
||||
let v_current = velocities.entry(entity).or_default();
|
||||
let v_target = input.target_velocity(speed, time.delta_seconds());
|
||||
|
||||
let v_next = Transform {
|
||||
translation: v_current.translation.lerp(v_target.translation, lerp_val),
|
||||
rotation: v_current.rotation.slerp(v_target.rotation, lerp_val),
|
||||
..default()
|
||||
};
|
||||
let cam_rot = cam_transform.rotation;
|
||||
cam_transform.translation += cam_rot * v_next.translation;
|
||||
cam_transform.rotation *= v_next.rotation;
|
||||
*v_current = v_next;
|
||||
|
||||
input.reset();
|
||||
}
|
||||
@ -161,7 +161,6 @@ pub fn build_cube(
|
||||
|
||||
let polyline = polylines.add(Polyline {
|
||||
vertices: vertices.into(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let material = polyline_materials.add(PolylineMaterial {
|
||||
|
||||
99
src/lib.rs
99
src/lib.rs
@ -2,17 +2,46 @@
|
||||
//! observable universe, with no added dependencies, while remaining largely compatible with the
|
||||
//! rest of the Bevy ecosystem.
|
||||
//!
|
||||
//! ## Problem
|
||||
//! ### Problem
|
||||
//!
|
||||
//! Objects far from the origin suffer from reduced precision, causing rendered meshes to jitter and
|
||||
//! jiggle, and transformation calculations to encounter catastrophic cancellation.
|
||||
//!
|
||||
//! ## Solution
|
||||
//! 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.
|
||||
//!
|
||||
//! While using the [`FloatingOriginPlugin`], entities are placed into a large fixed precision grid,
|
||||
//! and their [`Transform`]s are recomputed to be relative to their current grid cell. If an entity
|
||||
//! moves into a neighboring cell, its transform will be recomputed. This prevents `Transforms` from
|
||||
//! ever becoming larger than a single grid cell.
|
||||
//! ### Solution
|
||||
//!
|
||||
//! While using the [`FloatingOriginPlugin`], entities are placed into a [`GridCell`] in a large
|
||||
//! fixed precision grid. Inside a `GridCell`, an entity's `Transform` is relative to the center of
|
||||
//! that grid cell. If an entity moves into a neighboring cell, its transform will be 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.
|
||||
//!
|
||||
//! The same thing happens 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.
|
||||
//!
|
||||
//! # Getting Started
|
||||
//!
|
||||
//! All that's needed to start using this plugin:
|
||||
//! 1. Add the [`FloatingOriginPlugin`] to your `App`
|
||||
//! 2. Add the [`GridCell`] component to all spatial entities
|
||||
//! 3. Add the [`FloatingOrigin`] component to the active camera
|
||||
//!
|
||||
//! Take a look at [`FloatingOriginSettings`] resource for configuration options, as well as some
|
||||
//! useful helper methods.
|
||||
//!
|
||||
//! # Moving Entities
|
||||
//!
|
||||
@ -38,28 +67,29 @@
|
||||
//!
|
||||
//! 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 floating origin
|
||||
//! 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.
|
||||
//! 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 cannot 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, then directly compute the [`GridCell`] and [`Transform`] of that entity
|
||||
//! using [`FloatingOriginSettings::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.
|
||||
//! 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 [`FloatingOriginSettings::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.
|
||||
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use bevy::{math::DVec3, prelude::*, transform::TransformSystem};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub mod camera;
|
||||
pub mod debug;
|
||||
pub mod precision;
|
||||
|
||||
@ -173,12 +203,16 @@ impl FloatingOriginSettings {
|
||||
}
|
||||
|
||||
/// Convert a large translation into a small translation relative to a grid cell.
|
||||
pub fn translation_to_grid<P: GridPrecision>(&self, input: Vec3) -> (GridCell<P>, Vec3) {
|
||||
let l = self.grid_edge_length;
|
||||
let Vec3 { x, y, z } = input;
|
||||
pub fn translation_to_grid<P: GridPrecision>(
|
||||
&self,
|
||||
input: impl Into<DVec3>,
|
||||
) -> (GridCell<P>, Vec3) {
|
||||
let l = self.grid_edge_length as f64;
|
||||
let input = input.into();
|
||||
let DVec3 { x, y, z } = input;
|
||||
|
||||
if input.abs().max_element() < self.maximum_distance_from_origin {
|
||||
return (GridCell::default(), input);
|
||||
if input.abs().max_element() < self.maximum_distance_from_origin as f64 {
|
||||
return (GridCell::default(), input.as_vec3());
|
||||
}
|
||||
|
||||
let x_r = (x / l).round();
|
||||
@ -190,13 +224,21 @@ impl FloatingOriginSettings {
|
||||
|
||||
(
|
||||
GridCell {
|
||||
x: P::from_f32(x_r),
|
||||
y: P::from_f32(y_r),
|
||||
z: P::from_f32(z_r),
|
||||
x: P::from_f32(x_r as f32),
|
||||
y: P::from_f32(y_r as f32),
|
||||
z: P::from_f32(z_r as f32),
|
||||
},
|
||||
Vec3::new(t_x, t_y, t_z),
|
||||
Vec3::new(t_x as f32, t_y as f32, t_z as f32),
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert a large translation into a small translation relative to a grid cell.
|
||||
pub fn imprecise_translation_to_grid<P: GridPrecision>(
|
||||
&self,
|
||||
input: Vec3,
|
||||
) -> (GridCell<P>, Vec3) {
|
||||
self.translation_to_grid(input.as_dvec3())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FloatingOriginSettings {
|
||||
@ -333,7 +375,7 @@ pub fn recenter_transform_on_grid<P: GridPrecision>(
|
||||
> settings.maximum_distance_from_origin
|
||||
{
|
||||
let (grid_cell_delta, translation) =
|
||||
settings.translation_to_grid(transform.as_ref().translation);
|
||||
settings.imprecise_translation_to_grid(transform.as_ref().translation);
|
||||
*grid_pos = *grid_pos + grid_cell_delta;
|
||||
transform.translation = translation;
|
||||
}
|
||||
@ -376,7 +418,6 @@ fn update_global_from_cell_local<P: GridPrecision>(
|
||||
) {
|
||||
let grid_cell_delta = entity_cell - origin_cell;
|
||||
*global = local
|
||||
.clone()
|
||||
.with_translation(settings.grid_position(&grid_cell_delta, local))
|
||||
.into();
|
||||
}
|
||||
@ -450,7 +491,7 @@ pub fn transform_propagate_system<P: GridPrecision>(
|
||||
changed |= changed_children;
|
||||
for child in children {
|
||||
let _ = propagate_recursive(
|
||||
&global_transform,
|
||||
global_transform,
|
||||
&mut transform_query,
|
||||
&children_query,
|
||||
*child,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user