smooth_bevy_cameras/
look_transform.rs1use bevy::{
2 app::prelude::*, ecs::prelude::*, math::prelude::*, prelude::ReflectDefault, reflect::Reflect,
3 transform::components::Transform,
4};
5
6pub struct LookTransformPlugin;
7
8impl Plugin for LookTransformPlugin {
9 fn build(&self, app: &mut App) {
10 app.add_systems(Update, look_transform_system);
11 }
12}
13
14#[derive(Bundle, Clone)]
15pub struct LookTransformBundle {
16 pub transform: LookTransform,
17 pub smoother: Smoother,
18}
19
20#[derive(Component, Debug, PartialEq, Clone, Copy, Reflect)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[reflect(Component, Default, Debug, PartialEq)]
25pub struct LookTransform {
26 pub eye: Vec3,
27 pub target: Vec3,
28 pub up: Vec3,
29}
30
31impl From<LookTransform> for Transform {
32 fn from(t: LookTransform) -> Self {
33 eye_look_at_target_transform(t.eye, t.target, t.up)
34 }
35}
36
37impl Default for LookTransform {
38 fn default() -> Self {
39 Self {
40 eye: Vec3::default(),
41 target: Vec3::default(),
42 up: Vec3::Y,
43 }
44 }
45}
46
47impl LookTransform {
48 pub fn new(eye: Vec3, target: Vec3, up: Vec3) -> Self {
49 Self { eye, target, up }
50 }
51
52 pub fn radius(&self) -> f32 {
53 (self.target - self.eye).length()
54 }
55
56 pub fn look_direction(&self) -> Option<Vec3> {
57 (self.target - self.eye).try_normalize()
58 }
59}
60
61fn eye_look_at_target_transform(eye: Vec3, target: Vec3, up: Vec3) -> Transform {
62 let look_vector = (target - eye).normalize();
64 let look_at = eye + look_vector;
65
66 Transform::from_translation(eye).looking_at(look_at, up)
67}
68
69#[derive(Clone, Component, Copy, Debug, Reflect)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
72#[reflect(Component, Default, Debug)]
73pub struct Smoother {
74 lag_weight: f32,
75 lerp_tfm: Option<LookTransform>,
76 enabled: bool,
77}
78
79impl Default for Smoother {
80 fn default() -> Self {
81 Self {
82 lag_weight: 0.9,
83 lerp_tfm: Some(LookTransform::default()),
84 enabled: true,
85 }
86 }
87}
88
89impl Smoother {
90 pub fn new(lag_weight: f32) -> Self {
91 Self {
92 lag_weight,
93 lerp_tfm: None,
94 enabled: true,
95 }
96 }
97
98 pub(crate) fn set_enabled(&mut self, enabled: bool) {
99 self.enabled = enabled;
100 if self.enabled {
101 self.reset();
104 }
105 }
106
107 pub fn set_lag_weight(&mut self, lag_weight: f32) {
108 self.lag_weight = lag_weight;
109 }
110
111 pub fn smooth_transform(&mut self, new_tfm: &LookTransform) -> LookTransform {
112 debug_assert!(0.0 <= self.lag_weight);
113 debug_assert!(self.lag_weight < 1.0);
114
115 let old_lerp_tfm = self.lerp_tfm.unwrap_or(*new_tfm);
116
117 let lead_weight = 1.0 - self.lag_weight;
118 let lerp_tfm = LookTransform {
119 eye: old_lerp_tfm.eye * self.lag_weight + new_tfm.eye * lead_weight,
120 target: old_lerp_tfm.target * self.lag_weight + new_tfm.target * lead_weight,
121 up: new_tfm.up,
122 };
123
124 self.lerp_tfm = Some(lerp_tfm);
125
126 lerp_tfm
127 }
128
129 pub fn reset(&mut self) {
130 self.lerp_tfm = None;
131 }
132}
133
134pub fn look_transform_system(
135 mut cameras: Query<(&LookTransform, &mut Transform, Option<&mut Smoother>)>,
136) {
137 for (look_transform, mut scene_transform, smoother) in cameras.iter_mut() {
138 match smoother {
139 Some(mut s) if s.enabled => {
140 *scene_transform = s.smooth_transform(look_transform).into()
141 }
142 _ => (),
143 };
144 }
145}