use glam::{DQuat, DVec2, DVec3, Vec4};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Rotator {
pub pitch: f64,
pub yaw: f64,
pub roll: f64,
}
impl Rotator {
pub const ZERO: Rotator = Rotator { pitch: 0.0, yaw: 0.0, roll: 0.0 };
pub fn new(pitch: f64, yaw: f64, roll: f64) -> Self {
Rotator { pitch, yaw, roll }
}
}
impl From<Rotator> for DQuat {
fn from(r: Rotator) -> DQuat {
let deg2rad = std::f64::consts::PI / 180.0;
let (sp, cp) = (r.pitch * 0.5 * deg2rad).sin_cos();
let (sy, cy) = (r.yaw * 0.5 * deg2rad).sin_cos();
let (sr, cr) = (r.roll * 0.5 * deg2rad).sin_cos();
DQuat::from_xyzw(
cy * cp * sr - sy * sp * cr,
cy * sp * cr + sy * cp * sr,
sy * cp * cr - cy * sp * sr,
cy * cp * cr + sy * sp * sr,
)
}
}
impl From<DQuat> for Rotator {
fn from(q: DQuat) -> Rotator {
let rad2deg = 180.0 / std::f64::consts::PI;
let sinr_cosp = 2.0 * (q.w * q.x + q.y * q.z);
let cosr_cosp = 1.0 - 2.0 * (q.x * q.x + q.y * q.y);
let roll = sinr_cosp.atan2(cosr_cosp);
let sinp = 2.0 * (q.w * q.y - q.z * q.x);
let pitch = if sinp.abs() >= 1.0 {
std::f64::consts::FRAC_PI_2.copysign(sinp)
} else {
sinp.asin()
};
let siny_cosp = 2.0 * (q.w * q.z + q.x * q.y);
let cosy_cosp = 1.0 - 2.0 * (q.y * q.y + q.z * q.z);
let yaw = siny_cosp.atan2(cosy_cosp);
Rotator {
pitch: pitch * rad2deg,
yaw: yaw * rad2deg,
roll: roll * rad2deg,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform {
pub rotation: DQuat,
pub translation: DVec3,
pub scale: DVec3,
}
impl Transform {
pub const IDENTITY: Transform = Transform {
rotation: DQuat::IDENTITY,
translation: DVec3::ZERO,
scale: DVec3::ONE,
};
pub fn new(rotation: DQuat, translation: DVec3, scale: DVec3) -> Self {
Transform { rotation, translation, scale }
}
pub fn from_translation(translation: DVec3) -> Self {
Transform { translation, ..Self::IDENTITY }
}
pub fn from_rotation(rotation: DQuat) -> Self {
Transform { rotation, ..Self::IDENTITY }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct LinearColor {
pub r: f32,
pub g: f32,
pub b: f32,
pub a: f32,
}
impl LinearColor {
pub const BLACK: LinearColor = LinearColor { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
pub const WHITE: LinearColor = LinearColor { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
pub const RED: LinearColor = LinearColor { r: 1.0, g: 0.0, b: 0.0, a: 1.0 };
pub const GREEN: LinearColor = LinearColor { r: 0.0, g: 1.0, b: 0.0, a: 1.0 };
pub const BLUE: LinearColor = LinearColor { r: 0.0, g: 0.0, b: 1.0, a: 1.0 };
pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
LinearColor { r, g, b, a }
}
}
impl From<LinearColor> for Vec4 {
fn from(c: LinearColor) -> Vec4 {
Vec4::new(c.r, c.g, c.b, c.a)
}
}
impl From<Vec4> for LinearColor {
fn from(v: Vec4) -> LinearColor {
LinearColor { r: v.x, g: v.y, b: v.z, a: v.w }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub const BLACK: Color = Color { r: 0, g: 0, b: 0, a: 255 };
pub const WHITE: Color = Color { r: 255, g: 255, b: 255, a: 255 };
pub const RED: Color = Color { r: 255, g: 0, b: 0, a: 255 };
pub const GREEN: Color = Color { r: 0, g: 255, b: 0, a: 255 };
pub const BLUE: Color = Color { r: 0, g: 0, b: 255, a: 255 };
pub fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Color { r, g, b, a }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Plane {
pub normal: DVec3,
pub d: f64,
}
impl Plane {
pub fn new(normal: DVec3, d: f64) -> Self {
Plane { normal, d }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Ray {
pub origin: DVec3,
pub direction: DVec3,
}
impl Ray {
pub fn new(origin: DVec3, direction: DVec3) -> Self {
Ray { origin, direction }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Sphere {
pub center: DVec3,
pub radius: f64,
}
impl Sphere {
pub fn new(center: DVec3, radius: f64) -> Self {
Sphere { center, radius }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UeBox {
pub min: DVec3,
pub max: DVec3,
}
impl UeBox {
pub fn new(min: DVec3, max: DVec3) -> Self {
UeBox { min, max }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UeBox2d {
pub min: DVec2,
pub max: DVec2,
}
impl UeBox2d {
pub fn new(min: DVec2, max: DVec2) -> Self {
UeBox2d { min, max }
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct BoxSphereBounds {
pub origin: DVec3,
pub box_extent: DVec3,
pub sphere_radius: f64,
}
impl BoxSphereBounds {
pub fn new(origin: DVec3, box_extent: DVec3, sphere_radius: f64) -> Self {
BoxSphereBounds { origin, box_extent, sphere_radius }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rotator_zero_to_quat_is_identity() {
let q: DQuat = Rotator::ZERO.into();
let diff = (q - DQuat::IDENTITY).length();
assert!(diff < 1e-10, "Expected identity quat, got {q:?}");
}
#[test]
fn rotator_roundtrip() {
let r = Rotator::new(30.0, 45.0, 60.0);
let q: DQuat = r.into();
let r2: Rotator = q.into();
assert!((r.pitch - r2.pitch).abs() < 1e-10);
assert!((r.yaw - r2.yaw).abs() < 1e-10);
assert!((r.roll - r2.roll).abs() < 1e-10);
}
#[test]
fn linear_color_vec4_roundtrip() {
let c = LinearColor::new(0.5, 0.3, 0.8, 1.0);
let v: Vec4 = c.into();
let c2: LinearColor = v.into();
assert_eq!(c, c2);
}
}