schematic_mesher/mesher/
mod.rs

1//! Mesh generation from block models.
2//!
3//! This module converts resolved block models into triangle meshes.
4
5pub mod geometry;
6pub mod element;
7pub mod face_culler;
8pub mod tint;
9
10pub use geometry::{Mesh, Vertex};
11pub use tint::{TintColors, TintProvider};
12
13use crate::atlas::TextureAtlas;
14use crate::error::Result;
15use crate::resource_pack::ResourcePack;
16use crate::types::{BlockPosition, BlockSource, BoundingBox, InputBlock};
17
18/// Main mesher configuration.
19#[derive(Debug, Clone)]
20pub struct MesherConfig {
21    /// Enable face culling between adjacent blocks.
22    pub cull_hidden_faces: bool,
23    /// Maximum texture atlas dimension.
24    pub atlas_max_size: u32,
25    /// Padding between textures in the atlas.
26    pub atlas_padding: u32,
27    /// Include air blocks in output.
28    pub include_air: bool,
29    /// Tint provider for block coloring (grass, foliage, water, redstone, etc.)
30    pub tint_provider: TintProvider,
31    /// Enable ambient occlusion.
32    pub ambient_occlusion: bool,
33    /// AO intensity (0.0 = no darkening, 1.0 = full darkening).
34    pub ao_intensity: f32,
35}
36
37impl Default for MesherConfig {
38    fn default() -> Self {
39        Self {
40            cull_hidden_faces: true,
41            atlas_max_size: 4096,
42            atlas_padding: 1,
43            include_air: false,
44            tint_provider: TintProvider::new(),
45            ambient_occlusion: true,
46            ao_intensity: 0.4,
47        }
48    }
49}
50
51impl MesherConfig {
52    /// Create config with a specific biome for tinting.
53    pub fn with_biome(mut self, biome: &str) -> Self {
54        self.tint_provider = TintProvider::for_biome(biome);
55        self
56    }
57
58    /// Create config with custom tint colors.
59    pub fn with_tint_colors(mut self, colors: TintColors) -> Self {
60        self.tint_provider = TintProvider::with_colors(colors);
61        self
62    }
63}
64
65/// Output from the mesher.
66#[derive(Debug)]
67pub struct MesherOutput {
68    /// The opaque geometry mesh (rendered first).
69    pub opaque_mesh: Mesh,
70    /// The transparent geometry mesh (rendered second).
71    pub transparent_mesh: Mesh,
72    /// The texture atlas.
73    pub atlas: TextureAtlas,
74    /// Bounding box of the mesh.
75    pub bounds: BoundingBox,
76}
77
78impl MesherOutput {
79    /// Get a combined mesh (for backwards compatibility).
80    /// Note: For correct transparency, use opaque_mesh and transparent_mesh separately.
81    pub fn mesh(&self) -> Mesh {
82        let mut combined = self.opaque_mesh.clone();
83        combined.merge(&self.transparent_mesh);
84        combined
85    }
86
87    /// Check if the output has any transparent geometry.
88    pub fn has_transparency(&self) -> bool {
89        !self.transparent_mesh.is_empty()
90    }
91
92    /// Get total vertex count across both meshes.
93    pub fn total_vertices(&self) -> usize {
94        self.opaque_mesh.vertex_count() + self.transparent_mesh.vertex_count()
95    }
96
97    /// Get total triangle count across both meshes.
98    pub fn total_triangles(&self) -> usize {
99        self.opaque_mesh.triangle_count() + self.transparent_mesh.triangle_count()
100    }
101}
102
103/// The main mesher struct.
104pub struct Mesher {
105    resource_pack: ResourcePack,
106    config: MesherConfig,
107}
108
109impl Mesher {
110    /// Create a new mesher with default configuration.
111    pub fn new(resource_pack: ResourcePack) -> Self {
112        Self {
113            resource_pack,
114            config: MesherConfig::default(),
115        }
116    }
117
118    /// Create a new mesher with custom configuration.
119    pub fn with_config(resource_pack: ResourcePack, config: MesherConfig) -> Self {
120        Self {
121            resource_pack,
122            config,
123        }
124    }
125
126    /// Get a reference to the resource pack.
127    pub fn resource_pack(&self) -> &ResourcePack {
128        &self.resource_pack
129    }
130
131    /// Get a reference to the configuration.
132    pub fn config(&self) -> &MesherConfig {
133        &self.config
134    }
135
136    /// Generate a mesh from a block source.
137    pub fn mesh<S: BlockSource>(&self, source: &S) -> Result<MesherOutput> {
138        let bounds = source.bounds();
139        let blocks: Vec<_> = source.iter_blocks().collect();
140
141        self.mesh_blocks_internal(blocks.into_iter(), bounds)
142    }
143
144    /// Generate a mesh from an iterator of blocks.
145    pub fn mesh_blocks<'a>(
146        &self,
147        blocks: impl Iterator<Item = (BlockPosition, &'a InputBlock)>,
148        bounds: BoundingBox,
149    ) -> Result<MesherOutput> {
150        self.mesh_blocks_internal(blocks, bounds)
151    }
152
153    fn mesh_blocks_internal<'a>(
154        &self,
155        blocks: impl Iterator<Item = (BlockPosition, &'a InputBlock)>,
156        bounds: BoundingBox,
157    ) -> Result<MesherOutput> {
158        let mut mesh_builder = element::MeshBuilder::new(&self.resource_pack, &self.config);
159
160        // Collect blocks for face culling
161        let blocks: Vec<_> = blocks.collect();
162
163        // Build occupancy map for face culling if enabled
164        // Uses model data to determine which blocks are full opaque cubes
165        let culler = if self.config.cull_hidden_faces {
166            Some(face_culler::FaceCuller::new(&self.resource_pack, &blocks))
167        } else {
168            None
169        };
170
171        // Process each block
172        for (pos, block) in &blocks {
173            if !self.config.include_air && block.is_air() {
174                continue;
175            }
176
177            mesh_builder.add_block(*pos, block, culler.as_ref())?;
178        }
179
180        // Build the final meshes and atlas
181        let (opaque_mesh, transparent_mesh, atlas) = mesh_builder.build()?;
182
183        Ok(MesherOutput {
184            opaque_mesh,
185            transparent_mesh,
186            atlas,
187            bounds,
188        })
189    }
190}