veloren_common/util/
dir.rs

1use super::{Plane, Projection};
2use rand::Rng;
3use serde::{Deserialize, Serialize};
4use tracing::warn;
5use vek::*;
6
7/// Type representing a direction using Vec3 that is normalized and NaN free
8/// These properties are enforced actively via panics when `debug_assertions` is
9/// enabled
10#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
11#[serde(into = "SerdeDir")]
12#[serde(from = "SerdeDir")]
13pub struct Dir(Vec3<f32>);
14impl Default for Dir {
15    fn default() -> Self { Self::forward() }
16}
17
18// Validate at Deserialization
19#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
20struct SerdeDir(Vec3<f32>);
21impl From<SerdeDir> for Dir {
22    fn from(dir: SerdeDir) -> Self {
23        let dir = dir.0;
24        if dir.map(f32::is_nan).reduce_or() {
25            warn!(
26                ?dir,
27                "Deserialized dir containing NaNs, replacing with default"
28            );
29            Default::default()
30        } else if !dir.is_normalized() {
31            warn!(
32                ?dir,
33                "Deserialized unnormalized dir, replacing with default"
34            );
35            Default::default()
36        } else {
37            Self(dir)
38        }
39    }
40}
41
42impl From<Dir> for SerdeDir {
43    fn from(other: Dir) -> SerdeDir { SerdeDir(*other) }
44}
45/*pub enum TryFromVec3Error {
46    ContainsNans,
47    NotNormalized,
48}
49
50impl TryFrom<Vec3<f32>> for Dir {
51    type Error = TryFromVec3Error;
52
53    fn try_from(v: Vec3) -> Result<Self, TryFromVec3Error> {
54        if v.map(f32::is_nan).reduce_or() {
55            Err(TryFromVec3Error::ContainsNans)
56        } else {
57            v.try_normalized()
58                .map(|n| Self(n))
59                .ok_or(TryFromVec3Error::NotNormalized)
60        }
61    }
62}*/
63
64impl Dir {
65    pub fn new(dir: Vec3<f32>) -> Self {
66        debug_assert!(!dir.map(f32::is_nan).reduce_or());
67        debug_assert!(dir.is_normalized());
68        Self(dir)
69    }
70
71    pub fn from_unnormalized(dirs: Vec3<f32>) -> Option<Self> {
72        dirs.try_normalized().map(|dir| {
73            #[cfg(debug_assertions)]
74            {
75                if dir.map(f32::is_nan).reduce_or() {
76                    panic!("{} => {}", dirs, dir);
77                }
78            }
79            Self(dir)
80        })
81    }
82
83    /// Generates a random direction that has a z component of 0
84    pub fn random_2d(rng: &mut impl Rng) -> Self {
85        let a = rng.gen_range(0.0..std::f32::consts::TAU);
86        // This will always be normalized.
87        Self::new(Vec3::new(a.cos(), a.sin(), 0.0))
88    }
89
90    pub fn slerp(from: Self, to: Self, factor: f32) -> Self {
91        Self(slerp_normalized(from.0, to.0, factor))
92    }
93
94    #[must_use]
95    pub fn slerped_to(self, to: Self, factor: f32) -> Self {
96        Self(slerp_normalized(self.0, to.0, factor))
97    }
98
99    /// Note: this uses `from` if `to` is unnormalizable
100    pub fn slerp_to_vec3(from: Self, to: Vec3<f32>, factor: f32) -> Self {
101        Self(slerp_to_unnormalized(from.0, to, factor).unwrap_or_else(|e| e))
102    }
103
104    pub fn rotation_between(&self, to: Self) -> Quaternion<f32> {
105        Quaternion::<f32>::rotation_from_to_3d(self.0, to.0)
106    }
107
108    pub fn rotation(&self) -> Quaternion<f32> { Self::default().rotation_between(*self) }
109
110    pub fn is_valid(&self) -> bool { !self.0.map(f32::is_nan).reduce_or() && self.is_normalized() }
111
112    pub fn up() -> Self { Dir::new(Vec3::<f32>::unit_z()) }
113
114    pub fn down() -> Self { -Dir::new(Vec3::<f32>::unit_z()) }
115
116    pub fn left() -> Self { -Dir::new(Vec3::<f32>::unit_x()) }
117
118    pub fn right() -> Self { Dir::new(Vec3::<f32>::unit_x()) }
119
120    pub fn forward() -> Self { Dir::new(Vec3::<f32>::unit_y()) }
121
122    pub fn back() -> Self { -Dir::new(Vec3::<f32>::unit_y()) }
123
124    pub fn to_horizontal(self) -> Option<Self> { Self::from_unnormalized(self.xy().into()) }
125
126    pub fn vec(&self) -> &Vec3<f32> { &self.0 }
127
128    pub fn to_vec(self) -> Vec3<f32> { self.0 }
129}
130
131impl std::ops::Deref for Dir {
132    type Target = Vec3<f32>;
133
134    fn deref(&self) -> &Vec3<f32> { &self.0 }
135}
136
137impl From<Dir> for Vec3<f32> {
138    fn from(dir: Dir) -> Self { *dir }
139}
140
141impl Projection<Plane> for Dir {
142    type Output = Option<Self>;
143
144    fn projected(self, plane: &Plane) -> Self::Output {
145        Dir::from_unnormalized(plane.projection(*self))
146    }
147}
148
149impl Projection<Dir> for Vec3<f32> {
150    type Output = Vec3<f32>;
151
152    fn projected(self, dir: &Dir) -> Self::Output {
153        let dir = **dir;
154        self.dot(dir) * dir
155    }
156}
157
158impl std::ops::Mul<Dir> for Quaternion<f32> {
159    type Output = Dir;
160
161    fn mul(self, dir: Dir) -> Self::Output { Dir((self * *dir).normalized()) }
162}
163
164impl std::ops::Neg for Dir {
165    type Output = Dir;
166
167    fn neg(self) -> Dir { Dir::new(-self.0) }
168}
169
170/// Begone ye NaN's
171/// Slerp two `Vec3`s skipping the slerp if their directions are very close
172/// This avoids a case where `vek`s slerp produces NaN's
173/// Additionally, it avoids unnecessary calculations if they are near identical
174/// Assumes `from` and `to` are normalized and returns a normalized vector
175#[inline(always)]
176fn slerp_normalized(from: Vec3<f32>, to: Vec3<f32>, factor: f32) -> Vec3<f32> {
177    debug_assert!(!to.map(f32::is_nan).reduce_or());
178    debug_assert!(!from.map(f32::is_nan).reduce_or());
179    // Ensure from is normalized
180    #[cfg(debug_assertions)]
181    {
182        let unnormalized = {
183            let len_sq = from.magnitude_squared();
184            !(0.999..=1.001).contains(&len_sq)
185        };
186
187        if unnormalized {
188            panic!("Called slerp_normalized with unnormalized `from`: {}", from);
189        }
190    }
191
192    // Ensure to is normalized
193    #[cfg(debug_assertions)]
194    {
195        let unnormalized = {
196            let len_sq = from.magnitude_squared();
197            !(0.999..=1.001).contains(&len_sq)
198        };
199
200        if unnormalized {
201            panic!("Called slerp_normalized with unnormalized `to`: {}", to);
202        }
203    }
204
205    let dot = from.dot(to);
206    if dot >= 1.0 - 1E-6 {
207        // Close together, just use to
208        return to;
209    }
210
211    let (from, to, factor) = if dot < -0.999 {
212        // Not linearly independent (slerp will fail since it doesn't check for this)
213        // Instead we will choose a midpoint and slerp from or to that depending on the
214        // factor
215        let mid_dir = if from.z.abs() > 0.999 {
216            // If vec's lie along the z-axis default to (1, 0, 0) as midpoint
217            Vec3::unit_x()
218        } else {
219            // Default to picking midpoint in the xy plane
220            Vec3::new(from.y, -from.x, 0.0).normalized()
221        };
222
223        if factor > 0.5 {
224            (mid_dir, to, factor * 2.0 - 1.0)
225        } else {
226            (from, mid_dir, factor * 2.0)
227        }
228    } else {
229        (from, to, factor)
230    };
231
232    let slerped = Vec3::slerp(from, to, factor);
233    let slerped_normalized = slerped.normalized();
234    // Ensure normalization worked
235    // This should not be possible but I will leave it here for now just in case
236    // something was missed
237    #[cfg(debug_assertions)]
238    {
239        if !slerped_normalized.is_normalized() || slerped_normalized.map(f32::is_nan).reduce_or() {
240            panic!(
241                "Failed to normalize {:?} produced from:\nslerp(\n    {:?},\n    {:?},\n    \
242                 {:?},\n)\nWith result: {:?})",
243                slerped, from, to, factor, slerped_normalized
244            );
245        }
246    }
247
248    slerped_normalized
249}
250
251/// Begone ye NaN's
252/// Slerp two `Vec3`s skipping the slerp if their directions are very close
253/// This avoids a case where `vek`s slerp produces NaN's
254/// Additionally, it avoids unnecessary calculations if they are near identical
255/// Assumes `from` is normalized and returns a normalized vector, but `to`
256/// doesn't need to be normalized
257/// Returns `Err(from)`` if `to` is unnormalizable
258// TODO: in some cases we might want to base the slerp rate on the magnitude of
259// `to` for example when `to` is velocity and `from` is orientation
260fn slerp_to_unnormalized(
261    from: Vec3<f32>,
262    to: Vec3<f32>,
263    factor: f32,
264) -> Result<Vec3<f32>, Vec3<f32>> {
265    to.try_normalized()
266        .map(|to| slerp_normalized(from, to, factor))
267        .ok_or(from)
268}