mirage-engine 0.1.0

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
//! What a session draws with no room to keep the mesh data it built.

use super::*;

meshes! { enum ChurnedSet { Churned } }

/// The color [`column`] reads, without the alpha.
fn along(pixels: &[u8], across: u32) -> [u8; 3] {
    let [red, green, blue, _] = column(pixels, across);
    [red, green, blue]
}

/// A square that fills the view, keyed by the step that drew it, and a
/// square a quarter the size every step draws again. Each is drawn in a color
/// of its own, so a step reads the two of them back only where the cache
/// had both built.
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
enum Churned {
    Novel(u32),
    Kept,
}

impl Catalog for Churned {
    fn catalog() -> Vec<Self> {
        vec![Self::Kept, Self::Novel(0)]
    }
}

impl Mesh for Churned {
    fn build(&self, assets: &Assets) -> MeshData {
        let square = Quad.build(assets);
        match self {
            Self::Novel(_) => square.with_material(Material::color(Color::WHITE)),
            Self::Kept => square.with_material(Material::color(Color::rgb(1.0, 0.0, 0.0))),
        }
    }
}

/// Draws the square it keeps over one no step before it drew.
struct Streaming(u32);

impl Game for Streaming {
    type Meshes = ChurnedSet;
    type Sounds = NoSounds;
    type InputActions = NoInputActions;
    type Skyboxes = NoSkyboxes;
    type SurfaceStyles = ();
    type PostEffects = ();

    fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {}

    fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
        ctx.set_camera(Camera::new(
            View::look_at(Vec3::Z, Vec3::ZERO),
            Projection::orthographic(1.0),
        ));
        ctx.draw(Churned::Kept.at(Transform::from_scale(Vec3::splat(0.25))));
        ctx.draw(Churned::Novel(self.0).at(Vec3::NEG_Z));
        self.0 += 1;
    }
}

#[test]
fn a_session_that_keeps_no_mesh_data_draws_every_step_the_same() {
    let config = raw("headless cache").with_mesh_memory(0);
    let Ok(mut session) = Session::new(config, UVec2::splat(SIDE), |_ctx| Ok(Streaming(0))) else {
        eprintln!("skipped: this machine has no usable graphics adapter");
        return;
    };

    session.step();
    let first = session.pixels().expect("the target reads back");
    for _ in 0..8 {
        session.step();
    }
    let last = session.pixels().expect("the target reads back");

    assert_eq!(
        middle(&first),
        [u8::MAX, 0, 0, u8::MAX],
        "the square it keeps is drawn over the one this step built"
    );
    assert_eq!(
        along(&first, SIDE / 8),
        [u8::MAX; 3],
        "which covers the middle and no more"
    );
    assert_eq!(last, first, "and every step after it reads back the same");
}