Skip to main content

sim_lib_view_spatial/
pose_view.rs

1//! Device-local view state consumed by the stereo reprojector.
2
3/// A pose-derived view sample for one device-rate reprojector step.
4///
5/// This type is intentionally not part of the content encoder. It is local
6/// adapter state: the encoded `scene/spatial` packet can be reused while fresh
7/// samples update the stereo eye views.
8#[derive(Clone, Debug, PartialEq)]
9pub struct PoseView {
10    /// Monotone sample sequence number.
11    pub sample_seq: u64,
12    /// Age of the sample at the current adapter tick.
13    pub age_ms: u64,
14    /// Requested prediction lead in nanoseconds.
15    pub predict_ns: u64,
16    /// Head translation in meters relative to the encoded content origin.
17    pub translation_m: [f64; 3],
18    /// Yaw angle in degrees.
19    pub yaw_deg: f64,
20    /// Pitch angle in degrees.
21    pub pitch_deg: f64,
22    /// Roll angle in degrees.
23    pub roll_deg: f64,
24    /// Distance between eyes in meters.
25    pub inter_eye_m: f64,
26}
27
28impl Default for PoseView {
29    fn default() -> Self {
30        Self::identity(0)
31    }
32}
33
34impl PoseView {
35    /// Builds an identity view sample for `sample_seq`.
36    pub fn identity(sample_seq: u64) -> Self {
37        Self {
38            sample_seq,
39            age_ms: 0,
40            predict_ns: 0,
41            translation_m: [0.0, 0.0, 0.0],
42            yaw_deg: 0.0,
43            pitch_deg: 0.0,
44            roll_deg: 0.0,
45            inter_eye_m: 0.064,
46        }
47    }
48
49    /// Returns this sample with a different age and prediction lead.
50    pub fn with_timing(mut self, age_ms: u64, predict_ns: u64) -> Self {
51        self.age_ms = age_ms;
52        self.predict_ns = predict_ns;
53        self
54    }
55
56    /// Returns this sample translated in meters.
57    pub fn with_translation(mut self, translation_m: [f64; 3]) -> Self {
58        self.translation_m = translation_m;
59        self
60    }
61
62    /// Returns this sample with yaw, pitch, and roll in degrees.
63    pub fn with_angles(mut self, yaw_deg: f64, pitch_deg: f64, roll_deg: f64) -> Self {
64        self.yaw_deg = yaw_deg;
65        self.pitch_deg = pitch_deg;
66        self.roll_deg = roll_deg;
67        self
68    }
69
70    /// Clamps the prediction lead to `max_predict_ms`.
71    pub fn clamped_predict_ms(&self, max_predict_ms: u64) -> u64 {
72        (self.predict_ns / 1_000_000).min(max_predict_ms)
73    }
74
75    pub(crate) fn clamped_yaw_rad(&self, max_predict_ms: u64) -> f64 {
76        let requested_ms = self.predict_ns as f64 / 1_000_000.0;
77        let scale = if requested_ms <= f64::EPSILON {
78            0.0
79        } else {
80            self.clamped_predict_ms(max_predict_ms) as f64 / requested_ms
81        };
82        self.yaw_deg.to_radians() * scale
83    }
84}