rusteal-core 0.7.0

Safe Rust runtime for rusteal (object refs, lifecycle, dynamic calls)
Documentation
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);
    }
}