goud_engine/ecs/components/transform/
quat.rs1use crate::core::math::{Quaternion, Vec3};
4use cgmath::InnerSpace;
5use std::f32::consts::PI;
6
7#[repr(C)]
15#[derive(Clone, Copy, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
16pub struct Quat {
17 pub x: f32,
19 pub y: f32,
21 pub z: f32,
23 pub w: f32,
25}
26
27impl Quat {
28 pub const IDENTITY: Quat = Quat {
30 x: 0.0,
31 y: 0.0,
32 z: 0.0,
33 w: 1.0,
34 };
35
36 #[inline]
38 pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
39 Self { x, y, z, w }
40 }
41
42 #[inline]
60 pub fn from_axis_angle(axis: Vec3, angle: f32) -> Self {
61 let half_angle = angle * 0.5;
62 let s = half_angle.sin();
63 let c = half_angle.cos();
64 Self {
65 x: axis.x * s,
66 y: axis.y * s,
67 z: axis.z * s,
68 w: c,
69 }
70 }
71
72 #[inline]
89 pub fn from_euler(pitch: f32, yaw: f32, roll: f32) -> Self {
90 let (sx, cx) = (pitch * 0.5).sin_cos();
92 let (sy, cy) = (yaw * 0.5).sin_cos();
93 let (sz, cz) = (roll * 0.5).sin_cos();
94
95 Self {
96 x: sx * cy * cz + cx * sy * sz,
97 y: cx * sy * cz - sx * cy * sz,
98 z: cx * cy * sz + sx * sy * cz,
99 w: cx * cy * cz - sx * sy * sz,
100 }
101 }
102
103 #[inline]
107 pub fn from_rotation_arc(from: Vec3, to: Vec3) -> Self {
108 let from_cg: cgmath::Vector3<f32> = from.into();
109 let to_cg: cgmath::Vector3<f32> = to.into();
110
111 let dot = from_cg.dot(to_cg);
113 if dot < -0.999999 {
114 let mut axis = cgmath::Vector3::unit_x().cross(from_cg);
116 if axis.magnitude2() < 0.000001 {
117 axis = cgmath::Vector3::unit_y().cross(from_cg);
118 }
119 axis = axis.normalize();
120 return Self::from_axis_angle(Vec3::from(axis), PI);
121 }
122
123 let cross = from_cg.cross(to_cg);
124 Self {
125 x: cross.x,
126 y: cross.y,
127 z: cross.z,
128 w: 1.0 + dot,
129 }
130 .normalize()
131 }
132
133 #[inline]
135 pub fn length_squared(self) -> f32 {
136 self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
137 }
138
139 #[inline]
141 pub fn length(self) -> f32 {
142 self.length_squared().sqrt()
143 }
144
145 #[inline]
147 pub fn normalize(self) -> Self {
148 let len = self.length();
149 if len == 0.0 {
150 Self::IDENTITY
151 } else {
152 Self {
153 x: self.x / len,
154 y: self.y / len,
155 z: self.z / len,
156 w: self.w / len,
157 }
158 }
159 }
160
161 #[inline]
165 pub fn conjugate(self) -> Self {
166 Self {
167 x: -self.x,
168 y: -self.y,
169 z: -self.z,
170 w: self.w,
171 }
172 }
173
174 #[inline]
178 pub fn inverse(self) -> Self {
179 let len_sq = self.length_squared();
180 if len_sq == 0.0 {
181 Self::IDENTITY
182 } else {
183 let conj = self.conjugate();
184 Self {
185 x: conj.x / len_sq,
186 y: conj.y / len_sq,
187 z: conj.z / len_sq,
188 w: conj.w / len_sq,
189 }
190 }
191 }
192
193 #[inline]
198 pub fn multiply(self, other: Self) -> Self {
199 self * other
200 }
201
202 #[inline]
204 pub fn rotate_vector(self, v: Vec3) -> Vec3 {
205 let qv = Vec3::new(self.x, self.y, self.z);
207 let uv = qv.cross(v);
208 let uuv = qv.cross(uv);
209 v + (uv * self.w + uuv) * 2.0
210 }
211
212 #[inline]
216 pub fn slerp(self, other: Self, t: f32) -> Self {
217 let dot = self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w;
218
219 let (other, dot) = if dot < 0.0 {
221 (
222 Self {
223 x: -other.x,
224 y: -other.y,
225 z: -other.z,
226 w: -other.w,
227 },
228 -dot,
229 )
230 } else {
231 (other, dot)
232 };
233
234 if dot > 0.9995 {
236 return Self {
237 x: self.x + t * (other.x - self.x),
238 y: self.y + t * (other.y - self.y),
239 z: self.z + t * (other.z - self.z),
240 w: self.w + t * (other.w - self.w),
241 }
242 .normalize();
243 }
244
245 let theta_0 = dot.acos();
246 let theta = theta_0 * t;
247 let sin_theta = theta.sin();
248 let sin_theta_0 = theta_0.sin();
249
250 let s0 = (theta_0 - theta).cos() - dot * sin_theta / sin_theta_0;
251 let s1 = sin_theta / sin_theta_0;
252
253 Self {
254 x: self.x * s0 + other.x * s1,
255 y: self.y * s0 + other.y * s1,
256 z: self.z * s0 + other.z * s1,
257 w: self.w * s0 + other.w * s1,
258 }
259 }
260
261 #[inline]
265 pub fn to_euler(self) -> (f32, f32, f32) {
266 let sinr_cosp = 2.0 * (self.w * self.x + self.y * self.z);
268 let cosr_cosp = 1.0 - 2.0 * (self.x * self.x + self.y * self.y);
269 let roll = sinr_cosp.atan2(cosr_cosp);
270
271 let sinp = 2.0 * (self.w * self.y - self.z * self.x);
273 let pitch = if sinp.abs() >= 1.0 {
274 (PI / 2.0).copysign(sinp)
275 } else {
276 sinp.asin()
277 };
278
279 let siny_cosp = 2.0 * (self.w * self.z + self.x * self.y);
281 let cosy_cosp = 1.0 - 2.0 * (self.y * self.y + self.z * self.z);
282 let yaw = siny_cosp.atan2(cosy_cosp);
283
284 (pitch, yaw, roll)
285 }
286
287 #[inline]
289 pub fn forward(self) -> Vec3 {
290 self.rotate_vector(Vec3::new(0.0, 0.0, -1.0))
291 }
292
293 #[inline]
295 pub fn right(self) -> Vec3 {
296 self.rotate_vector(Vec3::new(1.0, 0.0, 0.0))
297 }
298
299 #[inline]
301 pub fn up(self) -> Vec3 {
302 self.rotate_vector(Vec3::new(0.0, 1.0, 0.0))
303 }
304}
305
306impl Default for Quat {
307 #[inline]
308 fn default() -> Self {
309 Self::IDENTITY
310 }
311}
312
313impl std::ops::Mul for Quat {
315 type Output = Self;
316
317 #[inline]
321 fn mul(self, other: Self) -> Self {
322 Self {
323 x: self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
324 y: self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x,
325 z: self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w,
326 w: self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
327 }
328 }
329}
330
331impl From<Quaternion<f32>> for Quat {
333 #[inline]
334 fn from(q: Quaternion<f32>) -> Self {
335 Self {
336 x: q.v.x,
337 y: q.v.y,
338 z: q.v.z,
339 w: q.s,
340 }
341 }
342}
343
344impl From<Quat> for Quaternion<f32> {
345 #[inline]
346 fn from(q: Quat) -> Self {
347 Quaternion::new(q.w, q.x, q.y, q.z)
348 }
349}