mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
284 lines
8.3 KiB
Rust
284 lines
8.3 KiB
Rust
pub mod biome;
|
|
pub mod block;
|
|
pub mod chonk;
|
|
pub mod map;
|
|
pub mod site;
|
|
pub mod sprite;
|
|
pub mod structure;
|
|
|
|
// Reexports
|
|
pub use self::{
|
|
biome::BiomeKind,
|
|
block::{Block, BlockKind},
|
|
map::MapSizeLg,
|
|
site::SitesKind,
|
|
sprite::SpriteKind,
|
|
structure::Structure,
|
|
};
|
|
use roots::find_roots_cubic;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{vol::RectVolSize, volumes::vol_grid_2d::VolGrid2d};
|
|
use vek::*;
|
|
|
|
// TerrainChunkSize
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TerrainChunkSize;
|
|
|
|
/// Base two logarithm of the number of blocks along either horizontal axis of
|
|
/// a chunk.
|
|
///
|
|
/// NOTE: (1 << CHUNK_SIZE_LG) is guaranteed to fit in a u32.
|
|
///
|
|
/// NOTE: A lot of code assumes that the two dimensions are equal, so we make it
|
|
/// explicit here.
|
|
///
|
|
/// NOTE: It is highly unlikely that a value greater than 5 will work, as many
|
|
/// frontend optimizations rely on being able to pack chunk horizontal
|
|
/// dimensions into 5 bits each.
|
|
pub const TERRAIN_CHUNK_BLOCKS_LG: u32 = 5;
|
|
|
|
impl RectVolSize for TerrainChunkSize {
|
|
const RECT_SIZE: Vec2<u32> = Vec2 {
|
|
x: (1 << TERRAIN_CHUNK_BLOCKS_LG),
|
|
y: (1 << TERRAIN_CHUNK_BLOCKS_LG),
|
|
};
|
|
}
|
|
|
|
// TerrainChunkMeta
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TerrainChunkMeta {
|
|
name: Option<String>,
|
|
biome: BiomeKind,
|
|
|
|
chaos: f32,
|
|
alt: f32,
|
|
//basement: f32,
|
|
//water_alt: f32,
|
|
//downhill: Option<Vec2<i32>>,
|
|
//flux: f32,
|
|
temp: f32,
|
|
humidity: f32,
|
|
rockiness: f32,
|
|
//is_cliffs: bool,
|
|
//near_cliffs: bool,
|
|
tree_density: f32,
|
|
//forest_kind: ForestKind,
|
|
//spawn_rate: f32,
|
|
//river: RiverData,
|
|
//warp_factor: f32,
|
|
surface_veg: f32,
|
|
cave_alt: f32,
|
|
/*place: Option<Id<Place>>, */
|
|
|
|
/*path: (Way, Path),*/
|
|
/* contains_waypoint: bool, */
|
|
}
|
|
|
|
impl TerrainChunkMeta {
|
|
pub fn new(
|
|
name: Option<String>,
|
|
biome: BiomeKind,
|
|
chaos: f32,
|
|
alt: f32,
|
|
temp: f32,
|
|
humidity: f32,
|
|
rockiness: f32,
|
|
tree_density: f32,
|
|
surface_veg: f32,
|
|
cave_alt: f32,
|
|
) -> Self {
|
|
Self {
|
|
name,
|
|
biome,
|
|
chaos,
|
|
alt,
|
|
temp,
|
|
humidity,
|
|
rockiness,
|
|
tree_density,
|
|
surface_veg,
|
|
cave_alt,
|
|
}
|
|
}
|
|
|
|
pub fn void() -> Self {
|
|
Self {
|
|
name: None,
|
|
biome: BiomeKind::Void,
|
|
chaos: 0.0,
|
|
alt: 0.0,
|
|
temp: 0.0,
|
|
humidity: 0.0,
|
|
rockiness: 0.0,
|
|
tree_density: 0.0,
|
|
surface_veg: 0.0,
|
|
cave_alt: 0.0,
|
|
}
|
|
}
|
|
|
|
pub fn name(&self) -> &str { self.name.as_deref().unwrap_or("Wilderness") }
|
|
|
|
pub fn biome(&self) -> BiomeKind { self.biome }
|
|
|
|
pub fn chaos(&self) -> f32 { self.chaos }
|
|
|
|
pub fn alt(&self) -> f32 { self.alt }
|
|
|
|
pub fn temp(&self) -> f32 { self.temp }
|
|
|
|
pub fn humidity(&self) -> f32 { self.humidity }
|
|
|
|
pub fn rockiness(&self) -> f32 { self.rockiness }
|
|
|
|
pub fn tree_density(&self) -> f32 { self.tree_density }
|
|
|
|
pub fn surface_veg(&self) -> f32 { self.surface_veg }
|
|
|
|
pub fn cave_alt(&self) -> f32 { self.cave_alt }
|
|
}
|
|
|
|
// Terrain type aliases
|
|
|
|
pub type TerrainChunk = chonk::Chonk<Block, TerrainChunkSize, TerrainChunkMeta>;
|
|
pub type TerrainGrid = VolGrid2d<TerrainChunk>;
|
|
|
|
// Terrain helper functions used across multiple crates.
|
|
|
|
/// Computes the position Vec2 of a SimChunk from an index, where the index was
|
|
/// generated by uniform_noise.
|
|
///
|
|
/// NOTE: Dimensions obey constraints on [map::MapConfig::map_size_lg].
|
|
#[inline(always)]
|
|
pub fn uniform_idx_as_vec2(map_size_lg: MapSizeLg, idx: usize) -> Vec2<i32> {
|
|
let x_mask = (1 << map_size_lg.vec().x) - 1;
|
|
Vec2::new((idx & x_mask) as i32, (idx >> map_size_lg.vec().x) as i32)
|
|
}
|
|
|
|
/// Computes the index of a Vec2 of a SimChunk from a position, where the index
|
|
/// is generated by uniform_noise. NOTE: Both components of idx should be
|
|
/// in-bounds!
|
|
#[inline(always)]
|
|
pub fn vec2_as_uniform_idx(map_size_lg: MapSizeLg, idx: Vec2<i32>) -> usize {
|
|
((idx.y as usize) << map_size_lg.vec().x) | idx.x as usize
|
|
}
|
|
|
|
// NOTE: want to keep this such that the chunk index is in ascending order!
|
|
pub const NEIGHBOR_DELTA: [(i32, i32); 8] = [
|
|
(-1, -1),
|
|
(0, -1),
|
|
(1, -1),
|
|
(-1, 0),
|
|
(1, 0),
|
|
(-1, 1),
|
|
(0, 1),
|
|
(1, 1),
|
|
];
|
|
|
|
/// Iterate through all cells adjacent to a chunk.
|
|
#[inline(always)]
|
|
pub fn neighbors(map_size_lg: MapSizeLg, posi: usize) -> impl Clone + Iterator<Item = usize> {
|
|
let pos = uniform_idx_as_vec2(map_size_lg, posi);
|
|
let world_size = map_size_lg.chunks();
|
|
NEIGHBOR_DELTA
|
|
.iter()
|
|
.map(move |&(x, y)| Vec2::new(pos.x + x, pos.y + y))
|
|
.filter(move |pos| {
|
|
pos.x >= 0 && pos.y >= 0 && pos.x < world_size.x as i32 && pos.y < world_size.y as i32
|
|
})
|
|
.map(move |pos| vec2_as_uniform_idx(map_size_lg, pos))
|
|
}
|
|
|
|
pub fn river_spline_coeffs(
|
|
// _sim: &WorldSim,
|
|
chunk_pos: Vec2<f64>,
|
|
spline_derivative: Vec2<f32>,
|
|
downhill_pos: Vec2<f64>,
|
|
) -> Vec3<Vec2<f64>> {
|
|
let dxy = downhill_pos - chunk_pos;
|
|
// Since all splines have been precomputed, we don't have to do that much work
|
|
// to evaluate the spline. The spline is just ax^2 + bx + c = 0, where
|
|
//
|
|
// a = dxy - chunk.river.spline_derivative
|
|
// b = chunk.river.spline_derivative
|
|
// c = chunk_pos
|
|
let spline_derivative = spline_derivative.map(|e| e as f64);
|
|
Vec3::new(dxy - spline_derivative, spline_derivative, chunk_pos)
|
|
}
|
|
|
|
/// Find the nearest point from a quadratic spline to this point (in terms of t,
|
|
/// the "distance along the curve" by which our spline is parameterized). Note
|
|
/// that if t < 0.0 or t >= 1.0, we probably shouldn't be considered "on the
|
|
/// curve"... hopefully this works out okay and gives us what we want (a
|
|
/// river that extends outwards tangent to a quadratic curve, with width
|
|
/// configured by distance along the line).
|
|
#[allow(clippy::let_and_return)] // TODO: Pending review in #587
|
|
#[allow(clippy::many_single_char_names)]
|
|
pub fn quadratic_nearest_point(
|
|
spline: &Vec3<Vec2<f64>>,
|
|
point: Vec2<f64>,
|
|
) -> Option<(f64, Vec2<f64>, f64)> {
|
|
let a = spline.z.x;
|
|
let b = spline.y.x;
|
|
let c = spline.x.x;
|
|
let d = point.x;
|
|
let e = spline.z.y;
|
|
let f = spline.y.y;
|
|
let g = spline.x.y;
|
|
let h = point.y;
|
|
// This is equivalent to solving the following cubic equation (derivation is a
|
|
// bit annoying):
|
|
//
|
|
// A = 2(c^2 + g^2)
|
|
// B = 3(b * c + g * f)
|
|
// C = ((a - d) * 2 * c + b^2 + (e - h) * 2 * g + f^2)
|
|
// D = ((a - d) * b + (e - h) * f)
|
|
//
|
|
// Ax³ + Bx² + Cx + D = 0
|
|
//
|
|
// Once solved, this yield up to three possible values for t (reflecting minimal
|
|
// and maximal values). We should choose the minimal such real value with t
|
|
// between 0.0 and 1.0. If we fall outside those bounds, then we are
|
|
// outside the spline and return None.
|
|
let a_ = (c * c + g * g) * 2.0;
|
|
let b_ = (b * c + g * f) * 3.0;
|
|
let a_d = a - d;
|
|
let e_h = e - h;
|
|
let c_ = a_d * c * 2.0 + b * b + e_h * g * 2.0 + f * f;
|
|
let d_ = a_d * b + e_h * f;
|
|
let roots = find_roots_cubic(a_, b_, c_, d_);
|
|
let roots = roots.as_ref();
|
|
|
|
let min_root = roots
|
|
.iter()
|
|
.copied()
|
|
.filter_map(|root| {
|
|
let river_point = spline.x * root * root + spline.y * root + spline.z;
|
|
let river_zero = spline.z;
|
|
let river_one = spline.x + spline.y + spline.z;
|
|
if root > 0.0 && root < 1.0 {
|
|
Some((root, river_point))
|
|
} else if river_point.distance_squared(river_zero) < 0.5 {
|
|
Some((root, /*river_point*/ river_zero))
|
|
} else if river_point.distance_squared(river_one) < 0.5 {
|
|
Some((root, /*river_point*/ river_one))
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.map(|(root, river_point)| {
|
|
let river_distance = river_point.distance_squared(point);
|
|
(root, river_point, river_distance)
|
|
})
|
|
// In the (unlikely?) case that distances are equal, prefer the earliest point along the
|
|
// river.
|
|
.min_by(|&(ap, _, a), &(bp, _, b)| {
|
|
(a, ap < 0.0 || ap > 1.0, ap)
|
|
.partial_cmp(&(b, bp < 0.0 || bp > 1.0, bp))
|
|
.unwrap()
|
|
});
|
|
min_root
|
|
}
|