From ace2570fc8ef4fdc1cb6fdccd5e2c92e0997c7e5 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 10 Sep 2024 12:31:37 +0200 Subject: [PATCH 01/12] Initialize collider scale --- src/collision/collider/backend.rs | 32 ++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index 58547062..461c4cf3 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -95,9 +95,39 @@ impl Plugin for ColliderBackendPlugin { // Initialize missing components for colliders. hooks.on_add(|mut world, entity, _| { - let entity_ref = world.entity(entity); + let parent_global_transform = world + .entity(entity) + .get::() + .and_then(|parent| world.entity(parent.get()).get::().copied()) + .unwrap_or_default(); + let transform = world + .entity(entity) + .get::() + .copied() + .unwrap_or_default(); + let global_transform = parent_global_transform * transform; + let scale = global_transform.compute_transform().scale; + #[cfg(feature = "2d")] + let scale = scale.xy(); + // Make sure the is initialized with the correct scale. + // This overwrites the scale set by the constructor, but that one is + // meant to be only changed after initialization. + // You may notice that this will fail if the hierarchy's scale was updated in this + // frame. Remember that `GlobalTransform` is not updated inbetween fixed updates. + // But this is fine, as `update_collider_scale` will be updated in the next fixed update anyways. + // The reason why we care about initializing this scale here is for those users that opted out of + // `update_collider_scale` in order to do their own interpolation, which implies that they won't touch + // the `Transform` component before the collider is initialized, which in turn means that it will + // always be initialized with the correct `GlobalTransform`. + world + .entity_mut(entity) + .get_mut::() + .unwrap() + .set_scale(scale, 10); + let entity_ref = world.entity(entity); let collider = entity_ref.get::().unwrap(); + let aabb = entity_ref .get::() .copied() From 42dbb2817b0f97b3b3638349f948b74292acbe20 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 10 Sep 2024 12:38:20 +0200 Subject: [PATCH 02/12] Allow scale sync opt-out --- src/collision/collider/backend.rs | 7 ++++++- src/sync/mod.rs | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index 461c4cf3..3116aa97 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -4,7 +4,7 @@ use std::marker::PhantomData; -use crate::{broad_phase::BroadPhaseSet, prelude::*, prepare::PrepareSet}; +use crate::{broad_phase::BroadPhaseSet, prelude::*, prepare::PrepareSet, sync::SyncConfig}; #[cfg(all(feature = "bevy_scene", feature = "default-collider"))] use bevy::scene::SceneInstance; use bevy::{ @@ -642,7 +642,12 @@ pub fn update_collider_scale( // Child colliders Query<(&ColliderTransform, &mut C), (With, Changed)>, )>, + sync_config: Res, ) { + if !sync_config.transform_to_collider_scale { + return; + } + // Update collider scale for root bodies for (transform, mut collider) in &mut colliders.p0() { #[cfg(feature = "2d")] diff --git a/src/sync/mod.rs b/src/sync/mod.rs index ca5579e2..31331131 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -143,15 +143,24 @@ impl Plugin for SyncPlugin { } } -/// Configures what physics data is synchronized by the [`SyncPlugin`] and how. +/// Configures what physics data is synchronized by the [`SyncPlugin`] and [`PreparePlugin`] and how. #[derive(Resource, Reflect, Clone, Debug, PartialEq, Eq)] #[reflect(Resource)] pub struct SyncConfig { /// Updates transforms based on [`Position`] and [`Rotation`] changes. Defaults to true. + /// + /// This operation is run in [`SyncSet::PositionToTransform`]. pub position_to_transform: bool, /// Updates [`Position`] and [`Rotation`] based on transform changes, - /// allowing you to move bodies using `Transform`. Defaults to true. + /// allowing you to move bodies using [`Transform`]. Defaults to true. + /// + /// This operation is run in [`SyncSet::TransformToPosition`]. pub transform_to_position: bool, + /// Updates [`Collider::scale()`] based on transform changes, + /// allowing you to scale colliders using [`Transform`]. Defaults to true. + /// + /// This operation is run in [`PrepareSet::Finalize`] + pub transform_to_collider_scale: bool, } impl Default for SyncConfig { @@ -159,6 +168,7 @@ impl Default for SyncConfig { SyncConfig { position_to_transform: true, transform_to_position: true, + transform_to_collider_scale: true, } } } From 1ebfefbfa5ac148d77dd02b979bb60b45cc87cd7 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Tue, 10 Sep 2024 13:56:22 +0200 Subject: [PATCH 03/12] Make code more robust when there is a preexisting global transform --- src/collision/collider/backend.rs | 44 ++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index 3116aa97..496693cb 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -95,30 +95,44 @@ impl Plugin for ColliderBackendPlugin { // Initialize missing components for colliders. hooks.on_add(|mut world, entity, _| { - let parent_global_transform = world + let existing_global_transform = world .entity(entity) - .get::() - .and_then(|parent| world.entity(parent.get()).get::().copied()) - .unwrap_or_default(); - let transform = world - .entity(entity) - .get::() + .get::() .copied() .unwrap_or_default(); - let global_transform = parent_global_transform * transform; + let global_transform = if existing_global_transform != GlobalTransform::IDENTITY { + // This collider was built deferred, probably via `ColliderConstructor`. + existing_global_transform + } else { + // This collider *may* have been `spawn`ed directly on a new entity. + // As such, its global transform is not yet available. + // You may notice that this will fail if the hierarchy's scale was updated in this + // frame. Remember that `GlobalTransform` is not updated inbetween fixed updates. + // But this is fine, as `update_collider_scale` will be updated in the next fixed update anyways. + // The reason why we care about initializing this scale here is for those users that opted out of + // `update_collider_scale` in order to do their own interpolation, which implies that they won't touch + // the `Transform` component before the collider is initialized, which in turn means that it will + // always be initialized with the correct `GlobalTransform`. + let parent_global_transform = world + .entity(entity) + .get::() + .and_then(|parent| world.entity(parent.get()).get::().copied()) + .unwrap_or_default(); + let transform = world + .entity(entity) + .get::() + .copied() + .unwrap_or_default(); + parent_global_transform * transform + }; + let scale = global_transform.compute_transform().scale; #[cfg(feature = "2d")] let scale = scale.xy(); + // Make sure the is initialized with the correct scale. // This overwrites the scale set by the constructor, but that one is // meant to be only changed after initialization. - // You may notice that this will fail if the hierarchy's scale was updated in this - // frame. Remember that `GlobalTransform` is not updated inbetween fixed updates. - // But this is fine, as `update_collider_scale` will be updated in the next fixed update anyways. - // The reason why we care about initializing this scale here is for those users that opted out of - // `update_collider_scale` in order to do their own interpolation, which implies that they won't touch - // the `Transform` component before the collider is initialized, which in turn means that it will - // always be initialized with the correct `GlobalTransform`. world .entity_mut(entity) .get_mut::() From 5291c8e23b3e046a45f3be354c450762bd532e9e Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Wed, 11 Sep 2024 03:07:55 +0200 Subject: [PATCH 04/12] Initialize SyncConfig in PreparePlugin --- src/prepare.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/prepare.rs b/src/prepare.rs index ade1863c..3ef976e0 100644 --- a/src/prepare.rs +++ b/src/prepare.rs @@ -4,7 +4,7 @@ #![allow(clippy::type_complexity)] -use crate::prelude::*; +use crate::{prelude::*, sync::SyncConfig}; use bevy::{ ecs::{ intern::Interned, @@ -77,6 +77,8 @@ pub enum PrepareSet { impl Plugin for PreparePlugin { fn build(&self, app: &mut App) { + app.init_resource::() + .register_type::(); app.configure_sets( self.schedule, ( From 67d62220164fc30b9e008389fafcbddc5be94d48 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Wed, 11 Sep 2024 03:20:08 +0200 Subject: [PATCH 05/12] Use actual condition for condition --- src/collision/collider/backend.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index 496693cb..f6b5f8c3 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -276,7 +276,9 @@ impl Plugin for ColliderBackendPlugin { .in_set(PrepareSet::InitTransforms) .after(init_transforms::), ( - update_collider_scale::, + update_collider_scale::.run_if(|sync_config: Res| { + sync_config.transform_to_collider_scale + }), update_collider_mass_properties::, ) .chain() @@ -656,12 +658,7 @@ pub fn update_collider_scale( // Child colliders Query<(&ColliderTransform, &mut C), (With, Changed)>, )>, - sync_config: Res, ) { - if !sync_config.transform_to_collider_scale { - return; - } - // Update collider scale for root bodies for (transform, mut collider) in &mut colliders.p0() { #[cfg(feature = "2d")] From 03db36f6e096b392f6090dd1ac398cb8817c051e Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Thu, 12 Sep 2024 09:57:11 +0200 Subject: [PATCH 06/12] Add rigid body scale --- src/collision/collider/backend.rs | 28 +++---- src/collision/collider/hierarchy.rs | 34 +++++++- src/lib.rs | 2 +- src/position.rs | 30 +++++++ src/prepare.rs | 119 +++++++++++++++++++++++++--- 5 files changed, 188 insertions(+), 25 deletions(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index f6b5f8c3..2896ea02 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -276,9 +276,7 @@ impl Plugin for ColliderBackendPlugin { .in_set(PrepareSet::InitTransforms) .after(init_transforms::), ( - update_collider_scale::.run_if(|sync_config: Res| { - sync_config.transform_to_collider_scale - }), + update_collider_scale::, update_collider_mass_properties::, ) .chain() @@ -658,20 +656,22 @@ pub fn update_collider_scale( // Child colliders Query<(&ColliderTransform, &mut C), (With, Changed)>, )>, + sync_config: Res, ) { - // Update collider scale for root bodies - for (transform, mut collider) in &mut colliders.p0() { - #[cfg(feature = "2d")] - let scale = transform.scale.truncate().adjust_precision(); - #[cfg(feature = "3d")] - let scale = transform.scale.adjust_precision(); - if scale != collider.scale() { - // TODO: Support configurable subdivision count for shapes that - // can't be represented without approximations after scaling. - collider.set_scale(scale, 10); + if sync_config.transform_to_collider_scale { + // Update collider scale for root bodies + for (transform, mut collider) in &mut colliders.p0() { + #[cfg(feature = "2d")] + let scale = transform.scale.truncate().adjust_precision(); + #[cfg(feature = "3d")] + let scale = transform.scale.adjust_precision(); + if scale != collider.scale() { + // TODO: Support configurable subdivision count for shapes that + // can't be represented without approximations after scaling. + collider.set_scale(scale, 10); + } } } - // Update collider scale for child colliders for (collider_transform, mut collider) in &mut colliders.p1() { if collider_transform.scale != collider.scale() { diff --git a/src/collision/collider/hierarchy.rs b/src/collision/collider/hierarchy.rs index 6b112d5f..1e5bfb13 100644 --- a/src/collision/collider/hierarchy.rs +++ b/src/collision/collider/hierarchy.rs @@ -216,13 +216,45 @@ pub(crate) fn update_child_collider_position( // is a collider or is a `AncestorMarker`. type ShouldPropagate = Or<(With>, With)>; +pub(crate) fn propagate_collider_transforms( + mut collider_query: Query<(Entity, &mut ColliderTransform, &ColliderParent)>, + transform_query: Query<(&Position, &Rotation, &RigidBodyScale)>, +) { + for (entity, mut collider_transform, parent) in &mut collider_query { + let parent = parent.get(); + let Ok((&collider_pos, &collider_rot, &collider_scale)) = transform_query.get(entity) + else { + error!("Entity {entity}'s collider has no position or rotation.",); + continue; + }; + let Ok((&parent_pos, &parent_rot, &parent_scale)) = transform_query.get(parent) else { + error!("Entity {entity}'s collider parent {parent} has no position or rotation.",); + continue; + }; + let parent_global_transform = GlobalTransform::from(Transform { + translation: parent_pos.into(), + rotation: parent_rot.into(), + scale: parent_scale.into(), + ..default() + }); + let collider_global_transform = GlobalTransform::from(Transform { + translation: collider_pos.into(), + rotation: collider_rot.into(), + scale: collider_scale.into(), + ..default() + }); + let relative_transform = collider_global_transform.reparented_to(&parent_global_transform); + *collider_transform = ColliderTransform::from(relative_transform); + } +} + /// Updates [`ColliderTransform`]s based on entity hierarchies. Each transform is computed by recursively /// traversing the children of each rigid body and adding their transforms together to form /// the total transform relative to the body. /// /// This is largely a clone of `propagate_transforms` in `bevy_transform`. #[allow(clippy::type_complexity)] -pub(crate) fn propagate_collider_transforms( +pub(crate) fn propagate_collider_transforms_old( mut root_query: Query< (Entity, Ref, &Children), (Without, With>), diff --git a/src/lib.rs b/src/lib.rs index d476ef7f..8bac052a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -478,7 +478,7 @@ pub mod prelude { *, }, dynamics::{self, ccd::SpeculativeMargin, prelude::*}, - position::{Position, Rotation}, + position::{Position, RigidBodyScale, Rotation}, prepare::{init_transforms, update_mass_properties, PrepareConfig, PreparePlugin}, schedule::*, spatial_query::{self, *}, diff --git a/src/position.rs b/src/position.rs index 8d519f04..c3b1ed97 100644 --- a/src/position.rs +++ b/src/position.rs @@ -98,6 +98,15 @@ impl From<&GlobalTransform> for Position { } } +impl From for Vec3 { + fn from(value: Position) -> Self { + let inner = value.0; + #[cfg(feature = "2d")] + let inner = inner.extend(0.0); + inner + } +} + /// The translation accumulated before the XPBD position solve. #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] @@ -944,3 +953,24 @@ impl From for Rotation { #[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))] #[reflect(Debug, Component, Default, PartialEq)] pub struct PreviousRotation(pub Rotation); + +#[derive(Reflect, Clone, Copy, Component, Debug, Deref, DerefMut, PartialEq)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))] +#[reflect(Debug, Component, Default, PartialEq)] +pub struct RigidBodyScale(pub Vector); + +impl Default for RigidBodyScale { + fn default() -> Self { + Self(Vector::ONE) + } +} + +impl From for Vec3 { + fn from(value: RigidBodyScale) -> Self { + let inner = value.0; + #[cfg(feature = "2d")] + let inner = inner.extend(1.0); + inner + } +} diff --git a/src/prepare.rs b/src/prepare.rs index 3ef976e0..14985a0c 100644 --- a/src/prepare.rs +++ b/src/prepare.rs @@ -188,10 +188,10 @@ impl Plugin for PreparePlugin { #[derive(Resource, Reflect, Clone, Debug, PartialEq, Eq)] #[reflect(Resource)] pub struct PrepareConfig { - /// Initializes [`Transform`] based on [`Position`] and [`Rotation`]. + /// Initializes [`Transform`] based on [`Position`], [`Rotation`], and [`RigidBodyScale`]. /// Defaults to true. pub position_to_transform: bool, - /// Initializes [`Position`] and [`Rotation`] based on [`Transform`]. + /// Initializes [`Position`], [`Rotation`], and [`RigidBodyScale`] based on [`Transform`]. /// Defaults to true. pub transform_to_position: bool, } @@ -222,6 +222,7 @@ pub fn init_transforms( Option<&GlobalTransform>, Option<&Position>, Option<&Rotation>, + Option<&RigidBodyScale>, Option<&PreviousRotation>, Option<&Parent>, Has, @@ -232,6 +233,7 @@ pub fn init_transforms( ( Option<&Position>, Option<&Rotation>, + Option<&RigidBodyScale>, Option<&GlobalTransform>, ), With, @@ -242,13 +244,23 @@ pub fn init_transforms( return; } - for (entity, transform, global_transform, pos, rot, previous_rot, parent, has_rigid_body) in - &query + for ( + entity, + transform, + global_transform, + pos, + rot, + scale, + previous_rot, + parent, + has_rigid_body, + ) in &query { let parent_transforms = parent.and_then(|parent| parents.get(parent.get()).ok()); - let parent_pos = parent_transforms.and_then(|(pos, _, _)| pos); - let parent_rot = parent_transforms.and_then(|(_, rot, _)| rot); - let parent_global_trans = parent_transforms.and_then(|(_, _, trans)| trans); + let parent_pos = parent_transforms.and_then(|(pos, _, _, _)| pos); + let parent_rot = parent_transforms.and_then(|(_, rot, _, _)| rot); + let parent_scale = parent_transforms.and_then(|(_, _, scale, _)| scale); + let parent_global_trans = parent_transforms.and_then(|(_, _, _, trans)| trans); let mut new_transform = if config.position_to_transform { Some(transform.copied().unwrap_or_default()) @@ -379,6 +391,84 @@ pub fn init_transforms( default() }; + let new_scale = if let Some(scale) = scale { + if let Some(transform) = &mut new_transform { + // Initialize new scale as global scale + #[cfg(feature = "2d")] + let mut new_scale = scale.f32().extend(transform.scale.z); + #[cfg(feature = "3d")] + let mut new_scale = scale.f32(); + + // If the body is a child, divide by the parent's global scale + // to get the local scale + if parent.is_some() { + if let Some(parent_scale) = parent_scale { + #[cfg(feature = "2d")] + { + new_scale /= parent_scale.f32().extend(new_scale.z); + } + #[cfg(feature = "3d")] + { + new_scale /= parent_scale.f32(); + } + } else if let Some(parent_transform) = parent_global_trans { + new_scale /= parent_transform.compute_transform().scale; + } + } + transform.scale = new_scale; + } + scale.0 + } else if config.transform_to_position { + let mut new_scale = Vector::ONE; + + if parent.is_some() { + let scale = transform.as_ref().map_or(Vec3::ONE, |t| t.scale); + if let Some(parent_scale) = parent_scale { + #[cfg(feature = "2d")] + { + new_scale = parent_scale.0 * scale.adjust_precision().truncate(); + } + #[cfg(feature = "3d")] + { + new_scale = parent_scale.0 * scale.adjust_precision(); + } + } else if let Some(parent_transform) = parent_global_trans { + let new_scale_unadjusted = parent_transform + .transform_point(transform.as_ref().map_or(Vec3::ONE, |t| t.scale)); + #[cfg(feature = "2d")] + { + new_scale = new_scale_unadjusted.truncate().adjust_precision(); + } + #[cfg(feature = "3d")] + { + new_scale = new_scale_unadjusted.adjust_precision(); + } + } + } else { + #[cfg(feature = "2d")] + { + new_scale = transform + .map(|t| t.scale.truncate().adjust_precision()) + .unwrap_or(global_transform.as_ref().map_or(Vector::ONE, |t| { + let t = t.compute_transform(); + Vector::new(t.scale.x as Scalar, t.scale.y as Scalar) + })); + } + #[cfg(feature = "3d")] + { + new_scale = transform.map(|t| t.scale.adjust_precision()).unwrap_or( + global_transform.as_ref().map_or(Vec3::ONE, |t| { + t.compute_transform().scale.adjust_precision() + }), + ) + } + }; + + new_scale + } else { + Vector::ONE + }; + let mut cmds = commands.entity(entity); // Insert the position and rotation. @@ -391,6 +481,7 @@ pub fn init_transforms( cmds.try_insert(( Position(new_position), new_rotation, + RigidBodyScale(new_scale), PreSolveAccumulatedTranslation::default(), *previous_rot.unwrap_or(&PreviousRotation(new_rotation)), PreSolveRotation::default(), @@ -401,16 +492,26 @@ pub fn init_transforms( transform, Position(new_position), new_rotation, + RigidBodyScale(new_scale), PreSolveAccumulatedTranslation::default(), *previous_rot.unwrap_or(&PreviousRotation(new_rotation)), PreSolveRotation::default(), )); } (false, None) => { - cmds.try_insert((Position(new_position), new_rotation)); + cmds.try_insert(( + Position(new_position), + new_rotation, + RigidBodyScale(new_scale), + )); } (false, Some(transform)) => { - cmds.try_insert((transform, Position(new_position), new_rotation)); + cmds.try_insert(( + transform, + Position(new_position), + new_rotation, + RigidBodyScale(new_scale), + )); } } } From 4082a8a78833c2a355952c5d87beeb8e684307da Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Thu, 12 Sep 2024 10:22:01 +0200 Subject: [PATCH 07/12] Remove rigid body scale again --- src/collision/collider/hierarchy.rs | 32 +++++--- src/lib.rs | 2 +- src/position.rs | 21 ----- src/prepare.rs | 119 +++------------------------- 4 files changed, 29 insertions(+), 145 deletions(-) diff --git a/src/collision/collider/hierarchy.rs b/src/collision/collider/hierarchy.rs index 1e5bfb13..4cddbf77 100644 --- a/src/collision/collider/hierarchy.rs +++ b/src/collision/collider/hierarchy.rs @@ -216,35 +216,41 @@ pub(crate) fn update_child_collider_position( // is a collider or is a `AncestorMarker`. type ShouldPropagate = Or<(With>, With)>; -pub(crate) fn propagate_collider_transforms( - mut collider_query: Query<(Entity, &mut ColliderTransform, &ColliderParent)>, - transform_query: Query<(&Position, &Rotation, &RigidBodyScale)>, +pub(crate) fn propagate_collider_transforms_new( + mut collider_query: Query<( + Entity, + &mut ColliderTransform, + &ColliderParent, + &GlobalTransform, + )>, + transform_query: Query<(&Position, &Rotation)>, ) { - for (entity, mut collider_transform, parent) in &mut collider_query { + for (entity, mut collider_transform, parent, global_transform) in &mut collider_query { let parent = parent.get(); - let Ok((&collider_pos, &collider_rot, &collider_scale)) = transform_query.get(entity) - else { + let Ok((&collider_pos, &collider_rot)) = transform_query.get(entity) else { error!("Entity {entity}'s collider has no position or rotation.",); continue; }; - let Ok((&parent_pos, &parent_rot, &parent_scale)) = transform_query.get(parent) else { + let Ok((&parent_pos, &parent_rot)) = transform_query.get(parent) else { error!("Entity {entity}'s collider parent {parent} has no position or rotation.",); continue; }; + let global_scale = global_transform.compute_transform().scale; let parent_global_transform = GlobalTransform::from(Transform { translation: parent_pos.into(), rotation: parent_rot.into(), - scale: parent_scale.into(), - ..default() + scale: Vec3::ONE, }); let collider_global_transform = GlobalTransform::from(Transform { translation: collider_pos.into(), rotation: collider_rot.into(), - scale: collider_scale.into(), - ..default() + scale: global_scale, }); let relative_transform = collider_global_transform.reparented_to(&parent_global_transform); - *collider_transform = ColliderTransform::from(relative_transform); + let new_collider_transform = ColliderTransform::from(relative_transform); + if collider_transform.as_ref() != &new_collider_transform { + *collider_transform = new_collider_transform; + } } } @@ -254,7 +260,7 @@ pub(crate) fn propagate_collider_transforms( /// /// This is largely a clone of `propagate_transforms` in `bevy_transform`. #[allow(clippy::type_complexity)] -pub(crate) fn propagate_collider_transforms_old( +pub(crate) fn propagate_collider_transforms( mut root_query: Query< (Entity, Ref, &Children), (Without, With>), diff --git a/src/lib.rs b/src/lib.rs index 8bac052a..d476ef7f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -478,7 +478,7 @@ pub mod prelude { *, }, dynamics::{self, ccd::SpeculativeMargin, prelude::*}, - position::{Position, RigidBodyScale, Rotation}, + position::{Position, Rotation}, prepare::{init_transforms, update_mass_properties, PrepareConfig, PreparePlugin}, schedule::*, spatial_query::{self, *}, diff --git a/src/position.rs b/src/position.rs index c3b1ed97..c4616b2b 100644 --- a/src/position.rs +++ b/src/position.rs @@ -953,24 +953,3 @@ impl From for Rotation { #[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))] #[reflect(Debug, Component, Default, PartialEq)] pub struct PreviousRotation(pub Rotation); - -#[derive(Reflect, Clone, Copy, Component, Debug, Deref, DerefMut, PartialEq)] -#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))] -#[reflect(Debug, Component, Default, PartialEq)] -pub struct RigidBodyScale(pub Vector); - -impl Default for RigidBodyScale { - fn default() -> Self { - Self(Vector::ONE) - } -} - -impl From for Vec3 { - fn from(value: RigidBodyScale) -> Self { - let inner = value.0; - #[cfg(feature = "2d")] - let inner = inner.extend(1.0); - inner - } -} diff --git a/src/prepare.rs b/src/prepare.rs index 14985a0c..3ef976e0 100644 --- a/src/prepare.rs +++ b/src/prepare.rs @@ -188,10 +188,10 @@ impl Plugin for PreparePlugin { #[derive(Resource, Reflect, Clone, Debug, PartialEq, Eq)] #[reflect(Resource)] pub struct PrepareConfig { - /// Initializes [`Transform`] based on [`Position`], [`Rotation`], and [`RigidBodyScale`]. + /// Initializes [`Transform`] based on [`Position`] and [`Rotation`]. /// Defaults to true. pub position_to_transform: bool, - /// Initializes [`Position`], [`Rotation`], and [`RigidBodyScale`] based on [`Transform`]. + /// Initializes [`Position`] and [`Rotation`] based on [`Transform`]. /// Defaults to true. pub transform_to_position: bool, } @@ -222,7 +222,6 @@ pub fn init_transforms( Option<&GlobalTransform>, Option<&Position>, Option<&Rotation>, - Option<&RigidBodyScale>, Option<&PreviousRotation>, Option<&Parent>, Has, @@ -233,7 +232,6 @@ pub fn init_transforms( ( Option<&Position>, Option<&Rotation>, - Option<&RigidBodyScale>, Option<&GlobalTransform>, ), With, @@ -244,23 +242,13 @@ pub fn init_transforms( return; } - for ( - entity, - transform, - global_transform, - pos, - rot, - scale, - previous_rot, - parent, - has_rigid_body, - ) in &query + for (entity, transform, global_transform, pos, rot, previous_rot, parent, has_rigid_body) in + &query { let parent_transforms = parent.and_then(|parent| parents.get(parent.get()).ok()); - let parent_pos = parent_transforms.and_then(|(pos, _, _, _)| pos); - let parent_rot = parent_transforms.and_then(|(_, rot, _, _)| rot); - let parent_scale = parent_transforms.and_then(|(_, _, scale, _)| scale); - let parent_global_trans = parent_transforms.and_then(|(_, _, _, trans)| trans); + let parent_pos = parent_transforms.and_then(|(pos, _, _)| pos); + let parent_rot = parent_transforms.and_then(|(_, rot, _)| rot); + let parent_global_trans = parent_transforms.and_then(|(_, _, trans)| trans); let mut new_transform = if config.position_to_transform { Some(transform.copied().unwrap_or_default()) @@ -391,84 +379,6 @@ pub fn init_transforms( default() }; - let new_scale = if let Some(scale) = scale { - if let Some(transform) = &mut new_transform { - // Initialize new scale as global scale - #[cfg(feature = "2d")] - let mut new_scale = scale.f32().extend(transform.scale.z); - #[cfg(feature = "3d")] - let mut new_scale = scale.f32(); - - // If the body is a child, divide by the parent's global scale - // to get the local scale - if parent.is_some() { - if let Some(parent_scale) = parent_scale { - #[cfg(feature = "2d")] - { - new_scale /= parent_scale.f32().extend(new_scale.z); - } - #[cfg(feature = "3d")] - { - new_scale /= parent_scale.f32(); - } - } else if let Some(parent_transform) = parent_global_trans { - new_scale /= parent_transform.compute_transform().scale; - } - } - transform.scale = new_scale; - } - scale.0 - } else if config.transform_to_position { - let mut new_scale = Vector::ONE; - - if parent.is_some() { - let scale = transform.as_ref().map_or(Vec3::ONE, |t| t.scale); - if let Some(parent_scale) = parent_scale { - #[cfg(feature = "2d")] - { - new_scale = parent_scale.0 * scale.adjust_precision().truncate(); - } - #[cfg(feature = "3d")] - { - new_scale = parent_scale.0 * scale.adjust_precision(); - } - } else if let Some(parent_transform) = parent_global_trans { - let new_scale_unadjusted = parent_transform - .transform_point(transform.as_ref().map_or(Vec3::ONE, |t| t.scale)); - #[cfg(feature = "2d")] - { - new_scale = new_scale_unadjusted.truncate().adjust_precision(); - } - #[cfg(feature = "3d")] - { - new_scale = new_scale_unadjusted.adjust_precision(); - } - } - } else { - #[cfg(feature = "2d")] - { - new_scale = transform - .map(|t| t.scale.truncate().adjust_precision()) - .unwrap_or(global_transform.as_ref().map_or(Vector::ONE, |t| { - let t = t.compute_transform(); - Vector::new(t.scale.x as Scalar, t.scale.y as Scalar) - })); - } - #[cfg(feature = "3d")] - { - new_scale = transform.map(|t| t.scale.adjust_precision()).unwrap_or( - global_transform.as_ref().map_or(Vec3::ONE, |t| { - t.compute_transform().scale.adjust_precision() - }), - ) - } - }; - - new_scale - } else { - Vector::ONE - }; - let mut cmds = commands.entity(entity); // Insert the position and rotation. @@ -481,7 +391,6 @@ pub fn init_transforms( cmds.try_insert(( Position(new_position), new_rotation, - RigidBodyScale(new_scale), PreSolveAccumulatedTranslation::default(), *previous_rot.unwrap_or(&PreviousRotation(new_rotation)), PreSolveRotation::default(), @@ -492,26 +401,16 @@ pub fn init_transforms( transform, Position(new_position), new_rotation, - RigidBodyScale(new_scale), PreSolveAccumulatedTranslation::default(), *previous_rot.unwrap_or(&PreviousRotation(new_rotation)), PreSolveRotation::default(), )); } (false, None) => { - cmds.try_insert(( - Position(new_position), - new_rotation, - RigidBodyScale(new_scale), - )); + cmds.try_insert((Position(new_position), new_rotation)); } (false, Some(transform)) => { - cmds.try_insert(( - transform, - Position(new_position), - new_rotation, - RigidBodyScale(new_scale), - )); + cmds.try_insert((transform, Position(new_position), new_rotation)); } } } From c468ae80f58c0b64b97e75b80bf44152d620cb0c Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Thu, 12 Sep 2024 10:23:40 +0200 Subject: [PATCH 08/12] Remove unused function --- src/collision/collider/hierarchy.rs | 38 ----------------------------- 1 file changed, 38 deletions(-) diff --git a/src/collision/collider/hierarchy.rs b/src/collision/collider/hierarchy.rs index 4cddbf77..6b112d5f 100644 --- a/src/collision/collider/hierarchy.rs +++ b/src/collision/collider/hierarchy.rs @@ -216,44 +216,6 @@ pub(crate) fn update_child_collider_position( // is a collider or is a `AncestorMarker`. type ShouldPropagate = Or<(With>, With)>; -pub(crate) fn propagate_collider_transforms_new( - mut collider_query: Query<( - Entity, - &mut ColliderTransform, - &ColliderParent, - &GlobalTransform, - )>, - transform_query: Query<(&Position, &Rotation)>, -) { - for (entity, mut collider_transform, parent, global_transform) in &mut collider_query { - let parent = parent.get(); - let Ok((&collider_pos, &collider_rot)) = transform_query.get(entity) else { - error!("Entity {entity}'s collider has no position or rotation.",); - continue; - }; - let Ok((&parent_pos, &parent_rot)) = transform_query.get(parent) else { - error!("Entity {entity}'s collider parent {parent} has no position or rotation.",); - continue; - }; - let global_scale = global_transform.compute_transform().scale; - let parent_global_transform = GlobalTransform::from(Transform { - translation: parent_pos.into(), - rotation: parent_rot.into(), - scale: Vec3::ONE, - }); - let collider_global_transform = GlobalTransform::from(Transform { - translation: collider_pos.into(), - rotation: collider_rot.into(), - scale: global_scale, - }); - let relative_transform = collider_global_transform.reparented_to(&parent_global_transform); - let new_collider_transform = ColliderTransform::from(relative_transform); - if collider_transform.as_ref() != &new_collider_transform { - *collider_transform = new_collider_transform; - } - } -} - /// Updates [`ColliderTransform`]s based on entity hierarchies. Each transform is computed by recursively /// traversing the children of each rigid body and adding their transforms together to form /// the total transform relative to the body. From 44623946fa2634e329810b8e3d1c11664182fed8 Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Thu, 12 Sep 2024 10:24:25 +0200 Subject: [PATCH 09/12] Remove unused impl --- src/position.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/position.rs b/src/position.rs index c4616b2b..8d519f04 100644 --- a/src/position.rs +++ b/src/position.rs @@ -98,15 +98,6 @@ impl From<&GlobalTransform> for Position { } } -impl From for Vec3 { - fn from(value: Position) -> Self { - let inner = value.0; - #[cfg(feature = "2d")] - let inner = inner.extend(0.0); - inner - } -} - /// The translation accumulated before the XPBD position solve. #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] From 68d99f97d3e2980313a73734f9e396ff3a8115ff Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Thu, 12 Sep 2024 17:14:19 +0200 Subject: [PATCH 10/12] Fix fail --- src/collision/collider/backend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index 2896ea02..9f567cb6 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -137,7 +137,7 @@ impl Plugin for ColliderBackendPlugin { .entity_mut(entity) .get_mut::() .unwrap() - .set_scale(scale, 10); + .set_scale(scale.into(), 10); let entity_ref = world.entity(entity); let collider = entity_ref.get::().unwrap(); From 487d268f21c1c6db35f15852fb5eb4b3ccb1dc9c Mon Sep 17 00:00:00 2001 From: Jan Hohenheim Date: Wed, 18 Sep 2024 21:36:23 +0200 Subject: [PATCH 11/12] Update backend.rs --- src/collision/collider/backend.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index 9f567cb6..2fbfb525 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -137,7 +137,7 @@ impl Plugin for ColliderBackendPlugin { .entity_mut(entity) .get_mut::() .unwrap() - .set_scale(scale.into(), 10); + .set_scale(scale.adjust_precision(), 10); let entity_ref = world.entity(entity); let collider = entity_ref.get::().unwrap(); From 5906446beccf57133dd632955dd476dda931e299 Mon Sep 17 00:00:00 2001 From: Joona Aalto Date: Thu, 26 Sep 2024 14:14:48 +0300 Subject: [PATCH 12/12] Fix typos --- src/collision/collider/backend.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/collision/collider/backend.rs b/src/collision/collider/backend.rs index 2fbfb525..a6cedb39 100644 --- a/src/collision/collider/backend.rs +++ b/src/collision/collider/backend.rs @@ -107,8 +107,8 @@ impl Plugin for ColliderBackendPlugin { // This collider *may* have been `spawn`ed directly on a new entity. // As such, its global transform is not yet available. // You may notice that this will fail if the hierarchy's scale was updated in this - // frame. Remember that `GlobalTransform` is not updated inbetween fixed updates. - // But this is fine, as `update_collider_scale` will be updated in the next fixed update anyways. + // frame. Remember that `GlobalTransform` is not updated in between fixed updates. + // But this is fine, as `update_collider_scale` will be updated in the next fixed update anyway. // The reason why we care about initializing this scale here is for those users that opted out of // `update_collider_scale` in order to do their own interpolation, which implies that they won't touch // the `Transform` component before the collider is initialized, which in turn means that it will @@ -130,7 +130,7 @@ impl Plugin for ColliderBackendPlugin { #[cfg(feature = "2d")] let scale = scale.xy(); - // Make sure the is initialized with the correct scale. + // Make sure the collider is initialized with the correct scale. // This overwrites the scale set by the constructor, but that one is // meant to be only changed after initialization. world