1use glam::{DQuat, DVec2, DVec3, Vec4};
5
6#[derive(Clone, Copy, Debug, PartialEq)]
11pub struct Rotator {
12 pub pitch: f64,
13 pub yaw: f64,
14 pub roll: f64,
15}
16
17impl Rotator {
18 pub const ZERO: Rotator = Rotator { pitch: 0.0, yaw: 0.0, roll: 0.0 };
19
20 pub fn new(pitch: f64, yaw: f64, roll: f64) -> Self {
21 Rotator { pitch, yaw, roll }
22 }
23}
24
25impl From<Rotator> for DQuat {
28 fn from(r: Rotator) -> DQuat {
29 let deg2rad = std::f64::consts::PI / 180.0;
30 let (sp, cp) = (r.pitch * 0.5 * deg2rad).sin_cos();
31 let (sy, cy) = (r.yaw * 0.5 * deg2rad).sin_cos();
32 let (sr, cr) = (r.roll * 0.5 * deg2rad).sin_cos();
33
34 DQuat::from_xyzw(
36 cy * cp * sr - sy * sp * cr,
37 cy * sp * cr + sy * cp * sr,
38 sy * cp * cr - cy * sp * sr,
39 cy * cp * cr + sy * sp * sr,
40 )
41 }
42}
43
44impl From<DQuat> for Rotator {
45 fn from(q: DQuat) -> Rotator {
46 let rad2deg = 180.0 / std::f64::consts::PI;
47
48 let sinr_cosp = 2.0 * (q.w * q.x + q.y * q.z);
50 let cosr_cosp = 1.0 - 2.0 * (q.x * q.x + q.y * q.y);
51 let roll = sinr_cosp.atan2(cosr_cosp);
52
53 let sinp = 2.0 * (q.w * q.y - q.z * q.x);
54 let pitch = if sinp.abs() >= 1.0 {
55 std::f64::consts::FRAC_PI_2.copysign(sinp)
56 } else {
57 sinp.asin()
58 };
59
60 let siny_cosp = 2.0 * (q.w * q.z + q.x * q.y);
61 let cosy_cosp = 1.0 - 2.0 * (q.y * q.y + q.z * q.z);
62 let yaw = siny_cosp.atan2(cosy_cosp);
63
64 Rotator {
65 pitch: pitch * rad2deg,
66 yaw: yaw * rad2deg,
67 roll: roll * rad2deg,
68 }
69 }
70}
71
72#[derive(Clone, Copy, Debug, PartialEq)]
77pub struct Transform {
78 pub rotation: DQuat,
79 pub translation: DVec3,
80 pub scale: DVec3,
81}
82
83impl Transform {
84 pub const IDENTITY: Transform = Transform {
85 rotation: DQuat::IDENTITY,
86 translation: DVec3::ZERO,
87 scale: DVec3::ONE,
88 };
89
90 pub fn new(rotation: DQuat, translation: DVec3, scale: DVec3) -> Self {
91 Transform { rotation, translation, scale }
92 }
93
94 pub fn from_translation(translation: DVec3) -> Self {
95 Transform { translation, ..Self::IDENTITY }
96 }
97
98 pub fn from_rotation(rotation: DQuat) -> Self {
99 Transform { rotation, ..Self::IDENTITY }
100 }
101}
102
103#[derive(Clone, Copy, Debug, PartialEq)]
109pub struct LinearColor {
110 pub r: f32,
111 pub g: f32,
112 pub b: f32,
113 pub a: f32,
114}
115
116impl LinearColor {
117 pub const BLACK: LinearColor = LinearColor { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
118 pub const WHITE: LinearColor = LinearColor { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
119 pub const RED: LinearColor = LinearColor { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
120 pub const GREEN: LinearColor = LinearColor { r: 0.0, g: 1.0, b: 0.0, a: 1.0 };
121 pub const BLUE: LinearColor = LinearColor { r: 0.0, g: 0.0, b: 1.0, a: 1.0 };
122
123 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
124 LinearColor { r, g, b, a }
125 }
126}
127
128impl From<LinearColor> for Vec4 {
129 fn from(c: LinearColor) -> Vec4 {
130 Vec4::new(c.r, c.g, c.b, c.a)
131 }
132}
133
134impl From<Vec4> for LinearColor {
135 fn from(v: Vec4) -> LinearColor {
136 LinearColor { r: v.x, g: v.y, b: v.z, a: v.w }
137 }
138}
139
140#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
143pub struct Color {
144 pub r: u8,
145 pub g: u8,
146 pub b: u8,
147 pub a: u8,
148}
149
150impl Color {
151 pub const BLACK: Color = Color { r: 0, g: 0, b: 0, a: 255 };
152 pub const WHITE: Color = Color { r: 255, g: 255, b: 255, a: 255 };
153 pub const RED: Color = Color { r: 255, g: 0, b: 0, a: 255 };
154 pub const GREEN: Color = Color { r: 0, g: 255, b: 0, a: 255 };
155 pub const BLUE: Color = Color { r: 0, g: 0, b: 255, a: 255 };
156
157 pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
158 Color { r, g, b, a }
159 }
160}
161
162#[derive(Clone, Copy, Debug, PartialEq)]
168pub struct Plane {
169 pub normal: DVec3,
170 pub d: f64,
171}
172
173impl Plane {
174 pub fn new(normal: DVec3, d: f64) -> Self {
175 Plane { normal, d }
176 }
177}
178
179#[derive(Clone, Copy, Debug, PartialEq)]
181pub struct Ray {
182 pub origin: DVec3,
183 pub direction: DVec3,
184}
185
186impl Ray {
187 pub fn new(origin: DVec3, direction: DVec3) -> Self {
188 Ray { origin, direction }
189 }
190}
191
192#[derive(Clone, Copy, Debug, PartialEq)]
194pub struct Sphere {
195 pub center: DVec3,
196 pub radius: f64,
197}
198
199impl Sphere {
200 pub fn new(center: DVec3, radius: f64) -> Self {
201 Sphere { center, radius }
202 }
203}
204
205#[derive(Clone, Copy, Debug, PartialEq)]
212pub struct UeBox {
213 pub min: DVec3,
214 pub max: DVec3,
215}
216
217impl UeBox {
218 pub fn new(min: DVec3, max: DVec3) -> Self {
219 UeBox { min, max }
220 }
221}
222
223#[derive(Clone, Copy, Debug, PartialEq)]
225pub struct UeBox2d {
226 pub min: DVec2,
227 pub max: DVec2,
228}
229
230impl UeBox2d {
231 pub fn new(min: DVec2, max: DVec2) -> Self {
232 UeBox2d { min, max }
233 }
234}
235
236#[derive(Clone, Copy, Debug, PartialEq)]
238pub struct BoxSphereBounds {
239 pub origin: DVec3,
240 pub box_extent: DVec3,
241 pub sphere_radius: f64,
242}
243
244impl BoxSphereBounds {
245 pub fn new(origin: DVec3, box_extent: DVec3, sphere_radius: f64) -> Self {
246 BoxSphereBounds { origin, box_extent, sphere_radius }
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn rotator_zero_to_quat_is_identity() {
256 let q: DQuat = Rotator::ZERO.into();
257 let diff = (q - DQuat::IDENTITY).length();
258 assert!(diff < 1e-10, "Expected identity quat, got {q:?}");
259 }
260
261 #[test]
262 fn rotator_roundtrip() {
263 let r = Rotator::new(30.0, 45.0, 60.0);
264 let q: DQuat = r.into();
265 let r2: Rotator = q.into();
266 assert!((r.pitch - r2.pitch).abs() < 1e-10);
267 assert!((r.yaw - r2.yaw).abs() < 1e-10);
268 assert!((r.roll - r2.roll).abs() < 1e-10);
269 }
270
271 #[test]
272 fn linear_color_vec4_roundtrip() {
273 let c = LinearColor::new(0.5, 0.3, 0.8, 1.0);
274 let v: Vec4 = c.into();
275 let c2: LinearColor = v.into();
276 assert_eq!(c, c2);
277 }
278}