veloren/world/src/block/mod.rs

501 lines
15 KiB
Rust
Raw Normal View History

mod natural;
2019-06-09 10:24:18 +00:00
use crate::{
2019-07-09 23:51:54 +00:00
column::{ColumnGen, ColumnSample, StructureData},
2019-06-15 10:36:26 +00:00
util::{HashCache, RandomField, Sampler, SamplerMut},
World, CONFIG,
2019-06-09 10:24:18 +00:00
};
2019-06-15 10:36:26 +00:00
use common::{
terrain::{structure::StructureBlock, Block, Structure},
2019-08-04 19:54:08 +00:00
util::linear_to_srgb,
2019-06-15 10:36:26 +00:00
vol::{ReadVol, Vox},
};
use noise::NoiseFn;
2019-07-01 18:40:41 +00:00
use std::ops::{Add, Div, Mul, Neg};
2019-06-15 10:36:26 +00:00
use vek::*;
2019-06-09 10:24:18 +00:00
pub struct BlockGen<'a> {
world: &'a World,
2019-06-22 15:53:21 +00:00
column_cache: HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
2019-06-09 10:24:18 +00:00
column_gen: ColumnGen<'a>,
}
impl<'a> BlockGen<'a> {
pub fn new(world: &'a World, column_gen: ColumnGen<'a>) -> Self {
Self {
world,
2019-06-22 15:53:21 +00:00
column_cache: HashCache::with_capacity(64),
2019-06-09 10:24:18 +00:00
column_gen,
}
}
2019-06-22 15:53:21 +00:00
fn sample_column(
column_gen: &ColumnGen<'a>,
2019-06-23 19:43:02 +00:00
cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
wpos: Vec2<i32>,
2019-06-22 15:53:21 +00:00
) -> Option<ColumnSample<'a>> {
cache
2019-06-09 10:24:18 +00:00
.get(Vec2::from(wpos), |wpos| column_gen.get(wpos))
.clone()
}
2019-06-21 00:53:11 +00:00
2019-06-22 15:53:21 +00:00
fn get_cliff_height(
column_gen: &ColumnGen<'a>,
cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
wpos: Vec2<f32>,
close_cliffs: &[(Vec2<i32>, u32); 9],
cliff_hill: f32,
) -> f32 {
2019-06-23 19:43:02 +00:00
close_cliffs.iter().fold(
0.0f32,
|max_height, (cliff_pos, seed)| match Self::sample_column(
column_gen,
cache,
Vec2::from(*cliff_pos),
) {
Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => {
2019-06-23 19:43:02 +00:00
let cliff_pos3d = Vec3::from(*cliff_pos);
let height = RandomField::new(seed + 1).get(cliff_pos3d) % 48;
let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8;
max_height.max(
if cliff_pos.map(|e| e as f32).distance_squared(wpos)
< (radius * radius) as f32
{
cliff_sample.alt
+ height as f32 * (1.0 - cliff_sample.chaos)
+ cliff_hill
} else {
0.0
2019-06-23 19:43:02 +00:00
},
)
2019-06-21 00:53:11 +00:00
}
2019-06-23 19:43:02 +00:00
_ => max_height,
},
)
2019-06-21 00:53:11 +00:00
}
2019-06-09 10:24:18 +00:00
2019-07-04 23:14:55 +00:00
pub fn get_z_cache(&mut self, wpos: Vec2<i32>) -> Option<ZCache<'a>> {
let BlockGen {
world,
column_cache,
column_gen,
} = self;
2019-06-09 10:24:18 +00:00
2019-07-05 20:37:38 +00:00
// Main sample
2019-07-04 23:14:55 +00:00
let sample = Self::sample_column(column_gen, column_cache, wpos)?;
2019-07-05 20:37:38 +00:00
// Tree samples
let mut structure_samples = [None, None, None, None, None, None, None, None, None];
for i in 0..structure_samples.len() {
2019-07-09 23:51:54 +00:00
if let Some(st) = sample.close_structures[i] {
let st_sample = Self::sample_column(column_gen, column_cache, Vec2::from(st.pos));
structure_samples[i] = st_sample;
}
2019-07-04 23:14:55 +00:00
}
let mut structures = [None, None, None, None, None, None, None, None, None];
for i in 0..structures.len() {
2019-07-09 23:51:54 +00:00
if let (Some(st), Some(st_sample)) =
(sample.close_structures[i], structure_samples[i].clone())
{
let st_info = match st.meta {
None => natural::structure_gen(
column_gen,
column_cache,
i,
st.pos,
st.seed,
&structure_samples,
),
Some(meta) => Some(StructureInfo {
pos: Vec3::from(st.pos) + Vec3::unit_z() * st_sample.alt as i32,
seed: st.seed,
meta,
}),
};
2019-07-09 23:51:54 +00:00
if let Some(st_info) = st_info {
structures[i] = Some((st_info, st_sample));
}
}
}
Some(ZCache {
wpos,
sample,
structures,
})
2019-07-04 23:14:55 +00:00
}
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> {
2019-06-22 15:53:21 +00:00
let BlockGen {
world,
column_cache,
column_gen,
} = self;
2019-07-04 23:14:55 +00:00
let &ColumnSample {
2019-06-09 10:24:18 +00:00
alt,
chaos,
2019-06-19 19:58:56 +00:00
water_level,
2019-07-01 18:40:41 +00:00
//river,
2019-06-09 10:24:18 +00:00
surface_color,
2019-06-23 13:55:54 +00:00
sub_surface_color,
2019-07-01 18:40:41 +00:00
//tree_density,
//forest_kind,
2019-07-09 23:51:54 +00:00
//close_structures,
2019-06-09 10:24:18 +00:00
cave_xy,
cave_alt,
rock,
2019-07-01 18:40:41 +00:00
//cliffs,
2019-06-21 00:53:11 +00:00
cliff_hill,
close_cliffs,
2019-07-01 18:40:41 +00:00
//temp,
2019-06-18 21:22:31 +00:00
..
2019-07-04 23:14:55 +00:00
} = &z_cache?.sample;
let structures = &z_cache?.structures;
2019-06-09 10:24:18 +00:00
let wposf = wpos.map(|e| e as f64);
2019-06-23 19:43:02 +00:00
let (definitely_underground, height, water_height) =
if (wposf.z as f32) < alt - 64.0 * chaos {
// Shortcut warping
(true, alt, water_level)
} else {
2019-06-23 19:43:02 +00:00
// Apply warping
let warp = (world
.sim()
.gen_ctx
.warp_nz
.get((wposf.div(Vec3::new(150.0, 150.0, 150.0))).into_array())
as f32)
.mul((chaos - 0.1).max(0.0))
2019-07-08 19:08:08 +00:00
.mul(96.0);
2019-06-23 19:43:02 +00:00
let height = if (wposf.z as f32) < alt + warp - 10.0 {
// Shortcut cliffs
alt + warp
} else {
let turb = Vec2::new(
world
.sim()
.gen_ctx
.turb_x_nz
.get((wposf.div(48.0)).into_array()) as f32,
world
.sim()
.gen_ctx
.turb_y_nz
.get((wposf.div(48.0)).into_array()) as f32,
) * 12.0;
let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb;
let cliff_height = Self::get_cliff_height(
column_gen,
column_cache,
wpos_turb,
&close_cliffs,
cliff_hill,
);
(alt + warp).max(cliff_height)
};
2019-07-08 19:08:08 +00:00
(false, height, (water_level + warp).max(CONFIG.sea_level))
};
2019-06-21 00:53:11 +00:00
2019-06-09 10:24:18 +00:00
// Sample blocks
2019-08-04 19:54:08 +00:00
// let stone_col = Rgb::new(240, 230, 220);
let stone_col = Rgb::new(248, 243, 239);
2019-07-01 18:40:41 +00:00
// let dirt_col = Rgb::new(79, 67, 60);
2019-06-12 09:02:18 +00:00
2019-06-09 10:24:18 +00:00
let air = Block::empty();
2019-07-01 18:40:41 +00:00
// let stone = Block::new(2, stone_col);
// let surface_stone = Block::new(1, Rgb::new(200, 220, 255));
// let dirt = Block::new(1, dirt_col);
// let sand = Block::new(1, Rgb::new(180, 150, 50));
// let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
2019-08-04 19:54:08 +00:00
// let water = Block::new(1, Rgb::new(100, 150, 255));
let water = Block::new(1, Rgb::new(168, 202, 255));
2019-06-09 10:24:18 +00:00
let grass_depth = 1.5 + 2.0 * chaos;
2019-06-23 13:55:54 +00:00
let block = if (wposf.z as f32) < height - grass_depth {
let col = Lerp::lerp(
2019-08-04 19:54:08 +00:00
linear_to_srgb(sub_surface_color).map(|e| (e * 255.0) as u8),
2019-06-23 13:55:54 +00:00
stone_col,
(height - grass_depth - wposf.z as f32) * 0.15,
);
2019-06-12 09:02:18 +00:00
2019-06-09 10:24:18 +00:00
// Underground
2019-06-12 09:02:18 +00:00
if (wposf.z as f32) > alt - 32.0 * chaos {
Some(Block::new(1, col))
2019-06-10 10:50:48 +00:00
} else {
2019-06-12 09:02:18 +00:00
Some(Block::new(2, col))
2019-06-10 10:50:48 +00:00
}
2019-06-09 10:24:18 +00:00
} else if (wposf.z as f32) < height {
2019-06-12 10:34:44 +00:00
let col = Lerp::lerp(
2019-06-23 13:55:54 +00:00
sub_surface_color,
2019-06-12 10:34:44 +00:00
surface_color,
2019-06-23 19:43:02 +00:00
(wposf.z as f32 - (height - grass_depth))
.div(grass_depth)
.powf(0.5),
2019-06-12 10:34:44 +00:00
);
2019-06-09 10:24:18 +00:00
// Surface
2019-08-04 19:54:08 +00:00
Some(Block::new(
1,
linear_to_srgb(col).map(|e| (e * 255.0) as u8),
))
2019-06-19 19:58:56 +00:00
} else if (wposf.z as f32) < water_height {
2019-06-09 10:24:18 +00:00
// Ocean
Some(water)
} else {
None
};
// Caves
let block = block.and_then(|block| {
// Underground
let cave = cave_xy.powf(2.0)
* (wposf.z as f32 - cave_alt)
.div(40.0)
.powf(4.0)
.neg()
.add(1.0)
> 0.9993;
if cave {
None
} else {
Some(block)
}
});
// Rocks
let block = block.or_else(|| {
if (height + 2.5 - wposf.z as f32).div(7.5).abs().powf(2.0) < rock {
2019-06-22 15:53:21 +00:00
let field0 = RandomField::new(world.sim().seed + 0);
let field1 = RandomField::new(world.sim().seed + 1);
let field2 = RandomField::new(world.sim().seed + 2);
2019-06-18 11:55:25 +00:00
2019-06-19 14:55:26 +00:00
Some(Block::new(
1,
2019-06-19 14:55:26 +00:00
stone_col
- Rgb::new(
2019-08-04 19:54:08 +00:00
field0.get(wpos) as u8 % 16,
field1.get(wpos) as u8 % 16,
field2.get(wpos) as u8 % 16,
2019-06-19 14:55:26 +00:00
),
))
2019-06-09 10:24:18 +00:00
} else {
None
}
});
2019-08-03 20:44:51 +00:00
let block = structures
.iter()
.find_map(|st| {
let (st, st_sample) = st.as_ref()?;
st.get(wpos, st_sample)
})
.or(block)
.unwrap_or(Block::empty());
2019-06-09 10:24:18 +00:00
Some(block)
}
}
2019-07-04 23:14:55 +00:00
pub struct ZCache<'a> {
wpos: Vec2<i32>,
2019-07-04 23:14:55 +00:00
sample: ColumnSample<'a>,
structures: [Option<(StructureInfo, ColumnSample<'a>)>; 9],
}
impl<'a> ZCache<'a> {
pub fn get_z_limits(&self) -> (f32, f32) {
let cave_depth = if self.sample.cave_xy.abs() > 0.9 {
(self.sample.alt - self.sample.cave_alt + 8.0).max(0.0)
} else {
0.0
};
let min = self.sample.alt - (self.sample.chaos * 48.0 + cave_depth) - 4.0;
let cliff = if self.sample.near_cliffs { 48.0 } else { 0.0 };
let warp = self.sample.chaos * 48.0;
2019-08-03 20:44:51 +00:00
let (structure_min, structure_max) = self
.structures
.iter()
.filter_map(|st| st.as_ref())
.fold((0.0f32, 0.0f32), |(min, max), (st_info, st_sample)| {
2019-07-09 23:51:54 +00:00
let bounds = st_info.get_bounds();
let st_area = Aabr {
min: Vec2::from(bounds.min),
max: Vec2::from(bounds.max),
};
2019-07-09 23:51:54 +00:00
if st_area.contains_point(self.wpos - st_info.pos) {
2019-08-03 20:44:51 +00:00
(min.min(bounds.min.z as f32), max.max(bounds.max.z as f32))
} else {
2019-08-03 20:44:51 +00:00
(min, max)
}
2019-08-03 20:44:51 +00:00
});
2019-08-03 20:44:51 +00:00
let min = min + structure_min;
let max = (self.sample.alt + cliff + structure_max + warp + 8.0)
2019-07-08 19:08:08 +00:00
.max(self.sample.water_level)
.max(CONFIG.sea_level + 2.0);
(min, max)
}
}
2019-07-04 23:14:55 +00:00
impl<'a> SamplerMut for BlockGen<'a> {
type Index = Vec3<i32>;
type Sample = Option<Block>;
fn get(&mut self, wpos: Vec3<i32>) -> Option<Block> {
let z_cache = self.get_z_cache(wpos.into());
self.get_with_z_cache(wpos, z_cache.as_ref())
}
}
2019-07-09 23:51:54 +00:00
#[derive(Copy, Clone)]
pub enum StructureMeta {
Pyramid {
height: i32,
},
Volume {
units: (Vec2<i32>, Vec2<i32>),
volume: &'static Structure,
},
}
pub struct StructureInfo {
pos: Vec3<i32>,
seed: u32,
meta: StructureMeta,
}
impl StructureInfo {
fn get_bounds(&self) -> Aabb<i32> {
match self.meta {
StructureMeta::Pyramid { height } => {
let base = 40;
Aabb {
min: Vec3::new(-base - height, -base - height, -base),
max: Vec3::new(base + height, base + height, height),
}
}
StructureMeta::Volume { units, volume } => {
let bounds = volume.get_bounds();
(Aabb {
min: Vec3::from(units.0 * bounds.min.x + units.1 * bounds.min.y)
+ Vec3::unit_z() * bounds.min.z,
max: Vec3::from(units.0 * bounds.max.x + units.1 * bounds.max.y)
+ Vec3::unit_z() * bounds.max.z,
})
.made_valid()
}
}
}
fn get(&self, wpos: Vec3<i32>, sample: &ColumnSample) -> Option<Block> {
match self.meta {
StructureMeta::Pyramid { height } => {
if wpos.z - self.pos.z
< height
- Vec2::from(wpos - self.pos)
.map(|e: i32| (e.abs() / 2) * 2)
.reduce_max()
{
2019-08-04 19:54:08 +00:00
Some(Block::new(2, Rgb::new(219, 196, 160)))
2019-07-09 23:51:54 +00:00
} else {
None
}
}
StructureMeta::Volume { units, volume } => {
let rpos = wpos - self.pos;
let block_pos = Vec3::unit_z() * rpos.z
+ Vec3::from(units.0) * rpos.x
+ Vec3::from(units.1) * rpos.y;
volume
.get((block_pos * 128) / 128) // Scaling
2019-08-03 20:44:51 +00:00
.ok()
.and_then(|b| {
2019-07-09 23:51:54 +00:00
block_from_structure(*b, block_pos, self.pos.into(), self.seed, sample)
})
}
}
}
}
fn block_from_structure(
sblock: StructureBlock,
pos: Vec3<i32>,
structure_pos: Vec2<i32>,
structure_seed: u32,
_sample: &ColumnSample,
2019-08-03 20:44:51 +00:00
) -> Option<Block> {
2019-07-09 23:51:54 +00:00
let field = RandomField::new(structure_seed + 0);
let lerp = 0.5
+ ((field.get(Vec3::from(structure_pos)) % 256) as f32 / 256.0 - 0.5) * 0.65
+ ((field.get(Vec3::from(pos)) % 256) as f32 / 256.0 - 0.5) * 0.15;
match sblock {
2019-08-03 20:44:51 +00:00
StructureBlock::TemperateLeaves => Some(Block::new(
2019-07-09 23:51:54 +00:00
1,
2019-08-04 19:54:08 +00:00
Lerp::lerp(
Rgb::new(0.00, 143.0, 103.6),
Rgb::new(168.1, 195.5, 0.0),
lerp,
)
.map(|e| e as u8),
2019-08-03 20:44:51 +00:00
)),
StructureBlock::PineLeaves => Some(Block::new(
2019-07-09 23:51:54 +00:00
1,
2019-08-04 19:54:08 +00:00
Lerp::lerp(
Rgb::new(0.00, 133.2, 122.4),
Rgb::new(96.3, 168.1, 55.8),
lerp,
)
.map(|e| e as u8),
2019-08-03 20:44:51 +00:00
)),
StructureBlock::PalmLeaves => Some(Block::new(
2019-07-09 23:51:54 +00:00
1,
Lerp::lerp(
2019-08-04 19:54:08 +00:00
Rgb::new(68.6, 168.1, 96.3),
Rgb::new(128.0, 239.0, 0.00),
2019-07-09 23:51:54 +00:00
lerp,
)
.map(|e| e as u8),
2019-08-03 20:44:51 +00:00
)),
StructureBlock::Acacia => Some(Block::new(
2019-07-09 23:51:54 +00:00
1,
Lerp::lerp(
2019-08-04 19:54:08 +00:00
Rgb::new(103.6, 168.1, 55.8),
Rgb::new(143.0, 224.0, 88.2),
2019-07-09 23:51:54 +00:00
lerp,
)
.map(|e| e as u8),
2019-08-03 20:44:51 +00:00
)),
StructureBlock::Fruit => Some(Block::new(
2019-07-09 23:51:54 +00:00
1,
2019-08-04 19:54:08 +00:00
Lerp::lerp(
Rgb::new(255.0, 0.0, 0.0),
Rgb::new(229.1, 255.0, 42.4),
lerp,
)
.map(|e| e as u8),
2019-08-03 20:44:51 +00:00
)),
StructureBlock::Hollow => Some(Block::empty()),
StructureBlock::Block(block) => Some(block).filter(|block| !block.is_empty()),
2019-07-09 23:51:54 +00:00
}
}