steel_worldgen/density/traits.rs
1//! Traits for dimension-specific noise generation.
2//!
3//! These traits abstract over dimension-specific types (overworld, nether, etc.)
4//! allowing generic chunk generation code to work with any dimension's transpiled
5//! density functions.
6
7use std::simd::f64x4;
8
9use crate::BlockStateId;
10use crate::random::RandomSplitter;
11use crate::surface::SurfaceRuleContext;
12use rustc_hash::FxHashMap;
13
14use super::NoiseParameters;
15
16/// Noise settings for a dimension, parsed from the datapack.
17///
18/// These are compile-time constants generated from `noise_settings` JSON files.
19pub trait NoiseSettings: Send + Sync {
20 /// Minimum Y coordinate for this dimension.
21 const MIN_Y: i32;
22 /// Total height of the world in blocks.
23 const HEIGHT: i32;
24 /// Sea level Y coordinate.
25 const SEA_LEVEL: i32;
26 /// Cell width in blocks (XZ direction).
27 const CELL_WIDTH: i32;
28 /// Cell height in blocks (Y direction).
29 const CELL_HEIGHT: i32;
30 /// Whether aquifers are enabled for this dimension.
31 const AQUIFERS_ENABLED: bool;
32 /// Whether ore veins are enabled for this dimension.
33 const ORE_VEINS_ENABLED: bool;
34 /// Whether this dimension uses Java's LCG random (true) or Xoroshiro (false).
35 const LEGACY_RANDOM_SOURCE: bool;
36
37 /// Get the default block state ID for this dimension.
38 fn default_block_id() -> BlockStateId;
39
40 /// Get the default fluid state ID for this dimension.
41 fn default_fluid_id() -> BlockStateId;
42}
43
44/// Column cache for a dimension's flat-cached density function results.
45///
46/// Stores Y-independent values that only need to be computed once per (x, z) column.
47pub trait ColumnCache: Clone + Default + Send + Sync {
48 /// The associated noises type for this cache.
49 type Noises: DimensionNoises<ColumnCache = Self>;
50
51 /// Ensure the cache is populated for the given block coordinates.
52 ///
53 /// If the cache already holds values for this column, this is a no-op.
54 fn ensure(&mut self, x: i32, z: i32, noises: &Self::Noises);
55
56 /// Pre-compute flat-cached values for all quart positions in a chunk.
57 ///
58 /// Matches vanilla's `NoiseChunk.FlatCache`: eagerly fills a 2D grid of
59 /// `(quart_size+1)²` entries (size baked in per dimension at compile time).
60 /// After this call, `ensure()` for in-bounds positions is an O(1) grid
61 /// lookup. Out-of-bounds positions fall back to on-the-fly evaluation at
62 /// raw (non-quantized) coordinates.
63 fn init_grid(&mut self, chunk_block_x: i32, chunk_block_z: i32, noises: &Self::Noises);
64}
65
66/// All noise generators and density functions for a dimension.
67///
68/// This trait abstracts over dimension-specific noise types (`OverworldNoises`,
69/// `NetherNoises`, etc.) allowing generic code to work with any dimension.
70pub trait DimensionNoises: Sized + Send + Sync {
71 /// The column cache type for this dimension.
72 type ColumnCache: ColumnCache<Noises = Self>;
73 /// The noise settings type for this dimension.
74 type Settings: NoiseSettings;
75
76 /// Create all noise generators from a world seed and its positional splitter.
77 fn create(
78 seed: u64,
79 splitter: &RandomSplitter,
80 params: &FxHashMap<String, NoiseParameters>,
81 ) -> Self;
82
83 // ── Router functions ────────────────────────────────────────────────────
84
85 /// Final density for terrain generation (positive = solid, negative = air).
86 fn router_final_density(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
87
88 /// Depth from surface (used for terrain shaping).
89 fn router_depth(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
90
91 // ── Aquifer router functions ────────────────────────────────────────────
92
93 /// Barrier noise for aquifer boundaries.
94 fn router_barrier(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
95
96 /// Fluid level floodedness for aquifers.
97 fn router_fluid_level_floodedness(
98 &self,
99 cache: &mut Self::ColumnCache,
100 x: i32,
101 y: i32,
102 z: i32,
103 ) -> f64;
104
105 /// Fluid level spread for aquifers.
106 fn router_fluid_level_spread(
107 &self,
108 cache: &mut Self::ColumnCache,
109 x: i32,
110 y: i32,
111 z: i32,
112 ) -> f64;
113
114 /// Lava placement noise for aquifers.
115 fn router_lava(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
116
117 // ── Ore vein router functions ───────────────────────────────────────────
118
119 /// Vein toggle (sign determines copper vs iron).
120 fn router_vein_toggle(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
121
122 /// Vein ridged noise for ore placement.
123 fn router_vein_ridged(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
124
125 /// Vein gap noise for ore vs filler placement.
126 fn router_vein_gap(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
127
128 // ── Climate/biome router functions (Y-independent, cached) ──────────────
129
130 /// Erosion value (cached in column cache).
131 fn router_erosion(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
132
133 /// Continentalness value (cached in column cache).
134 fn router_continentalness(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
135
136 /// Temperature value (cached in column cache).
137 fn router_temperature(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
138
139 /// Vegetation/humidity value (cached in column cache).
140 fn router_vegetation(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
141
142 /// Ridges/weirdness value (cached in column cache).
143 fn router_ridges(&self, cache: &mut Self::ColumnCache, x: i32, y: i32, z: i32) -> f64;
144
145 /// Preliminary surface level (cached in column cache).
146 fn router_preliminary_surface_level(
147 &self,
148 cache: &mut Self::ColumnCache,
149 x: i32,
150 y: i32,
151 z: i32,
152 ) -> f64;
153
154 // ── Interpolation functions ─────────────────────────────────────────────
155
156 /// Total number of independently interpolated channels across all router
157 /// entries (`final_density` + `vein_toggle` + `vein_ridged`).
158 fn interpolated_count() -> usize;
159
160 /// Whether vein functions have interpolation channels.
161 fn vein_interp_enabled() -> bool;
162
163 /// Compute blended noise for an entire column of Y values.
164 ///
165 /// Called by `NoiseChunk::fill_slice` before iterating over Y corners.
166 /// Dimensions that use `BlendedNoise` (e.g. overworld) should override this
167 /// to SIMD-batch the blended noise computation.
168 ///
169 /// Default: no-op (fills `out` with zeros).
170 fn compute_noise_column(&self, _x: i32, _block_ys: &[i32], _z: i32, out: &mut [f64]) {
171 out.fill(0.0);
172 }
173
174 /// Evaluate the inner functions of all `Interpolated` markers at a cell corner.
175 ///
176 /// `out` must have length [`interpolated_count()`]. Each element receives
177 /// the value of one `Interpolated` marker's inner function at `(x, y, z)`.
178 /// `blended_noise_value` is the precomputed blended noise for this Y level.
179 fn fill_cell_corner_densities(
180 &self,
181 cache: &mut Self::ColumnCache,
182 x: i32,
183 y: i32,
184 z: i32,
185 blended_noise_value: f64,
186 out: &mut [f64],
187 );
188
189 /// SIMD form of [`fill_cell_corner_densities`] that batches 4 cell-corner
190 /// Y values at fixed `(x, z)`.
191 ///
192 /// `out` layout: lane-major `SoA`. Lane `i`'s `interpolated_count()` channels
193 /// occupy `out[i * interpolated_count()..(i + 1) * interpolated_count()]`.
194 /// `out` must have length `4 * interpolated_count()`.
195 ///
196 /// The default implementation calls the scalar [`fill_cell_corner_densities`]
197 /// four times. Dimensions can override with a true SIMD implementation (the
198 /// transpiled `compute_*_4x` chain) once the SIMD codegen is in place.
199 ///
200 /// [`fill_cell_corner_densities`]: Self::fill_cell_corner_densities
201 fn fill_cell_corner_densities_4x(
202 &self,
203 cache: &mut Self::ColumnCache,
204 x: i32,
205 ys: f64x4,
206 z: i32,
207 blended_noise_values: f64x4,
208 out: &mut [f64],
209 ) {
210 let interp_count = Self::interpolated_count();
211 let ys_arr = ys.to_array();
212 let blended_arr = blended_noise_values.to_array();
213 for lane in 0..4 {
214 let dst = &mut out[lane * interp_count..(lane + 1) * interp_count];
215 #[expect(
216 clippy::cast_possible_truncation,
217 reason = "block Y values are integer-valued f64s in cell-corner range"
218 )]
219 let y = ys_arr[lane] as i32;
220 self.fill_cell_corner_densities(cache, x, y, z, blended_arr[lane], dst);
221 }
222 }
223
224 /// Combine trilinearly interpolated values for `final_density`.
225 fn combine_interpolated(
226 &self,
227 cache: &mut Self::ColumnCache,
228 interpolated: &[f64],
229 x: i32,
230 y: i32,
231 z: i32,
232 ) -> f64;
233
234 /// Combine trilinearly interpolated values for `vein_toggle`.
235 fn combine_vein_toggle(
236 &self,
237 cache: &mut Self::ColumnCache,
238 interpolated: &[f64],
239 x: i32,
240 y: i32,
241 z: i32,
242 ) -> f64;
243
244 /// Combine trilinearly interpolated values for `vein_ridged`.
245 fn combine_vein_ridged(
246 &self,
247 cache: &mut Self::ColumnCache,
248 interpolated: &[f64],
249 x: i32,
250 y: i32,
251 z: i32,
252 ) -> f64;
253
254 // ── Surface rules ───────────────────────────────────────────────────────
255
256 /// Noise IDs referenced by this dimension's surface rule `NoiseThreshold`
257 /// conditions. Used to construct the `SurfaceSystem`'s condition noises.
258 fn surface_noise_ids() -> &'static [&'static str];
259
260 /// Random IDs referenced by this dimension's surface rule `VerticalGradient`
261 /// conditions. Used to construct reusable positional random factories.
262 fn surface_gradient_ids() -> &'static [&'static str];
263
264 /// Block states returned by this dimension's generated surface rules.
265 fn surface_rule_block_states() -> &'static [BlockStateId];
266
267 /// Whether the generated surface rule reads biome-dependent context.
268 fn surface_rule_uses_biome() -> bool;
269
270 /// Whether the generated surface rule reads preliminary surface level.
271 fn surface_rule_uses_preliminary_surface() -> bool;
272
273 /// Whether the generated surface rule reads surface secondary noise.
274 fn surface_rule_uses_surface_secondary() -> bool;
275
276 /// Whether the generated surface rule reads steep-column context.
277 fn surface_rule_uses_steep() -> bool;
278
279 /// Apply the transpiled surface rule at the given context position.
280 fn try_apply_surface_rule(ctx: &mut SurfaceRuleContext<'_>) -> Option<BlockStateId>;
281}