mirage-engine 0.1.1

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
use core::fmt::Debug;
use core::time::Duration;

use crate::animation::{Motion, Progress};
use crate::mesh::Clip;

/// The states a game poses one mesh through, and what each of them plays.
///
/// Required if you want a machine to pose a draw: write the states as a
/// fieldless enum, state what each of them plays and where it goes from
/// there, and keep an [`Animator`](crate::Animator) of them beside every
/// mesh the game poses.
pub trait AnimationStates: Copy + Eq + Debug + Sized {
    /// The clip vocabulary of the mesh these states pose.
    type Clip: Clip;

    /// What the states read to decide, the game's own to write; a machine
    /// reads the default until `animate` first runs it.
    type Input: Default;

    /// The state a machine starts in.
    fn entry() -> Self;

    /// What this state plays, read every tick.
    fn motion(&self, input: &Self::Input) -> Motion<Self::Clip>;

    /// Where the machine goes from this state, read every tick; `at` is how
    /// far along what it plays is, and nothing at all keeps it where it is.
    ///
    /// While a fade runs this is the state being faded into, so this state
    /// and nothing both leave that fade running, and any other interrupts
    /// it.
    fn next(&self, input: &Self::Input, at: Progress) -> Option<Transition<Self>>;

    /// Into this state over `over`; see [`Transition::fade`].
    fn fade(self, over: Duration) -> Transition<Self> {
        Transition::from(self).fade(over)
    }

    /// Into this state with no fade; see [`Transition::at_once`].
    fn at_once(self) -> Transition<Self> {
        Transition::from(self).at_once()
    }

    /// Into this state, entering `fraction` of the way in; see
    /// [`Transition::entering_at`].
    fn entering_at(self, fraction: f32) -> Transition<Self> {
        Transition::from(self).entering_at(fraction)
    }

    /// Into this state from the start of what it plays again; see
    /// [`Transition::restarted`].
    fn restarted(self) -> Transition<Self> {
        Transition::from(self).restarted()
    }
}

/// Where a machine goes next, and how it gets there.
///
/// A state on its own reads as one that takes the default fade, so a
/// transition that states no knob is `Some(State::Idle.into())`.
#[must_use = "a transition only moves a machine once next returns it"]
#[derive(Clone, Debug)]
pub struct Transition<S> {
    pub(crate) into: S,
    pub(crate) fade: Duration,
    pub(crate) entering_at: f32,
    pub(crate) restarted: bool,
}

impl<S> Transition<S> {
    /// The fade a transition takes to blend out of the state it leaves when
    /// no call sets one: `150` milliseconds.
    pub const DEFAULT_FADE: Duration = Duration::from_millis(150);

    /// Takes `over` to blend out of the state it leaves;
    /// [`Transition::DEFAULT_FADE`] until set.
    ///
    /// The state it leaves plays on under the fade, and a fade longer than
    /// what the state it enters plays holds that motion's last key.
    pub fn fade(mut self, over: Duration) -> Self {
        self.fade = over;
        self
    }

    /// Enters with no fade, which drops the state it leaves at the instant
    /// it enters.
    pub fn at_once(mut self) -> Self {
        self.fade = Duration::ZERO;
        self
    }

    /// Enters `fraction` of the way into what the state it enters plays, a
    /// fraction held inside `0.0..=1.0`; the start of it until set.
    ///
    /// Required if you want to skip the first part of a clip a state enters
    /// part way through.
    pub fn entering_at(mut self, fraction: f32) -> Self {
        self.entering_at = match fraction.is_nan() {
            true => 0.0,
            false => fraction.clamp(0.0, 1.0),
        };
        self
    }

    /// Plays what the state it enters plays from the start again, which is
    /// what moves a machine to the state it is already in.
    pub fn restarted(mut self) -> Self {
        self.restarted = true;
        self
    }
}

impl<S> From<S> for Transition<S> {
    /// Into `into` over [`Transition::DEFAULT_FADE`], from the start of
    /// what it plays.
    fn from(into: S) -> Self {
        Self {
            into,
            fade: Self::DEFAULT_FADE,
            entering_at: 0.0,
            restarted: false,
        }
    }
}