From 7a97c56dfb12cc9cf5c702d542b0b7d5866b4189 Mon Sep 17 00:00:00 2001 From: Tim Irmler <42088586+zwazel@users.noreply.github.com> Date: Mon, 12 Feb 2024 23:34:25 +0100 Subject: [PATCH] fix raycast does not follow entity transform without rigidbody (#310) # Objective - Fixes #288 ## Solution - If no Rotation and Position is available, fall back to GlobalTransform ## Changelog - I've added a `From` and `From<&GlobalTransform>` to `Position` as well as a `From<&GlobalTransform>` to `Rotation`. as a `From` already existed. I tested the updated `update_ray_caster_positions` system in my personal project, and it fixed the problem for me. I currently can't test the `update_shape_caster_positions` but as it is basically the same changes, i'd be surprised if it wouldn't work. as this is my first time contributing to this crate, i'm not sure if the code is what is expected. --------- Co-authored-by: Joona Aalto --- src/components/mod.rs | 38 ++++++++ src/components/rotation.rs | 6 ++ src/plugins/spatial_query/mod.rs | 144 +++++++++++++++++++------------ 3 files changed, 135 insertions(+), 53 deletions(-) diff --git a/src/components/mod.rs b/src/components/mod.rs index 1581f011..35aa755b 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -331,6 +331,44 @@ impl Position { } } +impl From for Position { + #[cfg(feature = "2d")] + fn from(value: GlobalTransform) -> Self { + Self::from_xy( + value.translation().adjust_precision().x, + value.translation().adjust_precision().y, + ) + } + + #[cfg(feature = "3d")] + fn from(value: GlobalTransform) -> Self { + Self::from_xyz( + value.translation().adjust_precision().x, + value.translation().adjust_precision().y, + value.translation().adjust_precision().z, + ) + } +} + +impl From<&GlobalTransform> for Position { + #[cfg(feature = "2d")] + fn from(value: &GlobalTransform) -> Self { + Self::from_xy( + value.translation().adjust_precision().x, + value.translation().adjust_precision().y, + ) + } + + #[cfg(feature = "3d")] + fn from(value: &GlobalTransform) -> Self { + Self::from_xyz( + value.translation().adjust_precision().x, + value.translation().adjust_precision().y, + value.translation().adjust_precision().z, + ) + } +} + /// The position of a [rigid body](RigidBody) at the start of a substep. #[derive(Reflect, Clone, Copy, Component, Debug, Default, Deref, DerefMut, PartialEq, From)] #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] diff --git a/src/components/rotation.rs b/src/components/rotation.rs index ff838fbf..370821c8 100644 --- a/src/components/rotation.rs +++ b/src/components/rotation.rs @@ -277,6 +277,12 @@ impl From for Rotation { } } +impl From<&GlobalTransform> for Rotation { + fn from(value: &GlobalTransform) -> Self { + Self::from(value.compute_transform().rotation) + } +} + #[cfg(feature = "2d")] impl From for Rotation { fn from(quat: Quat) -> Self { diff --git a/src/plugins/spatial_query/mod.rs b/src/plugins/spatial_query/mod.rs index 83cfe8cb..184a37f1 100644 --- a/src/plugins/spatial_query/mod.rs +++ b/src/plugins/spatial_query/mod.rs @@ -241,45 +241,64 @@ type RayCasterPositionQueryComponents = ( Option<&'static Position>, Option<&'static Rotation>, Option<&'static Parent>, + Option<&'static GlobalTransform>, ); +#[allow(clippy::type_complexity)] fn update_ray_caster_positions( mut rays: Query, - parents: Query<(Option<&Position>, Option<&Rotation>), With>, + parents: Query< + ( + Option<&Position>, + Option<&Rotation>, + Option<&GlobalTransform>, + ), + With, + >, ) { - for (mut ray, position, rotation, parent) in &mut rays { + for (mut ray, position, rotation, parent, transform) in &mut rays { let origin = ray.origin; let direction = ray.direction; - if let Some(position) = position { - ray.set_global_origin(position.0 + rotation.map_or(origin, |rot| rot.rotate(origin))); + let global_position = position.copied().or(transform.map(Position::from)); + let global_rotation = rotation.copied().or(transform.map(Rotation::from)); + + if let Some(global_position) = global_position { + ray.set_global_origin( + global_position.0 + rotation.map_or(origin, |rot| rot.rotate(origin)), + ); } else if parent.is_none() { ray.set_global_origin(origin); } - if let Some(rotation) = rotation { - let global_direction = rotation.rotate(ray.direction); + if let Some(global_rotation) = global_rotation { + let global_direction = global_rotation.rotate(ray.direction); ray.set_global_direction(global_direction); } else if parent.is_none() { ray.set_global_direction(direction); } - if let Some(parent) = parent { - if let Ok((parent_position, parent_rotation)) = parents.get(parent.get()) { - if position.is_none() { - if let Some(position) = parent_position { - let rotation = rotation.map_or( - parent_rotation.map_or(Rotation::default(), |rot| *rot), - |rot| *rot, - ); - ray.set_global_origin(position.0 + rotation.rotate(origin)); - } + if let Some(Ok((parent_position, parent_rotation, parent_transform))) = + parent.map(|p| parents.get(p.get())) + { + let parent_position = parent_position + .copied() + .or(parent_transform.map(Position::from)); + let parent_rotation = parent_rotation + .copied() + .or(parent_transform.map(Rotation::from)); + + // Apply parent transformations + if global_position.is_none() { + if let Some(position) = parent_position { + let rotation = global_rotation.unwrap_or(parent_rotation.unwrap_or_default()); + ray.set_global_origin(position.0 + rotation.rotate(origin)); } - if rotation.is_none() { - if let Some(rotation) = parent_rotation { - let global_direction = rotation.rotate(ray.direction); - ray.set_global_direction(global_direction); - } + } + if global_rotation.is_none() { + if let Some(rotation) = parent_rotation { + let global_direction = rotation.rotate(ray.direction); + ray.set_global_direction(global_direction); } } } @@ -291,34 +310,48 @@ type ShapeCasterPositionQueryComponents = ( Option<&'static Position>, Option<&'static Rotation>, Option<&'static Parent>, + Option<&'static GlobalTransform>, ); +#[allow(clippy::type_complexity)] fn update_shape_caster_positions( mut shape_casters: Query, - parents: Query<(Option<&Position>, Option<&Rotation>), With>, + parents: Query< + ( + Option<&Position>, + Option<&Rotation>, + Option<&GlobalTransform>, + ), + With, + >, ) { - for (mut shape_caster, position, rotation, parent) in &mut shape_casters { + for (mut shape_caster, position, rotation, parent, transform) in &mut shape_casters { let origin = shape_caster.origin; let shape_rotation = shape_caster.shape_rotation; let direction = shape_caster.direction; - if let Some(position) = position { - shape_caster - .set_global_origin(position.0 + rotation.map_or(origin, |rot| rot.rotate(origin))); + let global_position = position.copied().or(transform.map(Position::from)); + let global_rotation = rotation.copied().or(transform.map(Rotation::from)); + + if let Some(global_position) = global_position { + shape_caster.set_global_origin( + global_position.0 + rotation.map_or(origin, |rot| rot.rotate(origin)), + ); } else if parent.is_none() { shape_caster.set_global_origin(origin); } - if let Some(rotation) = rotation { - let global_direction = rotation.rotate(shape_caster.direction); + if let Some(global_rotation) = global_rotation { + let global_direction = global_rotation.rotate(shape_caster.direction); shape_caster.set_global_direction(global_direction); #[cfg(feature = "2d")] { - shape_caster.set_global_shape_rotation(shape_rotation + rotation.as_radians()); + shape_caster + .set_global_shape_rotation(shape_rotation + global_rotation.as_radians()); } #[cfg(feature = "3d")] { - shape_caster.set_global_shape_rotation(shape_rotation + rotation.0); + shape_caster.set_global_shape_rotation(shape_rotation + global_rotation.0); } } else if parent.is_none() { shape_caster.set_global_direction(direction); @@ -332,30 +365,35 @@ fn update_shape_caster_positions( } } - if let Some(parent) = parent { - if let Ok((parent_position, parent_rotation)) = parents.get(parent.get()) { - if position.is_none() { - if let Some(position) = parent_position { - let rotation = rotation.map_or( - parent_rotation.map_or(Rotation::default(), |rot| *rot), - |rot| *rot, - ); - shape_caster.set_global_origin(position.0 + rotation.rotate(origin)); - } + if let Some(Ok((parent_position, parent_rotation, parent_transform))) = + parent.map(|p| parents.get(p.get())) + { + let parent_position = parent_position + .copied() + .or(parent_transform.map(Position::from)); + let parent_rotation = parent_rotation + .copied() + .or(parent_transform.map(Rotation::from)); + + // Apply parent transformations + if global_position.is_none() { + if let Some(position) = parent_position { + let rotation = global_rotation.unwrap_or(parent_rotation.unwrap_or_default()); + shape_caster.set_global_origin(position.0 + rotation.rotate(origin)); } - if rotation.is_none() { - if let Some(rotation) = parent_rotation { - let global_direction = rotation.rotate(shape_caster.direction); - shape_caster.set_global_direction(global_direction); - #[cfg(feature = "2d")] - { - shape_caster - .set_global_shape_rotation(shape_rotation + rotation.as_radians()); - } - #[cfg(feature = "3d")] - { - shape_caster.set_global_shape_rotation(shape_rotation + rotation.0); - } + } + if global_rotation.is_none() { + if let Some(rotation) = parent_rotation { + let global_direction = rotation.rotate(shape_caster.direction); + shape_caster.set_global_direction(global_direction); + #[cfg(feature = "2d")] + { + shape_caster + .set_global_shape_rotation(shape_rotation + rotation.as_radians()); + } + #[cfg(feature = "3d")] + { + shape_caster.set_global_shape_rotation(shape_rotation + rotation.0); } } }