📄 rig/src/source.rs
//! The frame-source abstractions shared by live capture, replay, and
//! synthetic generation.
//!
//! Two stages, two traits:
//!
//! * [`DepthSource`] emits raw depth frames (millimetre depth maps plus
//!   RGB, as a Kinect reports them) — capture, recordings, and the
//!   synthetic dataset all speak this;
//! * [`FrameSource`] emits processed frames carrying the assembled
//!   [`SplatBatch`] — what the renderer and sinks consume. `splatgen`
//!   bridges the two.

use anyhow::Result;
use glam::UVec2;

use crate::camera::Camera;
use crate::rig::Rig;
use crate::splat::SplatBatch;

/// One processed rig frame, ready for rendering.
///
/// In the MVP (and the synthetic dataset) a frame is simply the freshly
/// constructed Gaussian list; recorded RGB/depth data rides along from
/// build-order step 2 on.
#[derive(Debug, Clone, PartialEq)]
pub struct Frame {
    /// Frame timestamp in seconds since the source started. Synthetic
    /// sources use deterministic timestamps (frame index / fps).
    pub timestamp_secs: f64,
    pub splats: SplatBatch,
}

/// A source of processed rig frames — the seam where live capture, replay
/// of recorded datasets, and synthetic generation are interchangeable.
///
/// Implementations must be deterministic when replaying: the same frame
/// sequence every run, so tests and recordings behave identically.
pub trait FrameSource {
    fn next_frame(&mut self) -> Result<Frame>;
}

/// RGB images from the two side webcams, at their own resolutions.
///
/// Present when the rig source provides them (live capture, the
/// synthetic dataset, recordings with side files); the ellipsoid-proxy
/// fill needs them for recoloring.
#[derive(Debug, Clone, PartialEq)]
pub struct SideRgb {
    /// Left camera, RGB, 3 bytes per pixel, row-major.
    pub left: Vec<u8>,
    /// Left camera resolution: `(width, height)`.
    pub left_resolution: UVec2,
    /// Right camera, RGB, 3 bytes per pixel, row-major.
    pub right: Vec<u8>,
    /// Right camera resolution: `(width, height)`.
    pub right_resolution: UVec2,
}

/// One raw depth-camera frame, as the sensors report it.
#[derive(Debug, Clone, PartialEq)]
pub struct DepthFrame {
    /// Frame timestamp in seconds since the source started.
    pub timestamp_secs: f64,
    /// Depth in millimetres, row-major, `width * height` at the depth
    /// camera's resolution. Zero marks an invalid pixel (no return),
    /// like the Kinect reports.
    pub depth_mm: Vec<u16>,
    /// RGB, 3 bytes per pixel, row-major, same resolution as the depth
    /// map (registered to depth, for a Kinect).
    pub rgb: Vec<u8>,
    /// RGB from the side webcams, when the source has them.
    pub side_rgb: Option<SideRgb>,
}

/// A source of raw depth frames: the seam where live capture, replay of
/// recordings, and the synthetic dataset are interchangeable.
///
/// Implementations must be deterministic when replaying.
pub trait DepthSource {
    /// The depth camera the frames come from (intrinsics, pose,
    /// resolution).
    fn camera(&self) -> &Camera;
    /// The full rig the source represents: the depth camera plus the
    /// side cameras. The pipeline needs the side cameras (intrinsics,
    /// poses, resolutions) for the occlusion-proxy recolouring, and
    /// the recorded rig is the authority for recordings.
    fn rig(&self) -> Rig;
    fn next_depth_frame(&mut self) -> Result<DepthFrame>;
}