mirage-engine 0.1.1

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
use crate::{Material, ReliefData, ShadingData, TextureData};

/// What one part of a mesh draws with: a material and its maps, over some
/// of the mesh's triangles.
///
/// A slot is a length, not a range.
#[derive(Clone, Debug)]
pub struct Slot {
    index_count: u32,
    material: Material,
    texture: Option<TextureData>,
    relief: Option<ReliefData>,
    shading: Option<ShadingData>,
    emissive: Option<TextureData>,
    /// The index of the part naming this slot, absent while it is
    /// anonymous.
    part: Option<u32>,
}

impl Slot {
    /// A slot drawn with `material` over a white default, for the next
    /// `index_count` indices.
    pub fn new(index_count: u32, material: Material) -> Self {
        Self {
            index_count,
            material,
            texture: None,
            relief: None,
            shading: None,
            emissive: None,
            part: None,
        }
    }

    /// Samples this slot from `texture`, in place of the white default.
    #[must_use]
    pub fn textured(mut self, texture: TextureData) -> Self {
        self.texture = Some(texture);
        self
    }

    /// The relief is a texture map: `RGB` holds each texel's normal, `+Y` up
    /// the map, and `A` its depth where the relief declares one, `1.0` one
    /// sprite width, half in front of the sprite and half behind.
    ///
    /// Required if you want a surface lit by normals its own corners do not
    /// hold. A billboarded or upright draw reads the normal in the axes the
    /// camera turned; every other draw reads it through the derivative
    /// basis — the basis its own position and UV derivatives span — and
    /// ignores the depth. The relief is windowed like the color texture, so
    /// each sheet cell lines up with its own relief cell. A depth also
    /// decides what a billboarded or upright draw casts: a texel with no
    /// depth casts nothing, and depth under the sprite's own texels, down to
    /// its foot, produces a shadow that starts at the ground it is drawn on.
    #[must_use]
    pub fn relief(mut self, relief: ReliefData) -> Self {
        self.relief = Some(relief);
        self
    }

    /// The shading map holds each texel's occlusion in `R`, its roughness in
    /// `G` and its metallic in `B`. The roughness and metallic each scale the
    /// slot's own lane; the occlusion instead darkens what the texel takes
    /// of the frame's sky.
    ///
    /// Required if you want the roughness, the metallic or the sky's own
    /// light on one material to differ across the surface. Every light of
    /// the frame lands whole, whatever the occlusion holds.
    #[must_use]
    pub fn shading(mut self, shading: ShadingData) -> Self {
        self.shading = Some(shading);
        self
    }

    /// The emissive map is the light this slot casts per texel, scaled by the
    /// material's emissive color.
    ///
    /// Required if you want part of a surface to cast light while the rest of
    /// it does not. A material casts none by default, so the map alone casts
    /// nothing.
    #[must_use]
    pub fn emissive_map(mut self, emissive: TextureData) -> Self {
        self.emissive = Some(emissive);
        self
    }

    /// The number of the mesh's indices this slot covers.
    pub fn index_count(&self) -> u32 {
        self.index_count
    }

    /// The material used unless a draw overrides this slot.
    pub fn material(&self) -> Material {
        self.material
    }

    /// The pixels this slot samples, absent while it is drawn over white.
    pub fn texture(&self) -> Option<&TextureData> {
        self.texture.as_ref()
    }

    /// The same slot named by the part at `part`.
    pub(crate) fn named(mut self, part: u32) -> Self {
        self.part = Some(part);
        self
    }

    /// The index of the part naming this slot, absent while it is
    /// anonymous.
    pub(crate) fn part(&self) -> Option<u32> {
        self.part
    }

    /// The relief this slot reads, absent where it has none.
    pub(crate) fn relief_map(&self) -> Option<&ReliefData> {
        self.relief.as_ref()
    }

    /// The shading map this slot reads, absent where it has none.
    pub(crate) fn shading_map(&self) -> Option<&ShadingData> {
        self.shading.as_ref()
    }

    /// The emissive map this slot reads, absent where it has none.
    pub(crate) fn emissive(&self) -> Option<&TextureData> {
        self.emissive.as_ref()
    }

    /// Sets the material, in place of the one it has.
    pub(crate) fn set_material(&mut self, material: Material) {
        self.material = material;
    }

    /// Sets the texture, in place of any it has.
    pub(crate) fn set_texture(&mut self, texture: TextureData) {
        self.texture = Some(texture);
    }

    /// Sets the relief, in place of any it has.
    pub(crate) fn set_relief(&mut self, relief: ReliefData) {
        self.relief = Some(relief);
    }

    /// Sets the shading map, in place of any it has.
    pub(crate) fn set_shading(&mut self, shading: ShadingData) {
        self.shading = Some(shading);
    }

    /// Sets the emissive map, in place of any it has.
    pub(crate) fn set_emissive_map(&mut self, emissive: TextureData) {
        self.emissive = Some(emissive);
    }

    /// Memory the pixels this slot samples hold.
    pub(crate) fn bytes(&self) -> usize {
        [
            self.texture.as_ref(),
            self.relief.as_ref().map(ReliefData::map),
            self.shading.as_ref().map(ShadingData::map),
            self.emissive.as_ref(),
        ]
        .into_iter()
        .flatten()
        .map(|texture| texture.pixels().len())
        .sum()
    }
}