Skip to content

Add support for floating origins using deltas in sync plugin #697

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/avian3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ bevy_heavy = { git = "https://github.com/Jondolf/bevy_heavy", features = [
] }
approx = "0.5"
criterion = { version = "0.5", features = ["html_reports"] }
big_space = { git = "https://github.com/aevyrie/big_space", branch = "bevy-0.16.0", features = [
"debug",
] }

# TODO: Update to the latest version when it's available.
# bevy_mod_debugdump = { version = "0.12" }

Expand Down
126 changes: 126 additions & 0 deletions crates/avian3d/examples/floating_origin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use avian3d::prelude::*;
use bevy::prelude::*;
use big_space::prelude::*;
use examples_common_3d::ExampleCommonPlugin;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins((
BigSpacePlugin::default(),
FloatingOriginDebugPlugin::default(),
PhysicsPlugins::default(),
PhysicsDebugPlugin::default(),
ExampleCommonPlugin,
))
.insert_resource(Gravity(Vec3::ZERO))
.add_systems(Startup, spawn_big_space)
.add_systems(Update, move_cube)
.add_systems(
PostUpdate,
move_camera.before(TransformSystem::TransformPropagate),
)
.run();
}

#[derive(Component)]
struct WasdControlled;

#[derive(Component)]
struct MainCamera;

fn spawn_big_space(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
commands.spawn_big_space(Grid::new(5.0, 0.0), |root| {
// Camera with no physics components
root.spawn_spatial((
FloatingOrigin,
Transform::from_xyz(0.0, 0.0, 25.0).looking_at(Vec3::new(0.0, 0.0, 0.0), Vec3::Y),
Camera::default(),
Camera3d::default(),
Projection::Perspective(PerspectiveProjection::default()),
MainCamera,
));

let cube_mesh = meshes.add(Cuboid::default());

// WASD controlled cube
root.spawn_spatial((
Mesh3d(cube_mesh.clone()),
MeshMaterial3d(materials.add(Color::WHITE)),
Transform::IDENTITY,
RigidBody::Dynamic,
Collider::cuboid(1.0, 1.0, 1.0),
WasdControlled,
LinearVelocity::default(),
LinearDamping::default(),
TransformInterpolation,
));
});
}

fn move_cube(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut query: Query<(&mut LinearVelocity, &mut LinearDamping), With<WasdControlled>>,
) {
let mut velocity = Vec3::ZERO;
if keyboard_input.pressed(KeyCode::KeyW) {
velocity += Vec3::Y * 0.1;
}
if keyboard_input.pressed(KeyCode::KeyS) {
velocity -= Vec3::Y * 0.1;
}
if keyboard_input.pressed(KeyCode::KeyA) {
velocity -= Vec3::X * 0.1;
}
if keyboard_input.pressed(KeyCode::KeyD) {
velocity += Vec3::X * 0.1;
}

for (mut linear_velocity, _) in &mut query {
linear_velocity.0 += velocity;
}

let linear_damping_value = if keyboard_input.pressed(KeyCode::Space) {
10.0
} else {
0.0
};

for (_, mut linear_damping) in &mut query {
linear_damping.0 = linear_damping_value;
}
}

fn move_camera(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut Transform, With<MainCamera>>,
time: Res<Time>,
) {
let camera_speed = 5.0;

for mut transform in &mut query {
let mut movement = Vec3::ZERO;

if keyboard_input.pressed(KeyCode::ArrowUp) {
movement += Vec3::Y;
}
if keyboard_input.pressed(KeyCode::ArrowDown) {
movement -= Vec3::Y;
}
if keyboard_input.pressed(KeyCode::ArrowLeft) {
movement -= Vec3::X;
}
if keyboard_input.pressed(KeyCode::ArrowRight) {
movement += Vec3::X;
}

if movement != Vec3::ZERO {
movement = movement.normalize() * camera_speed * time.delta_secs();
transform.translation += movement;
}
}
}
72 changes: 60 additions & 12 deletions src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ impl Plugin for SyncPlugin {
// trees that have no rigid bodies.
app.add_plugins(AncestorMarkerPlugin::<RigidBody>::default());

// Initialize PreviousPhysicsPosition for delta-based transforms
app.add_systems(
self.schedule,
init_previous_physics_position.in_set(SyncSet::First),
);

// Initialize `PreviousGlobalTransform` and apply `Transform` changes that happened
// between the end of the previous physics frame and the start of this physics frame.
app.add_systems(
Expand All @@ -88,6 +94,7 @@ impl Plugin for SyncPlugin {
transform_to_position,
// Update `PreviousGlobalTransform` for the physics step's `GlobalTransform` change detection
update_previous_global_transforms,
update_previous_physics_position,
)
.chain()
.after(PhysicsSet::Prepare)
Expand Down Expand Up @@ -183,6 +190,30 @@ pub enum SyncSet {
#[reflect(Component)]
pub struct PreviousGlobalTransform(pub GlobalTransform);

/// Stores the previous physics position for delta-based movement
#[derive(Component, Reflect, Clone, Copy, Debug, Default, Deref, DerefMut)]
#[reflect(Component)]
pub struct PreviousPosition(pub Vector);

/// Initialize the previous position component for delta-based movement
pub fn init_previous_physics_position(
mut commands: Commands,
query: Query<(Entity, &Position), Added<Position>>,
) {
for (entity, position) in &query {
commands
.entity(entity)
.try_insert(PreviousPosition(position.adjust_precision()));
}
}

/// Update the previous position after physics step for delta-based movement
pub fn update_previous_physics_position(mut query: Query<(&Position, &mut PreviousPosition)>) {
for (position, mut prev_position) in &mut query {
prev_position.0 = position.adjust_precision();
}
}

#[allow(clippy::type_complexity)]
pub(crate) fn init_previous_global_transform(
mut commands: Commands,
Expand All @@ -204,24 +235,30 @@ pub fn transform_to_position(
mut query: Query<(
&GlobalTransform,
&PreviousGlobalTransform,
&mut PreviousPosition,
&mut Position,
&mut Rotation,
)>,
) {
for (global_transform, previous_transform, mut position, mut rotation) in &mut query {
for (global_transform, previous_transform, mut previous_position, mut position, mut rotation) in
&mut query
{
// Skip entity if the global transform value hasn't changed
if *global_transform == previous_transform.0 {
continue;
}

let global_transform = global_transform.compute_transform();
let previous_transform = previous_transform.0.compute_transform();
let delta = global_transform.translation - previous_transform.translation;

#[cfg(feature = "2d")]
{
previous_position.0 += delta.truncate().adjust_precision();
position.0 = global_transform.translation.truncate().adjust_precision();
}
#[cfg(feature = "3d")]
{
previous_position.0 += delta.adjust_precision();
position.0 = global_transform.translation.adjust_precision();
}

Expand All @@ -239,6 +276,7 @@ pub fn transform_to_position(
type PosToTransformComponents = (
&'static mut Transform,
&'static Position,
&'static PreviousPosition,
&'static Rotation,
Option<&'static ChildOf>,
);
Expand All @@ -251,8 +289,9 @@ type ParentComponents = (
Option<&'static Rotation>,
);

/// Copies [`Position`] and [`Rotation`] changes to `Transform`.
/// This allows users and the engine to use these components for moving and positioning bodies.
/// Copies [`Position`] and [`Rotation`] changes to `Transform` using delta-based updates.
/// This allows users and the engine to use these components for moving and positioning bodies,
/// and is compatible with floating origin systems.
///
/// Nested rigid bodies move independently of each other, so the `Transform`s of child entities are updated
/// based on their own and their parent's [`Position`] and [`Rotation`].
Expand All @@ -261,7 +300,7 @@ pub fn position_to_transform(
mut query: Query<PosToTransformComponents, PosToTransformFilter>,
parents: Query<ParentComponents, With<Children>>,
) {
for (mut transform, pos, rot, parent) in &mut query {
for (mut transform, pos, prev_pos, rot, parent) in &mut query {
if let Some(&ChildOf(parent)) = parent {
if let Ok((parent_transform, parent_pos, parent_rot)) = parents.get(parent) {
// Compute the global transform of the parent using its Position and Rotation
Expand All @@ -288,18 +327,23 @@ pub fn position_to_transform(
)
.reparented_to(&GlobalTransform::from(parent_transform));

transform.translation = new_transform.translation;
// Apply position as delta
let delta = pos.adjust_precision() - prev_pos.0;
transform.translation += delta.extend(0.0);
transform.rotation = new_transform.rotation;
}
} else {
transform.translation = pos.f32().extend(transform.translation.z);
// For root entities, apply position change as a delta
let delta = pos.adjust_precision() - prev_pos.0;
transform.translation += delta.extend(0.0);
transform.rotation = Quaternion::from(*rot).f32();
}
}
}

/// Copies [`Position`] and [`Rotation`] changes to `Transform`.
/// This allows users and the engine to use these components for moving and positioning bodies.
/// Copies [`Position`] and [`Rotation`] changes to `Transform` using delta-based updates.
/// This allows users and the engine to use these components for moving and positioning bodies,
/// and is compatible with floating origin systems.
///
/// Nested rigid bodies move independently of each other, so the `Transform`s of child entities are updated
/// based on their own and their parent's [`Position`] and [`Rotation`].
Expand All @@ -308,7 +352,7 @@ pub fn position_to_transform(
mut query: Query<PosToTransformComponents, PosToTransformFilter>,
parents: Query<ParentComponents, With<Children>>,
) {
for (mut transform, pos, rot, parent) in &mut query {
for (mut transform, pos, prev_pos, rot, parent) in &mut query {
if let Some(&ChildOf(parent)) = parent {
if let Ok((parent_transform, parent_pos, parent_rot)) = parents.get(parent) {
// Compute the global transform of the parent using its Position and Rotation
Expand All @@ -327,11 +371,15 @@ pub fn position_to_transform(
)
.reparented_to(&GlobalTransform::from(parent_transform));

transform.translation = new_transform.translation;
// Apply position change as a delta
let delta = pos.adjust_precision() - prev_pos.0;
transform.translation += delta.f32();
transform.rotation = new_transform.rotation;
}
} else {
transform.translation = pos.f32();
// For root entities, apply position change as a delta
let delta = pos.adjust_precision() - prev_pos.0;
transform.translation += delta.f32();
transform.rotation = rot.f32();
}
}
Expand Down
Loading