Skip to main content

mirage_engine/mesh/
set.rs

1//! The set of every mesh a game draws, and the macro that writes it.
2
3use core::hash::Hash;
4
5use crate::Catalog;
6use crate::assets::Assets;
7use crate::holds::Sealed;
8use crate::mesh::Built;
9
10/// The set of every mesh type a game draws, named as
11/// [`Game::Meshes`](crate::Game::Meshes).
12///
13/// Written by [`meshes!`](crate::meshes) and never implemented by hand;
14/// [`NoMeshes`] for a game that draws nothing. Its catalog holds every
15/// value every one of its types catalogs.
16pub trait Meshes: Sealed + Catalog + Hash + Eq + Clone + 'static {
17    /// Builds the mesh this value wraps, its part type dropped, beside what
18    /// that build did not get, under the mesh type's own name.
19    #[doc(hidden)]
20    fn build(&self, assets: &Assets) -> Built;
21}
22
23/// The set of a game that draws no mesh of its own.
24///
25/// No value of it exists, so no draw compiles for such a game. `()` is not a
26/// mesh set:
27///
28/// ```compile_fail
29/// use mirage_engine::prelude::*;
30///
31/// fn set<M: Meshes>() {}
32///
33/// set::<()>();
34/// ```
35#[derive(Clone, Debug, Eq, Hash, PartialEq)]
36pub enum NoMeshes {}
37
38impl Sealed for NoMeshes {}
39
40impl Catalog for NoMeshes {
41    fn catalog() -> Vec<Self> {
42        Vec::new()
43    }
44}
45
46impl Meshes for NoMeshes {
47    fn build(&self, _assets: &Assets) -> Built {
48        match *self {}
49    }
50}
51
52/// Writes the set of every mesh a game draws: an enum with one variant
53/// wrapping each named type, which [`Game::Meshes`](crate::Game::Meshes)
54/// names.
55///
56/// Each type is named by its own name and must be in scope; a type named
57/// twice, and a type that implements [`Mesh`](crate::Mesh) for two part
58/// vocabularies, each fail to compile here. The set holds itself as well
59/// as every type it names, so a draw
60/// [`into_set`](crate::mesh::Instance::into_set) turned into the set is
61/// drawn too. A `pub` before `enum` makes
62/// the enum public, and `///` lines before that are the enum's.
63///
64/// ```
65/// use mirage_engine::prelude::*;
66///
67/// meshes! { enum Shape { Cube, Sphere } }
68/// ```
69#[macro_export]
70macro_rules! meshes {
71    ($(#[$attribute:meta])* $vis:vis enum $set:ident { $($mesh:ident),+ $(,)? }) => {
72        $(#[$attribute])*
73        #[derive(Clone, Eq, Hash, PartialEq)]
74        $vis enum $set {
75            $($mesh($mesh)),+
76        }
77
78        $(
79            impl ::core::convert::From<$mesh> for $set {
80                fn from(mesh: $mesh) -> Self {
81                    Self::$mesh(mesh)
82                }
83            }
84
85            impl $crate::Holds<$mesh> for $set {}
86        )+
87
88        impl $crate::Holds<$set> for $set {}
89
90        impl $crate::Sealed for $set {}
91
92        impl $crate::Catalog for $set {
93            fn catalog() -> ::std::vec::Vec<Self> {
94                let mut all = ::std::vec::Vec::new();
95                $(all.extend(<$mesh as $crate::Catalog>::catalog().into_iter().map(Self::$mesh));)+
96                all
97            }
98        }
99
100        impl $crate::Meshes for $set {
101            fn build(&self, assets: &$crate::Assets) -> $crate::mesh::Built {
102                match self {
103                    $(
104                        Self::$mesh(mesh) => $crate::Mesh::build(mesh, assets)
105                            .erased(::core::stringify!($mesh))
106                    ),+
107                }
108            }
109        }
110    };
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::mesh::{Cube, Mesh, MeshData, Sphere};
117
118    meshes! {
119        #[derive(Debug)]
120        enum Shape { Cube, Sphere }
121    }
122
123    meshes! {
124        enum Shapes { Wedge }
125    }
126
127    /// A mesh whose one index reaches past corners it does not have, which
128    /// the set reports under this type's name.
129    #[derive(Clone, Debug, Eq, Hash, PartialEq)]
130    struct Wedge;
131
132    impl Catalog for Wedge {
133        fn catalog() -> Vec<Self> {
134            vec![Self]
135        }
136    }
137
138    impl Mesh for Wedge {
139        fn build(&self, _assets: &Assets) -> MeshData {
140            MeshData::new(Vec::new(), vec![0])
141        }
142    }
143
144    #[test]
145    fn a_set_catalogs_every_value_of_every_type_it_names() {
146        let catalog = Shape::catalog();
147
148        assert_eq!(catalog.len(), 1 + Sphere::catalog().len());
149        assert_eq!(catalog[0], Shape::Cube(Cube));
150        assert!(matches!(catalog[1], Shape::Sphere(_)));
151    }
152
153    #[test]
154    fn a_set_value_builds_the_mesh_it_wraps_under_its_types_name() {
155        let assets = Assets::default();
156        let cube = Shape::from(Cube).build(&assets);
157
158        assert_eq!(cube.geometry.part_count(), 1);
159        assert!(
160            cube.unresolved.is_empty(),
161            "nothing is wrong with a cube: {:?}",
162            cube.unresolved
163        );
164
165        let wedge = Shapes::from(Wedge).build(&assets);
166        assert_eq!(
167            wedge
168                .unresolved
169                .error()
170                .expect("the index reaches past the corners")
171                .to_string(),
172            "the game's assets did not resolve: the mesh `Wedge` has the index 0 past its 0 \
173             vertices",
174            "and a value of the set names its own type in what it did not get"
175        );
176    }
177
178    #[test]
179    fn the_set_of_no_meshes_catalogs_nothing() {
180        assert!(NoMeshes::catalog().is_empty());
181    }
182}