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