mirage-engine 0.2.0

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
//! What `#[derive(Clip)]` answers to, and how a mesh names the clips it is
//! posed by, seen the way a game sees it.

use mirage_engine::prelude::*;

/// A vocabulary whose clips are named the way a modelling tool names them.
#[derive(Clip, Clone, Debug, Eq, Hash, PartialEq)]
enum Pace {
    Idle,
    #[clip("walk_01")]
    #[clip("walk-fast")]
    Walk,
}

/// A vocabulary of one clip, named by its own identifier.
#[derive(Clip, Clone, Debug, Eq, Hash, PartialEq)]
struct Open;

/// A mesh a source loads, posed by the one clip that source states.
#[derive(Catalog, Clone, Eq, Hash, PartialEq)]
struct Chest;

impl Mesh<NoParts, Open> for Chest {
    fn build(&self, assets: &Assets) -> MeshData<NoParts, Open> {
        assets.model("Chest")
    }
}

meshes! { enum Prop { Chest, Cube } }

#[test]
fn a_variant_answers_to_its_identifier_until_it_declares_a_name() {
    assert_eq!(Pace::from_name("Idle"), Some(Pace::Idle));
    assert_eq!(Pace::from_name("walk_01"), Some(Pace::Walk));
    assert_eq!(Pace::from_name("walk-fast"), Some(Pace::Walk), "and stack");
    assert_eq!(Pace::from_name("Walk"), None, "a declared name replaces it");
    assert_eq!(Pace::from_name("attack"), None);
    assert_eq!(Open::from_name("Open"), Some(Open));
    assert_eq!(Open::from_name("open"), None, "names are matched exactly");
}

#[test]
fn every_variant_is_counted_once_in_order_however_many_names_it_answers_to() {
    assert_eq!(Pace::all(), vec![Pace::Idle, Pace::Walk]);
    assert_eq!(
        Pace::all().iter().map(Pace::index).collect::<Vec<u32>>(),
        vec![0, 1]
    );
    assert_eq!(Open::all(), vec![Open]);
    assert_eq!(Open.index(), 0);
}

#[test]
fn a_mesh_no_clip_poses_resolves_no_name() {
    assert_eq!(NoClips::from_name("idle"), None);
    assert!(NoClips::all().is_empty(), "so startup asks nothing of it");
}

#[test]
fn a_set_holds_a_mesh_whatever_clips_pose_it() {
    let assets = Assets::default();

    assert_eq!(Prop::catalog().len(), 2);
    assert!(
        Chest.build(&assets).slots().is_empty(),
        "a model that did not load draws nothing rather than failing here"
    );
    // The set's own build is what startup calls, with the clip type dropped;
    // a game reads nothing of what it returns.
    Prop::from(Chest).build(&assets);
}