mirage-engine 0.1.1

Mirage, an immediate-mode 3D engine for simple games on desktop and the browser
Documentation
//! A decoded mesh whose slots still have the material names their source
//! used, until a game's part vocabulary resolves them.

use crate::assets::Unresolved;
use crate::material::Material;
use crate::mesh::{MeshData, NoClips, Part, Slot, Vertex};
use crate::{ReliefData, ShadingData, TextureData};

/// A decoded mesh, its slots named by the materials its source used.
pub(crate) struct NamedMesh {
    vertices: Vec<Vertex>,
    indices: Vec<u32>,
    slots: Vec<NamedSlot>,
}

impl NamedMesh {
    pub(crate) fn new(vertices: Vec<Vertex>, indices: Vec<u32>, slots: Vec<NamedSlot>) -> Self {
        Self {
            vertices,
            indices,
            slots,
        }
    }

    /// The mesh's vertices, in the order the vertex buffer takes them.
    pub(crate) fn vertices(&self) -> &[Vertex] {
        &self.vertices
    }

    /// The triangle indices, three per triangle.
    pub(crate) fn indices(&self) -> &[u32] {
        &self.indices
    }

    /// This mesh as a game's part vocabulary resolves it, which no clip
    /// poses.
    pub(crate) fn resolved<P: Part>(
        &self,
        asset: &str,
    ) -> Result<MeshData<P, NoClips>, Vec<Unresolved>> {
        let slots = self.parts::<P>(asset)?;

        Ok(MeshData::resolved(
            self.vertices.clone(),
            self.indices.clone(),
            slots,
        ))
    }

    /// This mesh's slots, each named by the part of `P` its material name
    /// resolves to, with everything `P` does not name left anonymous.
    ///
    /// Every part names exactly one slot.
    pub(crate) fn parts<P: Part>(&self, asset: &str) -> Result<Vec<Slot>, Vec<Unresolved>> {
        let parts: Vec<Option<P>> = self
            .slots
            .iter()
            .map(|slot| P::from_name(&slot.name))
            .collect();
        let wrong: Vec<Unresolved> = P::all()
            .into_iter()
            .filter_map(|part| {
                let named: Vec<&str> = parts
                    .iter()
                    .zip(&self.slots)
                    .filter(|(resolved, _)| resolved.as_ref() == Some(&part))
                    .map(|(_, slot)| slot.name.as_str())
                    .collect();
                match named[..] {
                    [_] => None,
                    [] => Some(Unresolved::PartUnnamed {
                        asset: asset.to_owned(),
                        part: format!("{part:?}"),
                    }),
                    [first, second, ..] => Some(Unresolved::PartTwice {
                        asset: asset.to_owned(),
                        part: format!("{part:?}"),
                        first: first.to_owned(),
                        second: second.to_owned(),
                    }),
                }
            })
            .collect();
        if !wrong.is_empty() {
            return Err(wrong);
        }

        Ok(parts
            .into_iter()
            .zip(&self.slots)
            .map(|(part, slot)| slot.keyed(part.map(|part| part.index())))
            .collect())
    }
}

/// One of that mesh's slots, named by the material name its source used.
pub(crate) struct NamedSlot {
    pub(crate) name: String,
    pub(crate) index_count: u32,
    pub(crate) material: Material,
    pub(crate) texture: Option<TextureData>,
    pub(crate) relief: Option<ReliefData>,
    pub(crate) shading: Option<ShadingData>,
    pub(crate) emissive: Option<TextureData>,
}

impl NamedSlot {
    /// The slot as a mesh draws it, named by the part at `part` where one
    /// resolved to it.
    fn keyed(&self, part: Option<u32>) -> Slot {
        let mut slot = Slot::new(self.index_count, self.material);
        if let Some(part) = part {
            slot = slot.named(part);
        }
        if let Some(texture) = self.texture.clone() {
            slot = slot.textured(texture);
        }
        if let Some(relief) = self.relief.clone() {
            slot = slot.relief(relief);
        }
        if let Some(shading) = self.shading.clone() {
            slot = slot.shading(shading);
        }
        if let Some(emissive) = self.emissive.clone() {
            slot = slot.emissive_map(emissive);
        }
        slot
    }
}