steel_worldgen/noise/
ore_veinifier.rs1use steel_math::map_clamped;
8use steel_registry::{REGISTRY, vanilla_blocks};
9use steel_utils::BlockStateId;
10use steel_utils::random::name_hash::NameHash;
11use steel_utils::random::{PositionalRandom, Random, RandomSplitter};
12use steel_worldgen::density::{ColumnCache, DimensionNoises};
13
14const VEININESS_THRESHOLD: f64 = 0.4;
16const EDGE_ROUNDOFF_BEGIN: f64 = 20.0;
18const MAX_EDGE_ROUNDOFF: f64 = -0.2;
20const VEIN_SOLIDNESS: f32 = 0.7;
22const MIN_RICHNESS: f64 = 0.1;
24const MAX_RICHNESS: f64 = 0.3;
26const CHANCE_OF_RAW_ORE_BLOCK: f32 = 0.02;
28const SKIP_ORE_IF_GAP_BELOW: f64 = -0.3;
30
31struct VeinType {
33 ore: BlockStateId,
34 raw_ore_block: BlockStateId,
35 filler: BlockStateId,
36 min_y: i32,
37 max_y: i32,
38}
39
40pub struct OreVeinifier {
43 ore_splitter: RandomSplitter,
44 copper: VeinType,
45 iron: VeinType,
46}
47
48impl OreVeinifier {
49 #[must_use]
54 pub fn new(splitter: &RandomSplitter) -> Self {
55 const ORE_HASH: NameHash = NameHash::new("minecraft:ore");
56 let mut ore_rng = splitter.with_hash_of(&ORE_HASH);
57 let ore_splitter = ore_rng.next_positional();
58
59 let copper = VeinType {
60 ore: REGISTRY
61 .blocks
62 .get_default_state_id(&vanilla_blocks::COPPER_ORE),
63 raw_ore_block: REGISTRY
64 .blocks
65 .get_default_state_id(&vanilla_blocks::RAW_COPPER_BLOCK),
66 filler: REGISTRY
67 .blocks
68 .get_default_state_id(&vanilla_blocks::GRANITE),
69 min_y: 0,
70 max_y: 50,
71 };
72
73 let iron = VeinType {
74 ore: REGISTRY
75 .blocks
76 .get_default_state_id(&vanilla_blocks::DEEPSLATE_IRON_ORE),
77 raw_ore_block: REGISTRY
78 .blocks
79 .get_default_state_id(&vanilla_blocks::RAW_IRON_BLOCK),
80 filler: REGISTRY.blocks.get_default_state_id(&vanilla_blocks::TUFF),
81 min_y: -60,
82 max_y: -8,
83 };
84
85 Self {
86 ore_splitter,
87 copper,
88 iron,
89 }
90 }
91
92 pub fn compute_interpolated<N: DimensionNoises>(
101 &self,
102 noises: &N,
103 cache: &mut N::ColumnCache,
104 interpolated: &[f64],
105 world_x: i32,
106 world_y: i32,
107 world_z: i32,
108 ) -> Option<BlockStateId> {
109 let vein_toggle = if N::vein_interp_enabled() {
110 noises.combine_vein_toggle(cache, interpolated, 0, world_y, 0)
111 } else {
112 cache.ensure(world_x, world_z, noises);
113 noises.router_vein_toggle(cache, world_x, world_y, world_z)
114 };
115
116 let vein_type = if vein_toggle > 0.0 {
118 &self.copper
119 } else {
120 &self.iron
121 };
122
123 let veininess = vein_toggle.abs();
124
125 let dist_from_top = vein_type.max_y - world_y;
127 let dist_from_bottom = world_y - vein_type.min_y;
128 if dist_from_bottom < 0 || dist_from_top < 0 {
129 return None;
130 }
131
132 let dist_from_edge = dist_from_top.min(dist_from_bottom);
134 let edge_roundoff = map_clamped(
135 f64::from(dist_from_edge),
136 0.0,
137 EDGE_ROUNDOFF_BEGIN,
138 MAX_EDGE_ROUNDOFF,
139 0.0,
140 );
141
142 if veininess + edge_roundoff < VEININESS_THRESHOLD {
143 return None;
144 }
145
146 let mut rng = self.ore_splitter.at(world_x, world_y, world_z);
148
149 if rng.next_f32() > VEIN_SOLIDNESS {
151 return None;
152 }
153
154 let vein_ridged = if N::vein_interp_enabled() {
156 noises.combine_vein_ridged(cache, interpolated, 0, world_y, 0)
157 } else {
158 cache.ensure(world_x, world_z, noises);
159 noises.router_vein_ridged(cache, world_x, world_y, world_z)
160 };
161 if vein_ridged >= 0.0 {
162 return None;
163 }
164
165 let richness = map_clamped(
167 veininess,
168 VEININESS_THRESHOLD,
169 0.6,
170 MIN_RICHNESS,
171 MAX_RICHNESS,
172 );
173
174 if (f64::from(rng.next_f32())) < richness {
175 cache.ensure(world_x, world_z, noises);
177 let vein_gap = noises.router_vein_gap(cache, world_x, world_y, world_z);
178 if vein_gap > SKIP_ORE_IF_GAP_BELOW {
179 if rng.next_f32() < CHANCE_OF_RAW_ORE_BLOCK {
181 Some(vein_type.raw_ore_block)
182 } else {
183 Some(vein_type.ore)
184 }
185 } else {
186 Some(vein_type.filler)
188 }
189 } else {
190 Some(vein_type.filler)
192 }
193 }
194}