bevy_mikktspace/
math.rs

1//! # Copyright
2//!
3//! This code is a Rust reimplementation of <https://github.com/mmikk/MikkTSpace>.
4//! The copyright notice below reflects that history, and should not be removed.
5//!
6//! > Copyright (C) 2011 by Morten S. Mikkelsen
7//! >
8//! > This software is provided 'as-is', without any express or implied
9//! > warranty.  In no event will the authors be held liable for any damages
10//! > arising from the use of this software.
11//! >
12//! > Permission is granted to anyone to use this software for any purpose,
13//! > including commercial applications, and to alter it and redistribute it
14//! > freely, subject to the following restrictions:
15//! >
16//! > 1. The origin of this software must not be misrepresented; you must not
17//! >    claim that you wrote the original software. If you use this software
18//! >    in a product, an acknowledgment in the product documentation would be
19//! >    appreciated but is not required.
20//! > 2. Altered source versions must be plainly marked as such, and must not be
21//! >    misrepresented as being the original software.
22//! > 3. This notice may not be removed or altered from any source distribution.
23
24use core::{
25    marker::PhantomData,
26    ops::{Add, Index, IndexMut, Mul, Neg, Sub},
27};
28
29/// Provides the math operations required by the tangent space algorithm but which
30/// aren't included in Rust's [`core`] crate.
31/// With the `std` feature enabled, a (default) implementation is provided.
32pub trait Ops {
33    /// Provides a [`sqrt`] implementation for [`f32`].
34    ///
35    /// [`sqrt`]: https://doc.rust-lang.org/stable/std/primitive.f32.html#method.sqrt
36    // TODO: Provide default implementation if/when `core_float_math` is stable.
37    //       See https://github.com/rust-lang/rust/issues/137578
38    fn sqrt(x: f32) -> f32;
39
40    /// Provides a [`acos`] implementation for [`f32`].
41    ///
42    /// [`acos`]: https://doc.rust-lang.org/stable/std/primitive.f32.html#method.acos
43    fn acos(x: f32) -> f32;
44}
45
46pub(crate) struct Vec3<O: Ops> {
47    pub(crate) x: f32,
48    pub(crate) y: f32,
49    pub(crate) z: f32,
50    pub(crate) _phantom: PhantomData<O>,
51}
52
53impl<O: Ops> Copy for Vec3<O> {}
54
55impl<O: Ops> Clone for Vec3<O> {
56    fn clone(&self) -> Self {
57        *self
58    }
59}
60
61impl<O: Ops> From<[f32; 3]> for Vec3<O> {
62    fn from([x, y, z]: [f32; 3]) -> Self {
63        Self {
64            x,
65            y,
66            z,
67            ..Self::ZERO
68        }
69    }
70}
71
72impl<O: Ops> From<Vec3<O>> for [f32; 3] {
73    fn from(Vec3 { x, y, z, .. }: Vec3<O>) -> Self {
74        [x, y, z]
75    }
76}
77
78impl<O: Ops> Vec3<O> {
79    pub(crate) const ZERO: Vec3<O> = Vec3 {
80        x: 0.,
81        y: 0.,
82        z: 0.,
83        _phantom: PhantomData,
84    };
85
86    pub(crate) fn dot(self, rhs: Self) -> f32 {
87        self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
88    }
89
90    pub(crate) fn normalize_or_zero(&mut self) {
91        // might change this to an epsilon based test
92        if not_zero(self.x) || not_zero(self.y) || not_zero(self.z) {
93            *self = *self * self.length().recip();
94        }
95    }
96
97    pub(crate) fn normalized_or_zero(mut self) -> Self {
98        self.normalize_or_zero();
99        self
100    }
101
102    pub(crate) fn length_squared(self) -> f32 {
103        self.dot(self)
104    }
105
106    pub(crate) fn length(self) -> f32 {
107        O::sqrt(self.length_squared())
108    }
109}
110
111impl<O: Ops> Index<usize> for Vec3<O> {
112    type Output = f32;
113
114    fn index(&self, index: usize) -> &Self::Output {
115        match index {
116            0 => &self.x,
117            1 => &self.y,
118            2 => &self.z,
119            _ => panic!(),
120        }
121    }
122}
123
124impl<O: Ops> IndexMut<usize> for Vec3<O> {
125    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
126        match index {
127            0 => &mut self.x,
128            1 => &mut self.y,
129            2 => &mut self.z,
130            _ => panic!(),
131        }
132    }
133}
134
135impl<O: Ops> Add for Vec3<O> {
136    type Output = Vec3<O>;
137
138    fn add(self, rhs: Self) -> Self::Output {
139        Vec3 {
140            x: self.x + rhs.x,
141            y: self.y + rhs.y,
142            z: self.z + rhs.z,
143            _phantom: PhantomData,
144        }
145    }
146}
147
148impl<O: Ops> Sub for Vec3<O> {
149    type Output = Vec3<O>;
150
151    fn sub(self, rhs: Self) -> Self::Output {
152        Vec3 {
153            x: self.x - rhs.x,
154            y: self.y - rhs.y,
155            z: self.z - rhs.z,
156            _phantom: PhantomData,
157        }
158    }
159}
160
161impl<O: Ops> Mul<f32> for Vec3<O> {
162    type Output = Vec3<O>;
163
164    fn mul(self, rhs: f32) -> Self::Output {
165        Vec3 {
166            x: rhs * self.x,
167            y: rhs * self.y,
168            z: rhs * self.z,
169            _phantom: PhantomData,
170        }
171    }
172}
173
174impl<O: Ops> Mul<Vec3<O>> for f32 {
175    type Output = Vec3<O>;
176
177    fn mul(self, rhs: Vec3<O>) -> Self::Output {
178        rhs * self
179    }
180}
181
182impl<O: Ops> PartialEq for Vec3<O> {
183    fn eq(&self, other: &Self) -> bool {
184        self.x == other.x && self.y == other.y && self.z == other.z
185    }
186}
187
188impl<O: Ops> Neg for Vec3<O> {
189    type Output = Vec3<O>;
190
191    fn neg(self) -> Self::Output {
192        Self {
193            x: -self.x,
194            y: -self.y,
195            z: -self.z,
196            _phantom: PhantomData,
197        }
198    }
199}
200
201pub(crate) fn fabsf(x: f32) -> f32 {
202    if x.is_sign_negative() { -x } else { x }
203}
204
205pub(crate) fn not_zero(x: f32) -> bool {
206    // could possibly use FLT_EPSILON instead
207    fabsf(x) > f32::MIN_POSITIVE
208}