steel_core/worldgen/carver/
canyon.rs1use std::f32::consts::{PI, TAU};
7
8use steel_math::trig;
9use steel_registry::carver::CanyonCarverConfiguration;
10use steel_utils::random::{Random, legacy_random::LegacyRandom};
11use steel_utils::{BlockPos, ChunkPos};
12use steel_worldgen::density::DimensionNoises;
13
14use crate::worldgen::carver::{CarveRun, can_reach, horizontal_tunnel_radius};
15
16const CARVER_RANGE: i32 = 4;
19const MAX_TUNNEL_DISTANCE: i32 = (CARVER_RANGE * 2 - 1) * 16;
20
21#[derive(Debug, Clone, Copy)]
23struct CanyonState {
24 x: f64,
25 y: f64,
26 z: f64,
27 horizontal_rotation: f32,
29 vertical_rotation: f32,
31}
32
33#[derive(Debug, Clone, Copy)]
35struct CanyonTunnel {
36 tunnel_seed: i64,
37 thickness: f32,
38 distance: i32,
39 y_scale: f64,
40}
41
42impl<N, F> CarveRun<'_, '_, N, F>
43where
44 N: DimensionNoises,
45 F: FnMut(BlockPos) -> u16,
46{
47 pub fn carve_canyon(
55 &mut self,
56 config: &CanyonCarverConfiguration,
57 source_pos: ChunkPos,
58 random: &mut LegacyRandom,
59 ) {
60 let source_min_x = source_pos.0.x * 16;
61 let source_min_z = source_pos.0.y * 16;
62
63 let state = CanyonState {
64 x: f64::from(source_min_x + random.next_i32_bounded(16)),
65 y: f64::from(config.base.y.sample(
66 random,
67 self.ctx.min_y,
68 self.ctx.gen_depth,
69 self.ctx.sea_level,
70 )),
71 z: f64::from(source_min_z + random.next_i32_bounded(16)),
72 horizontal_rotation: random.next_f32() * TAU,
73 vertical_rotation: config.vertical_rotation.sample(random),
74 };
75
76 let y_scale = f64::from(config.shape.y_scale.sample(random));
78 let thickness = config.shape.thickness.sample(random);
79 let distance =
80 (MAX_TUNNEL_DISTANCE as f32 * config.shape.distance_factor.sample(random)) as i32;
81 let tunnel_seed = random.next_i64();
82
83 let tunnel = CanyonTunnel {
84 tunnel_seed,
85 thickness,
86 distance,
87 y_scale,
88 };
89
90 self.do_carve_canyon(config, state, tunnel);
91 }
92
93 fn do_carve_canyon(
94 &mut self,
95 config: &CanyonCarverConfiguration,
96 mut state: CanyonState,
97 tunnel: CanyonTunnel,
98 ) {
99 let mut random = LegacyRandom::from_seed(tunnel.tunnel_seed as u64);
100 let width_factors = config
101 .shape
102 .init_width_factors(self.ctx.gen_depth, &mut random);
103 let mut y_rota: f32 = 0.0;
104 let mut x_rota: f32 = 0.0;
105
106 for current_step in 0..tunnel.distance {
107 let progress = PI * current_step as f32 / tunnel.distance as f32;
108 let mut horizontal_radius = horizontal_tunnel_radius(progress, tunnel.thickness);
109 let mut vertical_radius = horizontal_radius * tunnel.y_scale;
110 horizontal_radius *=
111 f64::from(config.shape.horizontal_radius_factor.sample(&mut random));
112 vertical_radius = config.shape.update_vertical_radius(
113 &mut random,
114 vertical_radius,
115 tunnel.distance as f32,
116 current_step as f32,
117 );
118
119 let xc = trig::cos(state.vertical_rotation);
120 let xs = trig::sin(state.vertical_rotation);
121 state.x += f64::from(trig::cos(state.horizontal_rotation) * xc);
122 state.y += f64::from(xs);
123 state.z += f64::from(trig::sin(state.horizontal_rotation) * xc);
124 state.vertical_rotation *= 0.7;
125 state.vertical_rotation += x_rota * 0.05;
126 state.horizontal_rotation += y_rota * 0.05;
127 x_rota *= 0.8;
128 y_rota *= 0.5;
129 x_rota += (random.next_f32() - random.next_f32()) * random.next_f32() * 2.0;
130 y_rota += (random.next_f32() - random.next_f32()) * random.next_f32() * 4.0;
131
132 if random.next_i32_bounded(4) == 0 {
133 continue;
134 }
135
136 if !can_reach(
137 self.chunk_min_x,
138 self.chunk_min_z,
139 state.x,
140 state.z,
141 current_step,
142 tunnel.distance,
143 tunnel.thickness,
144 ) {
145 return;
146 }
147
148 let min_y = self.ctx.min_y;
149 let skip_checker = |xd: f64, yd: f64, zd: f64, world_y: i32| {
150 should_skip_canyon(&width_factors, min_y, xd, yd, zd, world_y)
151 };
152
153 self.carve_ellipsoid(
154 state.x,
155 state.y,
156 state.z,
157 horizontal_radius,
158 vertical_radius,
159 skip_checker,
160 );
161 }
162 }
163}
164
165fn should_skip_canyon(
170 width_factors: &[f32],
171 min_y: i32,
172 xd: f64,
173 yd: f64,
174 zd: f64,
175 world_y: i32,
176) -> bool {
177 let y_index = (world_y - min_y - 1) as usize;
178 let factor = width_factors[y_index];
179 (xd * xd + zd * zd) * f64::from(factor) + yd * yd / 6.0 >= 1.0
180}