voxelize/world/generators/
terrain.rs1use kdtree::{distance::squared_euclidean, KdTree};
2use log::info;
3use serde::Serialize;
4use splines::interpolate::Interpolator;
5
6use crate::WorldConfig;
7
8use super::{
9 noise::{NoiseOptions, SeededNoise},
10 spline::SplineMap,
11};
12
13#[derive(PartialEq, Clone)]
14pub struct Biome {
15 pub name: String,
16 pub test_block: String,
17}
18
19impl Biome {
20 pub fn new(name: &str, test_block: &str) -> Self {
21 Self {
22 name: name.to_owned(),
23 test_block: test_block.to_owned(),
24 }
25 }
26}
27
28#[derive(Clone)]
30pub struct Terrain {
31 config: WorldConfig,
32 noise: SeededNoise,
33 biome_tree: KdTree<f64, Biome, Vec<f64>>,
34 pub layers: Vec<(TerrainLayer, f64)>,
35 pub noise_layers: Vec<(TerrainLayer, f64)>,
36}
37
38impl Terrain {
39 pub fn new(config: &WorldConfig) -> Self {
41 Self {
42 config: config.to_owned(),
43 noise: SeededNoise::new(config.seed, &config.terrain),
44 biome_tree: KdTree::new(2),
45 layers: vec![],
46 noise_layers: vec![],
47 }
48 }
49
50 pub fn add_layer(&mut self, layer: &TerrainLayer, weight: f64) -> &mut Self {
52 if self.biome_tree.size() > 0 {
60 panic!("Terrain layers must be added before biomes.");
61 }
62
63 let mut layer = layer.to_owned();
64 layer.set_seed(self.config.seed);
65 self.layers.push((layer, weight));
67
68 self.biome_tree = KdTree::new(self.layers.len());
69
70 self
71 }
72
73 pub fn add_noise_layer(&mut self, layer: &TerrainLayer, weight: f64) -> &mut Self {
75 let mut layer = layer.to_owned();
76 layer.set_seed(self.config.seed);
77 self.noise_layers.push((layer, weight));
78
79 self
80 }
81
82 pub fn add_biome(&mut self, point: &[f64], biome: Biome) -> &mut Self {
83 let point_vec = point.to_vec();
84 let point_vec = point_vec[..self.layers.len()]
85 .into_iter()
86 .enumerate()
87 .map(|(idx, val)| self.layers[idx].1 * val)
88 .collect::<Vec<f64>>()
89 .to_owned();
90
91 self.biome_tree.add(point_vec, biome).unwrap();
92 self
93 }
94
95 pub fn get_density_from_bias_offset(&self, bias: f64, offset: f64, vy: i32) -> f64 {
100 let max_height = self.config.max_height as f64;
101
102 -if self.layers.is_empty() {
104 0.0
105 } else {
106 bias * (vy as f64 - offset * max_height) / (offset * max_height)
107 }
108 }
109
110 pub fn get_bias_offset(&self, vx: i32, vy: i32, vz: i32) -> (f64, f64) {
113 let mut bias = 0.0;
114 let mut offset = 0.0;
115 let mut total_weight = 0.0;
116
117 self.layers.iter().for_each(|(layer, weight)| {
118 let value = if layer.options.dimension == 2 {
119 layer.noise.get2d(vx, vz)
120 } else {
121 layer.noise.get3d(vx, vy, vz)
122 };
123 bias += layer.sample_bias(value) * weight;
124 offset += layer.sample_offset(value) * weight;
125 total_weight += weight;
126 });
127
128 self.noise_layers.iter().for_each(|(layer, weight)| {
129 let value = if layer.options.dimension == 2 {
130 layer.noise.get2d(vx, vz)
131 } else {
132 layer.noise.get3d(vx, vy, vz)
133 };
134 bias += layer.sample_bias(value) * weight;
135 offset += layer.sample_offset(value) * weight;
136 total_weight += weight;
137 });
138
139 (bias / total_weight, offset / total_weight)
140 }
141
142 pub fn get_biome_at(&self, vx: i32, vy: i32, vz: i32) -> &Biome {
143 let values = self
144 .layers
145 .iter()
146 .map(|(layer, weight)| {
147 (if layer.options.dimension == 2 {
148 layer.noise.get2d(vx, vz)
149 } else {
150 layer.noise.get3d(vx, vy, vz)
151 }) * weight
152 })
153 .collect::<Vec<f64>>();
154
155 let result = self
156 .biome_tree
157 .nearest(&values, 1, &squared_euclidean)
158 .unwrap()[0];
159
160 result.1
161 }
162}
163
164#[derive(Clone, Serialize, Debug)]
168#[serde(rename_all = "camelCase")]
169pub struct TerrainLayer {
170 pub name: String,
171 #[serde(skip_serializing)]
172 pub noise: SeededNoise,
173 pub options: NoiseOptions,
174 pub height_bias_spline: SplineMap,
175 pub height_offset_spline: SplineMap,
176}
177
178impl TerrainLayer {
179 pub fn new(name: &str, options: &NoiseOptions) -> Self {
182 TerrainLayer {
183 name: name.to_owned(),
184 noise: SeededNoise::new(0, options),
185 options: options.to_owned(),
186 height_bias_spline: SplineMap::default(),
187 height_offset_spline: SplineMap::default(),
188 }
189 }
190
191 pub fn add_bias_point(mut self, point: [f64; 2]) -> Self {
193 self.height_bias_spline.add(point[0], point[1]);
194 self
195 }
196
197 pub fn add_bias_points(mut self, points: &[[f64; 2]]) -> Self {
199 points.into_iter().for_each(|point| {
200 self.height_bias_spline.add(point[0], point[1]);
201 });
202 self
203 }
204
205 pub fn add_offset_point(mut self, point: [f64; 2]) -> Self {
207 self.height_offset_spline.add(point[0], point[1]);
208 self
209 }
210
211 pub fn add_offset_points(mut self, points: &[[f64; 2]]) -> Self {
213 points.into_iter().for_each(|point| {
214 self.height_offset_spline.add(point[0], point[1]);
215 });
216 self
217 }
218
219 pub fn sample_bias(&self, x: f64) -> f64 {
221 self.height_bias_spline.sample(x)
222 }
223
224 pub fn sample_offset(&self, x: f64) -> f64 {
226 self.height_offset_spline.sample(x)
227 }
228
229 pub fn set_seed(&mut self, seed: u32) {
231 self.noise.set_seed(seed);
232 }
233
234 pub fn normalize(&mut self) {
236 self.height_bias_spline.rescale_values(-1.0, 1.0);
237 self.height_offset_spline.rescale_values(-1.0, 1.0);
238 }
239}