vbsp_common/
angle.rs

1use crate::EntityParseError;
2use binrw::BinRead;
3use cgmath::{Deg, Quaternion, Rotation3};
4use serde::de::{Error, Unexpected};
5use serde::{Deserialize, Deserializer};
6use std::str::FromStr;
7
8#[derive(Debug, Copy, Clone, BinRead, Default)]
9pub struct Angles {
10    pub pitch: f32,
11    pub yaw: f32,
12    pub roll: f32,
13}
14
15impl<'de> Deserialize<'de> for Angles {
16    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
17    where
18        D: Deserializer<'de>,
19    {
20        let str = <&str>::deserialize(deserializer)?;
21        str.parse()
22            .map_err(|_| D::Error::invalid_value(Unexpected::Other(str), &"a list of angles"))
23    }
24}
25
26impl FromStr for Angles {
27    type Err = EntityParseError;
28
29    fn from_str(s: &str) -> Result<Self, Self::Err> {
30        let mut floats = s.split_whitespace().map(f32::from_str);
31        let pitch = floats.next().ok_or(EntityParseError::ElementCount)??;
32        let yaw = floats.next().ok_or(EntityParseError::ElementCount)??;
33        let roll = floats.next().ok_or(EntityParseError::ElementCount)??;
34        Ok(Angles { pitch, yaw, roll })
35    }
36}
37
38impl Angles {
39    pub fn as_quaternion(&self) -> Quaternion<f32> {
40        // angles are applied in roll, pitch, yaw order
41        Quaternion::from_angle_y(Deg(self.yaw))
42            * Quaternion::from_angle_x(Deg(self.pitch))
43            * Quaternion::from_angle_z(Deg(self.roll))
44    }
45}