steel_core/worldgen/generator/
flat.rs1use glam::IVec3;
2use rustc_hash::FxHashMap;
3use steel_registry::biome::BiomeRef;
4use steel_registry::blocks::block_state_ext::BlockStateExt;
5use steel_registry::template_pool::{TemplateData, TemplatePoolData};
6use steel_registry::{REGISTRY, RegistryExt, vanilla_biomes};
7use steel_utils::random::RandomSource;
8use steel_utils::{BlockStateId, ChunkPos, Identifier};
9
10use crate::chunk::Chunk;
11use crate::worldgen::generator::{
12 CarversPhase, ChunkGenerator, GenerationChunk, NoisePhase, SurfacePhase,
13 xoroshiro_worldgen_region_random,
14};
15use crate::worldgen::region::WorldGenRegion;
16use crate::worldgen::structure::{StructureGenerator, create_structures};
17use steel_worldgen::noise::Beardifier;
18use steel_worldgen::structure::{ColumnBlock, StructureGenerationContext};
19
20pub struct FlatChunkGenerator {
25 pub layers: Vec<BlockStateId>,
27 biome_id: u16,
29 seed: i64,
31 sea_level: i32,
33 structure_generator: Option<StructureGenerator>,
35}
36
37impl FlatChunkGenerator {
38 #[must_use]
40 pub fn new(bedrock: BlockStateId, dirt: BlockStateId, grass: BlockStateId) -> Self {
41 Self::new_layers(vec![bedrock, dirt, dirt, grass])
42 }
43
44 #[must_use]
46 pub fn new_layers(layers: Vec<BlockStateId>) -> Self {
47 Self::new_layers_with_structures(layers, 0, 63, None)
48 }
49
50 #[must_use]
52 pub(crate) fn new_layers_with_structures(
53 layers: Vec<BlockStateId>,
54 seed: i64,
55 sea_level: i32,
56 structure_generator: Option<StructureGenerator>,
57 ) -> Self {
58 let biome_id = REGISTRY
59 .biomes
60 .id_from_key(&Identifier::vanilla("plains".to_string()))
61 .unwrap_or(0) as u16;
62
63 Self {
64 layers,
65 biome_id,
66 seed,
67 sea_level,
68 structure_generator,
69 }
70 }
71}
72
73struct FlatGenerationContext<'a> {
74 seed: i64,
75 chunk_x: i32,
76 chunk_z: i32,
77 chunk_min_x: i32,
78 chunk_min_z: i32,
79 center_block_x: i32,
80 center_block_z: i32,
81 sea_level: i32,
82 min_y: i32,
83 height: i32,
84 layers: &'a [BlockStateId],
85 biome: BiomeRef,
86 template_pools: &'a FxHashMap<Identifier, TemplatePoolData>,
87 templates: &'a FxHashMap<Identifier, TemplateData>,
88 surface_y_cache: Option<i32>,
89}
90
91impl FlatGenerationContext<'_> {
92 fn state_at_y(&self, y: i32) -> Option<BlockStateId> {
93 let relative_y = y.checked_sub(self.min_y)? as usize;
94 self.layers.get(relative_y).copied()
95 }
96
97 fn is_opaque_at_y(&self, y: i32, ocean_floor: bool) -> bool {
98 let Some(state) = self.state_at_y(y) else {
99 return false;
100 };
101 if ocean_floor {
102 state.is_solid()
103 } else {
104 state.is_solid() || state.has_fluid()
105 }
106 }
107
108 fn base_height_flat(&self, ocean_floor: bool) -> i32 {
109 for y in (self.min_y..self.min_y + self.height).rev() {
110 if self.is_opaque_at_y(y, ocean_floor) {
111 return y + 1;
112 }
113 }
114 self.min_y
115 }
116}
117
118impl StructureGenerationContext for FlatGenerationContext<'_> {
119 fn seed(&self) -> i64 {
120 self.seed
121 }
122
123 fn chunk_x(&self) -> i32 {
124 self.chunk_x
125 }
126
127 fn chunk_z(&self) -> i32 {
128 self.chunk_z
129 }
130
131 fn chunk_min_x(&self) -> i32 {
132 self.chunk_min_x
133 }
134
135 fn chunk_min_z(&self) -> i32 {
136 self.chunk_min_z
137 }
138
139 fn center_block_x(&self) -> i32 {
140 self.center_block_x
141 }
142
143 fn center_block_z(&self) -> i32 {
144 self.center_block_z
145 }
146
147 fn sea_level(&self) -> i32 {
148 self.sea_level
149 }
150
151 fn min_y(&self) -> i32 {
152 self.min_y
153 }
154
155 fn height(&self) -> i32 {
156 self.height
157 }
158
159 fn template_pools(&self) -> &FxHashMap<Identifier, TemplatePoolData> {
160 self.template_pools
161 }
162
163 fn templates(&self) -> &FxHashMap<Identifier, TemplateData> {
164 self.templates
165 }
166
167 fn base_height(&mut self, _x: i32, _z: i32, ocean_floor: bool) -> i32 {
168 self.base_height_flat(ocean_floor)
169 }
170
171 fn base_height_full(&mut self, _x: i32, _z: i32, ocean_floor: bool) -> i32 {
172 self.base_height_flat(ocean_floor)
173 }
174
175 fn biome_at(&mut self, _block_x: i32, _block_y: i32, _block_z: i32) -> BiomeRef {
176 self.biome
177 }
178
179 fn column_state(&mut self, _x: i32, y: i32, _z: i32) -> ColumnBlock {
180 let Some(state) = self.state_at_y(y) else {
181 return ColumnBlock::Air;
182 };
183 if state.is_solid() {
184 ColumnBlock::Solid
185 } else if state.has_fluid() {
186 ColumnBlock::Fluid
187 } else {
188 ColumnBlock::Air
189 }
190 }
191
192 fn surface_y(&mut self) -> i32 {
193 if let Some(y) = self.surface_y_cache {
194 return y;
195 }
196 let y = self.base_height_flat(false) - 1;
197 self.surface_y_cache = Some(y);
198 y
199 }
200
201 fn terrain_surface_height(&self, _x: i32, _z: i32, ocean_floor: bool) -> i32 {
202 self.base_height_flat(ocean_floor)
203 }
204
205 fn terrain_is_opaque(&self, _x: i32, y: i32, _z: i32, ocean_floor: bool) -> bool {
206 self.is_opaque_at_y(y, ocean_floor)
207 }
208}
209
210impl ChunkGenerator for FlatChunkGenerator {
211 fn min_y(&self) -> i32 {
212 0
213 }
214
215 fn gen_depth(&self) -> i32 {
216 384
217 }
218
219 fn noise_biome(&self, _quart_x: i32, _quart_y: i32, _quart_z: i32) -> BiomeRef {
220 &vanilla_biomes::PLAINS
221 }
222
223 fn spawn_height(&self, min_y: i32, height: i32) -> i32 {
224 min_y + height.min(self.layers.len() as i32)
225 }
226
227 fn structure_generator(&self) -> Option<&StructureGenerator> {
228 self.structure_generator.as_ref()
229 }
230
231 fn create_structures(&self, chunk: &Chunk) {
232 let Some(structure_generator) = &self.structure_generator else {
233 return;
234 };
235
236 let pos = chunk.pos();
237 let chunk_x = pos.0.x;
238 let chunk_z = pos.0.y;
239 let chunk_min_x = chunk_x * 16;
240 let chunk_min_z = chunk_z * 16;
241 let mut ctx = FlatGenerationContext {
242 seed: self.seed,
243 chunk_x,
244 chunk_z,
245 chunk_min_x,
246 chunk_min_z,
247 center_block_x: chunk_min_x + 8,
248 center_block_z: chunk_min_z + 8,
249 sea_level: self.sea_level,
250 min_y: chunk.min_y(),
251 height: (chunk.sections().sections.len() * 16) as i32,
252 layers: &self.layers,
253 biome: &vanilla_biomes::PLAINS,
254 template_pools: structure_generator.template_pools(),
255 templates: structure_generator.templates(),
256 surface_y_cache: None,
257 };
258 create_structures(structure_generator, chunk, &mut ctx);
259 }
260
261 fn create_biomes(&self, chunk: &Chunk) {
262 let section_count = chunk.sections().sections.len();
263
264 for section_index in 0..section_count {
265 let section = &chunk.sections().sections[section_index];
266 let mut section_guard = section.write();
267
268 for local_quart_x in 0..4usize {
269 for local_quart_y in 0..4usize {
270 for local_quart_z in 0..4usize {
271 section_guard.biomes.set(
272 local_quart_x,
273 local_quart_y,
274 local_quart_z,
275 self.biome_id,
276 );
277 }
278 }
279 }
280 drop(section_guard);
281 }
282
283 chunk.mark_dirty();
284 }
285
286 fn fill_from_noise(
287 &self,
288 chunk: GenerationChunk<'_, NoisePhase>,
289 _beardifier: Option<&Beardifier>,
290 ) {
291 let max_relative_y = chunk.section_count() * 16;
292
293 for x in 0..16 {
294 for z in 0..16 {
295 for (relative_y, block) in self.layers.iter().enumerate().take(max_relative_y) {
296 chunk.set_relative_block(x, relative_y, z, *block);
297 }
298 }
299 }
300 }
301
302 fn build_surface(
303 &self,
304 _chunk: GenerationChunk<'_, SurfacePhase>,
305 _neighbor_biomes: &dyn Fn(IVec3) -> u16,
306 ) {
307 }
308
309 fn apply_carvers(&self, _chunk: GenerationChunk<'_, CarversPhase>) {}
310
311 fn create_worldgen_region_random(&self, world_seed: i64, center: ChunkPos) -> RandomSource {
312 xoroshiro_worldgen_region_random(world_seed, center)
313 }
314
315 fn apply_biome_decorations(&self, _region: &mut WorldGenRegion<'_>) {}
316}