Skip to content
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

Better support fixed timesteps in newest Avian #61

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ bevy = { version = "^0.14", default-features = false, features = [
"x11",
# "filesystem_watcher",
] }
avian3d = { version = "^0.1", features = ["3d", "debug-plugin", "parallel", "parry-f32"] }
avian3d = { version = "^0.1", git = "https://github.com/Jondolf/avian", features = ["3d", "debug-plugin", "parallel", "parry-f32"] }
bevy-tnua-avian3d = { path = "avian3d" }

[package.metadata.docs.rs]
Expand Down
2 changes: 1 addition & 1 deletion avian2d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ readme = "../README.md"

[dependencies]
bevy = { version = "^0.14", default-features = false }
avian2d = {version = "^0.1", default-features = false, features = ["2d", "debug-plugin", "parallel"]}
avian2d = {version = "^0.1", git = "https://github.com/Jondolf/avian", default-features = false, features = ["2d", "debug-plugin", "parallel"]}
bevy-tnua-physics-integration-layer = { version = "^0.4", path = "../physics-integration-layer" }

[package.metadata.docs.rs]
Expand Down
59 changes: 38 additions & 21 deletions avian2d/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ use bevy_tnua_physics_integration_layer::*;
/// Add this plugin to use avian2d as a physics backend.
///
/// This plugin should be used in addition to `TnuaControllerPlugin`.
/// Note that you should make sure both of these plugins use the same schedule.
/// This should usually be `PhysicsSchedule`, which by default is `FixedUpdate`.
///
/// # Example
///
/// ```ignore
/// App::new()
/// .add_plugins((
/// DefaultPlugins,
/// PhysicsPlugins::default(),
/// TnuaControllerPlugin::new(PhysicsSchedule),
/// TnuaAvian2dPlugin::new(PhysicsSchedule),
/// ));
/// ```
pub struct TnuaAvian2dPlugin {
schedule: InternedScheduleLabel,
}
Expand All @@ -34,19 +48,12 @@ impl TnuaAvian2dPlugin {
}
}

impl Default for TnuaAvian2dPlugin {
fn default() -> Self {
Self::new(Update)
}
}

impl Plugin for TnuaAvian2dPlugin {
fn build(&self, app: &mut App) {
app.configure_sets(
self.schedule,
TnuaSystemSet
.before(PhysicsSet::Prepare)
.before(PhysicsStepSet::First)
.in_set(PhysicsStepSet::First)
.run_if(|physics_time: Res<Time<Physics>>| !physics_time.is_paused()),
);
app.add_systems(
Expand All @@ -71,24 +78,25 @@ pub struct TnuaAvian2dSensorShape(pub Collider);
fn update_rigid_body_trackers_system(
gravity: Res<Gravity>,
mut query: Query<(
&GlobalTransform,
&Position,
&Rotation,
&LinearVelocity,
&AngularVelocity,
&mut TnuaRigidBodyTracker,
Option<&TnuaToggle>,
)>,
) {
for (transform, linaer_velocity, angular_velocity, mut tracker, tnua_toggle) in query.iter_mut()
for (position, rotation, linaer_velocity, angular_velocity, mut tracker, tnua_toggle) in
query.iter_mut()
{
match tnua_toggle.copied().unwrap_or_default() {
TnuaToggle::Disabled => continue,
TnuaToggle::SenseOnly => {}
TnuaToggle::Enabled => {}
}
let (_, rotation, translation) = transform.to_scale_rotation_translation();
*tracker = TnuaRigidBodyTracker {
translation: translation.adjust_precision(),
rotation: rotation.adjust_precision(),
translation: position.adjust_precision().extend(0.0),
rotation: Quaternion::from(*rotation).adjust_precision(),
velocity: linaer_velocity.0.extend(0.0),
angvel: Vector3::new(0.0, 0.0, angular_velocity.0),
gravity: gravity.0.extend(0.0),
Expand All @@ -102,7 +110,9 @@ fn update_proximity_sensors_system(
collisions: Res<Collisions>,
mut query: Query<(
Entity,
&GlobalTransform,
&Position,
&Rotation,
&Collider,
&mut TnuaProximitySensor,
Option<&TnuaAvian2dSensorShape>,
Option<&mut TnuaGhostSensor>,
Expand All @@ -111,7 +121,7 @@ fn update_proximity_sensors_system(
)>,
collision_layers_entity: Query<&CollisionLayers>,
other_object_query: Query<(
Option<(&GlobalTransform, &LinearVelocity, &AngularVelocity)>,
Option<(&Position, &LinearVelocity, &AngularVelocity)>,
Option<&CollisionLayers>,
Has<TnuaGhostPlatform>,
Has<Sensor>,
Expand All @@ -120,7 +130,9 @@ fn update_proximity_sensors_system(
query.par_iter_mut().for_each(
|(
owner_entity,
transform,
position,
rotation,
collider,
mut sensor,
shape,
mut ghost_sensor,
Expand All @@ -132,6 +144,11 @@ fn update_proximity_sensors_system(
TnuaToggle::SenseOnly => {}
TnuaToggle::Enabled => {}
}
let transform = Transform {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this transform is only used for computing the cast_origin. Maybe we can just compute it directly with the rotation and position instead of creating a transform matrix?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how we would do that. Creating the transform matrix is super cheap, so I also don't think this is pressing.

translation: position.adjust_precision().extend(0.0),
rotation: Quaternion::from(*rotation).adjust_precision(),
scale: collider.scale().adjust_precision().extend(1.0),
};
let cast_origin = transform.transform_point(sensor.cast_origin.f32());
let cast_direction = sensor.cast_direction;
let cast_direction_2d = Dir2::new(cast_direction.truncate())
Expand Down Expand Up @@ -201,14 +218,14 @@ fn update_proximity_sensors_system(

let entity_linvel;
let entity_angvel;
if let Some((entity_transform, entity_linear_velocity, entity_angular_velocity)) =
if let Some((entity_position, entity_linear_velocity, entity_angular_velocity)) =
entity_kinematic_data
{
entity_angvel = Vector3::new(0.0, 0.0, entity_angular_velocity.0);
entity_linvel = entity_linear_velocity.0.extend(0.0)
+ if 0.0 < entity_angvel.length_squared() {
let relative_point = intersection_point
- entity_transform.translation().truncate().adjust_precision();
let relative_point =
intersection_point - entity_position.adjust_precision();
// NOTE: no need to project relative_point on the
// rotation plane, it will not affect the cross
// product.
Expand Down Expand Up @@ -257,7 +274,7 @@ fn update_proximity_sensors_system(
cast_direction_2d,
sensor.cast_range,
true,
query_filter,
&query_filter,
#[allow(clippy::useless_conversion)]
|shape_hit_data| {
apply_cast(CastResult {
Expand All @@ -275,7 +292,7 @@ fn update_proximity_sensors_system(
cast_direction_2d,
sensor.cast_range,
true,
query_filter,
&query_filter,
|ray_hit_data| {
apply_cast(CastResult {
entity: ray_hit_data.entity,
Expand Down
2 changes: 1 addition & 1 deletion avian3d/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ readme = "../README.md"

[dependencies]
bevy = { version = "^0.14", default-features = false }
avian3d = {version = "^0.1", default-features = false, features = ["3d", "debug-plugin", "parallel"] }
avian3d = {version = "^0.1", git = "https://github.com/Jondolf/avian", default-features = false, features = ["3d", "debug-plugin", "parallel"] }
bevy-tnua-physics-integration-layer = { version = "^0.4", path = "../physics-integration-layer" }

[package.metadata.docs.rs]
Expand Down
54 changes: 38 additions & 16 deletions avian3d/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ use bevy_tnua_physics_integration_layer::TnuaSystemSet;
/// Add this plugin to use avian3d as a physics backend.
///
/// This plugin should be used in addition to `TnuaControllerPlugin`.
/// Note that you should make sure both of these plugins use the same schedule.
/// This should usually be `PhysicsSchedule`, which by default is `FixedUpdate`.
///
/// # Example
///
/// ```ignore
/// App::new()
/// .add_plugins((
/// DefaultPlugins,
/// PhysicsPlugins::default(),
/// TnuaControllerPlugin::new(PhysicsSchedule),
/// TnuaAvian3dPlugin::new(PhysicsSchedule),
/// ));
/// ```
pub struct TnuaAvian3dPlugin {
schedule: InternedScheduleLabel,
}
Expand All @@ -50,8 +64,7 @@ impl Plugin for TnuaAvian3dPlugin {
app.configure_sets(
self.schedule,
TnuaSystemSet
.before(PhysicsSet::Prepare)
.before(PhysicsStepSet::First)
.in_set(PhysicsStepSet::First)
.run_if(|physics_time: Res<Time<Physics>>| !physics_time.is_paused()),
);
app.add_systems(
Expand All @@ -76,23 +89,24 @@ pub struct TnuaAvian3dSensorShape(pub Collider);
fn update_rigid_body_trackers_system(
gravity: Res<Gravity>,
mut query: Query<(
&GlobalTransform,
&Position,
&Rotation,
&LinearVelocity,
&AngularVelocity,
&mut TnuaRigidBodyTracker,
Option<&TnuaToggle>,
)>,
) {
for (transform, linaer_velocity, angular_velocity, mut tracker, tnua_toggle) in query.iter_mut()
for (position, rotation, linaer_velocity, angular_velocity, mut tracker, tnua_toggle) in
query.iter_mut()
{
match tnua_toggle.copied().unwrap_or_default() {
TnuaToggle::Disabled => continue,
TnuaToggle::SenseOnly => {}
TnuaToggle::Enabled => {}
}
let (_, rotation, translation) = transform.to_scale_rotation_translation();
*tracker = TnuaRigidBodyTracker {
translation: translation.adjust_precision(),
translation: position.adjust_precision(),
rotation: rotation.adjust_precision(),
velocity: linaer_velocity.0.adjust_precision(),
angvel: angular_velocity.0.adjust_precision(),
Expand All @@ -107,7 +121,9 @@ fn update_proximity_sensors_system(
collisions: Res<Collisions>,
mut query: Query<(
Entity,
&GlobalTransform,
&Position,
&Rotation,
&Collider,
&mut TnuaProximitySensor,
Option<&TnuaAvian3dSensorShape>,
Option<&mut TnuaGhostSensor>,
Expand All @@ -116,7 +132,7 @@ fn update_proximity_sensors_system(
)>,
collision_layers_entity: Query<&CollisionLayers>,
other_object_query: Query<(
Option<(&GlobalTransform, &LinearVelocity, &AngularVelocity)>,
Option<(&Position, &LinearVelocity, &AngularVelocity)>,
Option<&CollisionLayers>,
Has<TnuaGhostPlatform>,
Has<Sensor>,
Expand All @@ -125,7 +141,9 @@ fn update_proximity_sensors_system(
query.par_iter_mut().for_each(
|(
owner_entity,
transform,
position,
rotation,
collider,
mut sensor,
shape,
mut ghost_sensor,
Expand All @@ -137,6 +155,11 @@ fn update_proximity_sensors_system(
TnuaToggle::SenseOnly => {}
TnuaToggle::Enabled => {}
}
let transform = Transform {
translation: position.0,
rotation: rotation.0,
scale: collider.scale(),
};

// TODO: is there any point in doing these transformations as f64 when that feature
// flag is active?
Expand Down Expand Up @@ -206,14 +229,14 @@ fn update_proximity_sensors_system(

let entity_linvel;
let entity_angvel;
if let Some((entity_transform, entity_linear_velocity, entity_angular_velocity)) =
if let Some((entity_position, entity_linear_velocity, entity_angular_velocity)) =
entity_kinematic_data
{
entity_angvel = entity_angular_velocity.0.adjust_precision();
entity_linvel = entity_linear_velocity.0.adjust_precision()
+ if 0.0 < entity_angvel.length_squared() {
let relative_point = intersection_point
- entity_transform.translation().adjust_precision();
let relative_point =
intersection_point - entity_position.adjust_precision();
// NOTE: no need to project relative_point on the
// rotation plane, it will not affect the cross
// product.
Expand Down Expand Up @@ -255,10 +278,9 @@ fn update_proximity_sensors_system(

let query_filter = SpatialQueryFilter::from_excluded_entities([owner_entity]);
if let Some(TnuaAvian3dSensorShape(shape)) = shape {
let (_, owner_rotation, _) = transform.to_scale_rotation_translation();
let owner_rotation = Quat::from_axis_angle(
*cast_direction,
owner_rotation.to_scaled_axis().dot(*cast_direction),
rotation.to_scaled_axis().dot(*cast_direction),
);
spatial_query_pipeline.shape_hits_callback(
shape,
Expand All @@ -267,7 +289,7 @@ fn update_proximity_sensors_system(
cast_direction,
sensor.cast_range,
true,
query_filter,
&query_filter,
|shape_hit_data| {
apply_cast(CastResult {
entity: shape_hit_data.entity,
Expand All @@ -284,7 +306,7 @@ fn update_proximity_sensors_system(
cast_direction,
sensor.cast_range,
true,
query_filter,
&query_filter,
|ray_hit_data| {
apply_cast(CastResult {
entity: ray_hit_data.entity,
Expand Down
4 changes: 2 additions & 2 deletions demos/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ bevy-tnua-rapier2d = { path = "../rapier2d", optional = true }
bevy_rapier3d = { version = "^0.27", features = ["debug-render-3d"], optional = true }
bevy-tnua-rapier3d = { path = "../rapier3d", optional = true }

avian2d = {version = "^0.1", default-features = false, features = ["2d","debug-plugin", "parallel"], optional = true}
avian2d = {version = "^0.1", git = "https://github.com/Jondolf/avian", default-features = false, features = ["2d","debug-plugin", "parallel"], optional = true}
bevy-tnua-avian2d = { path = "../avian2d", default-features = false, optional = true }

avian3d = {version = "^0.1", default-features = false, features = ["3d","debug-plugin", "parallel"], optional = true }
avian3d = {version = "^0.1", git = "https://github.com/Jondolf/avian", default-features = false, features = ["3d","debug-plugin", "parallel"], optional = true }
bevy-tnua-avian3d = { path = "../avian3d", default-features = false, optional = true }

bevy_egui = { version = "0.28", optional = true, default-features = false, features = ["default_fonts", "render"] }
Expand Down
19 changes: 17 additions & 2 deletions demos/src/app_setup_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ use clap::{Parser, ValueEnum};

#[derive(Resource, Debug, Parser, Clone)]
pub struct AppSetupConfiguration {
#[arg(long = "schedule", default_value = "update")]
#[cfg_attr(feature = "rapier", arg(long = "schedule", default_value = "update"))]
#[cfg_attr(
feature = "avian",
arg(long = "schedule", default_value = "physics-schedule")
)]
pub schedule_to_use: ScheduleToUse,
#[arg(long = "level")]
pub level_to_load: Option<String>,
Expand All @@ -26,7 +30,18 @@ impl AppSetupConfiguration {
schedule_to_use: if let Some(value) = url_params.get("schedule") {
ScheduleToUse::from_str(&value, true).unwrap()
} else {
ScheduleToUse::Update
#[cfg(feature = "avian")]
{
ScheduleToUse::PhysicsSchedule
}
#[cfg(feature = "rapier")]
{
ScheduleToUse::Update
}
#[cfg(all(not(feature = "avian"), not(feature = "rapier")))]
{
panic!("No schedule was specified, but also no physics engine is avaible. Therefore, there is no fallback.")
}
},
level_to_load: url_params.get("level"),
}
Expand Down
Loading
Loading