bones3_remesh/vertex_data/
cube.rs

1//! Contains lookup tables and block model builders for generating cubic shapes
2//! for block models.
3
4use bevy::prelude::{IVec3, Vec2, Vec3};
5
6use crate::mesh::block_model::{BlockModelGenerator, BlockOcclusion};
7use crate::vertex_data::TempMesh;
8
9/// Contains the vertex data for generating a cube.
10///
11/// The vertex data is laid out as an array of vertices, where each vertex is
12/// stored as a tuple containing the vertex position, normal, and uv, in that
13/// order. Each of these vertex attributes is stored as a small array of floats,
14/// based on the size of the data stored in that attribute for a single vertex.
15///
16/// The vertex are laid out in six groups, where each group contains 4 vertices,
17/// pertaining to a single face of the cube. The face groups are stored in the
18/// order: -X, +X, -Y, +Y, -Z, +Z. In addition, each face group uses a quad
19/// index layout, as determined by [`QUAD_INDICES`].
20#[rustfmt::skip]
21const CUBE_VERTICES: [(Vec3, Vec3, Vec2); 24] = [
22    // -X
23    (Vec3::new(0.0, 0.0, 0.0), Vec3::new(-1., 0.0, 0.0), Vec2::new(0.0, 0.0)),
24    (Vec3::new(0.0, 0.0, 1.0), Vec3::new(-1., 0.0, 0.0), Vec2::new(0.0, 1.0)),
25    (Vec3::new(0.0, 1.0, 1.0), Vec3::new(-1., 0.0, 0.0), Vec2::new(1.0, 1.0)),
26    (Vec3::new(0.0, 1.0, 0.0), Vec3::new(-1., 0.0, 0.0), Vec2::new(1.0, 0.0)),
27    // +X
28    (Vec3::new(1.0, 0.0, 0.0), Vec3::new(1.0, 0.0, 0.0), Vec2::new(0.0, 0.0)),
29    (Vec3::new(1.0, 1.0, 0.0), Vec3::new(1.0, 0.0, 0.0), Vec2::new(0.0, 1.0)),
30    (Vec3::new(1.0, 1.0, 1.0), Vec3::new(1.0, 0.0, 0.0), Vec2::new(1.0, 1.0)),
31    (Vec3::new(1.0, 0.0, 1.0), Vec3::new(1.0, 0.0, 0.0), Vec2::new(1.0, 0.0)),
32    // -Y
33    (Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, -1., 0.0), Vec2::new(0.0, 0.0)),
34    (Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, -1., 0.0), Vec2::new(0.0, 1.0)),
35    (Vec3::new(1.0, 0.0, 1.0), Vec3::new(0.0, -1., 0.0), Vec2::new(1.0, 1.0)),
36    (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, -1., 0.0), Vec2::new(1.0, 0.0)),
37    // +Y
38    (Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 1.0, 0.0), Vec2::new(0.0, 0.0)),
39    (Vec3::new(0.0, 1.0, 1.0), Vec3::new(0.0, 1.0, 0.0), Vec2::new(0.0, 1.0)),
40    (Vec3::new(1.0, 1.0, 1.0), Vec3::new(0.0, 1.0, 0.0), Vec2::new(1.0, 1.0)),
41    (Vec3::new(1.0, 1.0, 0.0), Vec3::new(0.0, 1.0, 0.0), Vec2::new(1.0, 0.0)),
42    // -Z
43    (Vec3::new(0.0, 0.0, 0.0), Vec3::new(0.0, 0.0, -1.), Vec2::new(0.0, 0.0)),
44    (Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 0.0, -1.), Vec2::new(0.0, 1.0)),
45    (Vec3::new(1.0, 1.0, 0.0), Vec3::new(0.0, 0.0, -1.), Vec2::new(1.0, 1.0)),
46    (Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, 0.0, -1.), Vec2::new(1.0, 0.0)),
47    // +Z
48    (Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, 1.0), Vec2::new(0.0, 0.0)),
49    (Vec3::new(1.0, 0.0, 1.0), Vec3::new(0.0, 0.0, 1.0), Vec2::new(0.0, 1.0)),
50    (Vec3::new(1.0, 1.0, 1.0), Vec3::new(0.0, 0.0, 1.0), Vec2::new(1.0, 1.0)),
51    (Vec3::new(0.0, 1.0, 1.0), Vec3::new(0.0, 0.0, 1.0), Vec2::new(1.0, 0.0)),
52];
53
54/// The relative indices that are used to indicate how the vertices of a quad
55/// are applied to write to a mesh with the TriangleList topology.
56const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3];
57
58/// A block model builder for a cube.
59///
60/// This builder is designed to make it easier to write a custom cube model to a
61/// temp mesh.
62pub struct CubeModelBuilder {
63    /// The local position of the cube within the block.
64    local_pos: Vec3,
65
66    /// The size of the cube.
67    size: Vec3,
68
69    /// The occlusion of this cube.
70    occlusion: BlockOcclusion,
71}
72
73impl CubeModelBuilder {
74    /// Creates a new cube model builder with default settings.
75    ///
76    /// The default settings for the cube model is a 1x1x1 cube, located at the
77    /// origin, with no occlusion.
78    pub fn new() -> Self {
79        // TODO Add texture atlas support
80        Self {
81            local_pos: Vec3::ZERO,
82            size:      Vec3::ONE,
83            occlusion: BlockOcclusion::empty(),
84        }
85    }
86
87    /// Defines the position of this cube model within the block.
88    ///
89    /// The cube position is relative to the minimum corner of the cube.
90    pub fn set_pos(mut self, pos: Vec3) -> Self {
91        self.local_pos = pos;
92        self
93    }
94
95    /// Sets the size of this cube model.
96    pub fn set_size(mut self, size: Vec3) -> Self {
97        self.size = size;
98        self
99    }
100
101    /// Sets the faces of the cube that will be occluded.
102    pub fn set_occlusion(mut self, occlusion: BlockOcclusion) -> Self {
103        self.occlusion = occlusion;
104        self
105    }
106}
107
108impl Default for CubeModelBuilder {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114impl BlockModelGenerator for CubeModelBuilder {
115    fn write_to_mesh(&self, mesh: &mut TempMesh, block_pos: IVec3) {
116        let pos = block_pos.as_vec3() + self.local_pos;
117        let size = self.size;
118        let occlusion = self.occlusion;
119
120        let mut quad = |offset: usize| {
121            let vertex_count = mesh.vertices.len() as u16;
122            mesh.indices
123                .extend_from_slice(&QUAD_INDICES.map(|i| i + vertex_count));
124
125            for vert_data in CUBE_VERTICES.iter().skip(offset).take(4) {
126                let (vertex, normal, uv) = *vert_data;
127                mesh.vertices.push(vertex * size + pos);
128                mesh.normals.push(normal);
129                mesh.uvs.push(uv);
130            }
131        };
132
133        if !occlusion.contains(BlockOcclusion::NEG_X) {
134            quad(0);
135        }
136
137        if !occlusion.contains(BlockOcclusion::POS_X) {
138            quad(4);
139        }
140
141        if !occlusion.contains(BlockOcclusion::NEG_Y) {
142            quad(8);
143        }
144
145        if !occlusion.contains(BlockOcclusion::POS_Y) {
146            quad(12);
147        }
148
149        if !occlusion.contains(BlockOcclusion::NEG_Z) {
150            quad(16);
151        }
152
153        if !occlusion.contains(BlockOcclusion::POS_Z) {
154            quad(20);
155        }
156    }
157}
158
159#[cfg(test)]
160mod test {
161    use pretty_assertions::assert_eq;
162
163    use super::*;
164
165    #[test]
166    fn half_slab() {
167        let mut mesh = TempMesh::default();
168        let cube = CubeModelBuilder::new()
169            .set_size(Vec3::new(1.0, 0.5, 1.0))
170            .set_occlusion(BlockOcclusion::NEG_Y);
171
172        cube.write_to_mesh(&mut mesh, IVec3::new(3, 7, 2));
173
174        #[rustfmt::skip]
175        assert_eq!(mesh.vertices, vec![
176            // -X
177            Vec3::new(3.0, 7.0, 2.0), Vec3::new(3.0, 7.0, 3.0),
178            Vec3::new(3.0, 7.5, 3.0), Vec3::new(3.0, 7.5, 2.0),
179            // +X
180            Vec3::new(4.0, 7.0, 2.0), Vec3::new(4.0, 7.5, 2.0),
181            Vec3::new(4.0, 7.5, 3.0), Vec3::new(4.0, 7.0, 3.0),
182            // +Y
183            Vec3::new(3.0, 7.5, 2.0), Vec3::new(3.0, 7.5, 3.0),
184            Vec3::new(4.0, 7.5, 3.0), Vec3::new(4.0, 7.5, 2.0),
185            // -Z
186            Vec3::new(3.0, 7.0, 2.0), Vec3::new(3.0, 7.5, 2.0),
187            Vec3::new(4.0, 7.5, 2.0), Vec3::new(4.0, 7.0, 2.0),
188            // +Z
189            Vec3::new(3.0, 7.0, 3.0), Vec3::new(4.0, 7.0, 3.0),
190            Vec3::new(4.0, 7.5, 3.0), Vec3::new(3.0, 7.5, 3.0),
191        ]);
192
193        #[rustfmt::skip]
194        assert_eq!(mesh.normals, vec![
195            // -X
196            Vec3::new(-1., 0.0, 0.0), Vec3::new(-1., 0.0, 0.0),
197            Vec3::new(-1., 0.0, 0.0), Vec3::new(-1., 0.0, 0.0),
198            // +X
199            Vec3::new(1.0, 0.0, 0.0), Vec3::new(1.0, 0.0, 0.0),
200            Vec3::new(1.0, 0.0, 0.0), Vec3::new(1.0, 0.0, 0.0),
201            // +Y
202            Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 1.0, 0.0),
203            Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, 1.0, 0.0),
204            // -Z
205            Vec3::new(0.0, 0.0, -1.), Vec3::new(0.0, 0.0, -1.),
206            Vec3::new(0.0, 0.0, -1.), Vec3::new(0.0, 0.0, -1.),
207            // +Z
208            Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, 1.0),
209            Vec3::new(0.0, 0.0, 1.0), Vec3::new(0.0, 0.0, 1.0),
210        ]);
211
212        #[rustfmt::skip]
213        assert_eq!(mesh.uvs, vec![
214            // -X
215            Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0),
216            Vec2::new(1.0, 1.0), Vec2::new(1.0, 0.0),
217            // +X
218            Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0),
219            Vec2::new(1.0, 1.0), Vec2::new(1.0, 0.0),
220            // +Y
221            Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0),
222            Vec2::new(1.0, 1.0), Vec2::new(1.0, 0.0),
223            // -Z
224            Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0),
225            Vec2::new(1.0, 1.0), Vec2::new(1.0, 0.0),
226            // +Z
227            Vec2::new(0.0, 0.0), Vec2::new(0.0, 1.0),
228            Vec2::new(1.0, 1.0), Vec2::new(1.0, 0.0),
229        ]);
230
231        #[rustfmt::skip]
232        assert_eq!(mesh.indices, vec![
233            // -X
234             0,  1,  2,  0,  2,  3,
235            // +X
236             4,  5,  6,  4,  6,  7,
237            // +Y
238             8,  9, 10,  8, 10, 11,
239            // -Z
240            12, 13, 14, 12, 14, 15,
241            // +Z
242            16, 17, 18, 16, 18, 19,
243        ]);
244    }
245}