From 6723badf61b0e43e7a307af7fab5a5f50e7501d3 Mon Sep 17 00:00:00 2001 From: ira Date: Fri, 1 Jul 2022 03:58:54 +0000 Subject: [PATCH] Add helper methods for rotating `Transform`s (#5151) # Objective Users often ask for help with rotations as they struggle with `Quat`s. `Quat` is rather complex and has a ton of verbose methods. ## Solution Add rotation helper methods to `Transform`. Co-authored-by: devil-ira --- crates/bevy_animation/src/lib.rs | 4 +- crates/bevy_gltf/src/loader.rs | 2 +- .../src/components/transform.rs | 77 +++++++++++++++---- examples/2d/rotation.rs | 14 +--- examples/3d/lighting.rs | 6 +- examples/3d/parenting.rs | 2 +- examples/3d/render_to_texture.rs | 8 +- examples/3d/shapes.rs | 4 +- examples/ecs/hierarchy.rs | 4 +- examples/games/alien_cake_addict.rs | 2 +- examples/games/contributors.rs | 2 +- examples/shader/post_processing.rs | 4 +- examples/stress_tests/many_cubes.rs | 5 +- examples/stress_tests/many_foxes.rs | 4 +- examples/stress_tests/many_lights.rs | 5 +- examples/stress_tests/many_sprites.rs | 2 +- examples/tools/scene_viewer.rs | 6 +- examples/transforms/3d_rotation.rs | 18 ++--- examples/transforms/transform.rs | 4 +- examples/window/low_power.rs | 5 +- 20 files changed, 111 insertions(+), 67 deletions(-) diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index 7882ad4d2f274..ebacc5d8ee754 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -262,8 +262,8 @@ pub fn animation_player( rot_end = -rot_end; } // Rotations are using a spherical linear interpolation - transform.rotation = Quat::from_array(rot_start.normalize().into()) - .slerp(Quat::from_array(rot_end.normalize().into()), lerp); + transform.rotation = + rot_start.normalize().slerp(rot_end.normalize(), lerp); } Keyframes::Translation(keyframes) => { let translation_start = keyframes[step_start]; diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 39891699345e7..f18e2dcc7e595 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -368,7 +368,7 @@ async fn load_gltf<'a, 'b>( scale, } => Transform { translation: bevy_math::Vec3::from(translation), - rotation: bevy_math::Quat::from_vec4(rotation.into()), + rotation: bevy_math::Quat::from_array(rotation), scale: bevy_math::Vec3::from(scale), }, }, diff --git a/crates/bevy_transform/src/components/transform.rs b/crates/bevy_transform/src/components/transform.rs index bbca98e76961d..a26793391dc5e 100644 --- a/crates/bevy_transform/src/components/transform.rs +++ b/crates/bevy_transform/src/components/transform.rs @@ -195,20 +195,81 @@ impl Transform { self.local_z() } - /// Rotates the transform by the given rotation. + /// Rotates this [`Transform`] by the given rotation. #[inline] pub fn rotate(&mut self, rotation: Quat) { self.rotation = rotation * self.rotation; } - /// Rotates this [`Transform`] around a point in space. - /// If the point is a zero vector, this will rotate around the parent (if any) or the origin. + /// Rotates this [`Transform`] around the given `axis` by `angle` (in radians). + /// + /// If this [`Transform`] has a parent, the `axis` is relative to the rotation of the parent. + #[inline] + pub fn rotate_axis(&mut self, axis: Vec3, angle: f32) { + self.rotate(Quat::from_axis_angle(axis, angle)); + } + + /// Rotates this [`Transform`] around the X axis by `angle` (in radians). + /// + /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent. + #[inline] + pub fn rotate_x(&mut self, angle: f32) { + self.rotate(Quat::from_rotation_x(angle)); + } + + /// Rotates this [`Transform`] around the Y axis by `angle` (in radians). + /// + /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent. + #[inline] + pub fn rotate_y(&mut self, angle: f32) { + self.rotate(Quat::from_rotation_y(angle)); + } + + /// Rotates this [`Transform`] around the Z axis by `angle` (in radians). + /// + /// If this [`Transform`] has a parent, the axis is relative to the rotation of the parent. + #[inline] + pub fn rotate_z(&mut self, angle: f32) { + self.rotate(Quat::from_rotation_z(angle)); + } + + /// Rotates this [`Transform`] around its X axis by `angle` (in radians). + #[inline] + pub fn rotate_local_x(&mut self, angle: f32) { + self.rotate_axis(self.local_x(), angle); + } + + /// Rotates this [`Transform`] around its Y axis by `angle` (in radians). + #[inline] + pub fn rotate_local_y(&mut self, angle: f32) { + self.rotate_axis(self.local_y(), angle); + } + + /// Rotates this [`Transform`] around its Z axis by `angle` (in radians). + #[inline] + pub fn rotate_local_z(&mut self, angle: f32) { + self.rotate_axis(self.local_z(), angle); + } + + /// Rotates this [`Transform`] around a `point` in space. + /// + /// If this [`Transform`] has a parent, the `point` is relative to the [`Transform`] of the parent. #[inline] pub fn rotate_around(&mut self, point: Vec3, rotation: Quat) { self.translation = point + rotation * (self.translation - point); self.rotation *= rotation; } + /// Rotates this [`Transform`] so that its local negative z direction is toward + /// `target` and its local y direction is toward `up`. + #[inline] + pub fn look_at(&mut self, target: Vec3, up: Vec3) { + let forward = Vec3::normalize(self.translation - target); + let right = up.cross(forward).normalize(); + let up = forward.cross(right); + self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, forward)); + } + /// Multiplies `self` with `transform` component by component, returning the /// resulting [`Transform`] #[inline] @@ -239,16 +300,6 @@ impl Transform { pub fn apply_non_uniform_scale(&mut self, scale_factor: Vec3) { self.scale *= scale_factor; } - - /// Rotates this [`Transform`] so that its local z direction is toward - /// `target` and its local y direction is toward `up`. - #[inline] - pub fn look_at(&mut self, target: Vec3, up: Vec3) { - let forward = Vec3::normalize(self.translation - target); - let right = up.cross(forward).normalize(); - let up = forward.cross(right); - self.rotation = Quat::from_mat3(&Mat3::from_cols(right, up, forward)); - } } impl Default for Transform { diff --git a/examples/2d/rotation.rs b/examples/2d/rotation.rs index d38b911d23f4d..846befa857f8a 100644 --- a/examples/2d/rotation.rs +++ b/examples/2d/rotation.rs @@ -134,10 +134,8 @@ fn player_movement_system( movement_factor += 1.0; } - // create the change in rotation around the Z axis (perpendicular to the 2D plane of the screen) - let rotation_delta = Quat::from_rotation_z(rotation_factor * ship.rotation_speed * TIME_STEP); - // update the ship rotation with our rotation delta - transform.rotation *= rotation_delta; + // update the ship rotation around the Z axis (perpendicular to the 2D plane of the screen) + transform.rotate_z(rotation_factor * ship.rotation_speed * TIME_STEP); // get the ship's forward vector by applying the current rotation to the ships initial facing vector let movement_direction = transform.rotation * Vec3::Y; @@ -168,7 +166,7 @@ fn snap_to_player_system( // get the quaternion to rotate from the initial enemy facing direction to the direction // facing the player - let rotate_to_player = Quat::from_rotation_arc(Vec3::Y, Vec3::from((to_player, 0.0))); + let rotate_to_player = Quat::from_rotation_arc(Vec3::Y, to_player.extend(0.)); // rotate the enemy to face the player enemy_transform.rotation = rotate_to_player; @@ -243,11 +241,7 @@ fn rotate_to_player_system( // calculate angle of rotation with limit let rotation_angle = rotation_sign * (config.rotation_speed * TIME_STEP).min(max_angle); - // get the quaternion to rotate from the current enemy facing direction towards the - // direction facing the player - let rotation_delta = Quat::from_rotation_z(rotation_angle); - // rotate the enemy to face the player - enemy_transform.rotation *= rotation_delta; + enemy_transform.rotate_z(rotation_angle); } } diff --git a/examples/3d/lighting.rs b/examples/3d/lighting.rs index 10fff72614aa5..e8a1a067b7c13 100644 --- a/examples/3d/lighting.rs +++ b/examples/3d/lighting.rs @@ -34,7 +34,7 @@ fn setup( // left wall let mut transform = Transform::from_xyz(2.5, 2.5, 0.0); - transform.rotate(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2)); + transform.rotate_z(std::f32::consts::FRAC_PI_2); commands.spawn_bundle(PbrBundle { mesh: meshes.add(Mesh::from(shape::Box::new(5.0, 0.15, 5.0))), transform, @@ -47,7 +47,7 @@ fn setup( }); // back (right) wall let mut transform = Transform::from_xyz(0.0, 2.5, -2.5); - transform.rotate(Quat::from_rotation_x(std::f32::consts::FRAC_PI_2)); + transform.rotate_x(std::f32::consts::FRAC_PI_2); commands.spawn_bundle(PbrBundle { mesh: meshes.add(Mesh::from(shape::Box::new(5.0, 0.15, 5.0))), transform, @@ -214,7 +214,7 @@ fn animate_light_direction( mut query: Query<&mut Transform, With>, ) { for mut transform in query.iter_mut() { - transform.rotate(Quat::from_rotation_y(time.delta_seconds() * 0.5)); + transform.rotate_y(time.delta_seconds() * 0.5); } } diff --git a/examples/3d/parenting.rs b/examples/3d/parenting.rs index b8a4c99237d0a..06f489c011964 100644 --- a/examples/3d/parenting.rs +++ b/examples/3d/parenting.rs @@ -18,7 +18,7 @@ struct Rotator; /// rotates the parent, which will result in the child also rotating fn rotator_system(time: Res