2019-06-09 10:24:18 +00:00
|
|
|
mod tree;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
column::{ColumnGen, ColumnSample},
|
2019-06-15 10:36:26 +00:00
|
|
|
util::{HashCache, RandomField, Sampler, SamplerMut},
|
2019-07-01 18:40:41 +00:00
|
|
|
World,
|
2019-06-09 10:24:18 +00:00
|
|
|
};
|
2019-06-15 10:36:26 +00:00
|
|
|
use common::{
|
|
|
|
terrain::{structure::StructureBlock, Block},
|
|
|
|
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),
|
|
|
|
) {
|
2019-06-25 15:59:09 +00:00
|
|
|
Some(cliff_sample) if cliff_sample.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
|
2019-06-21 12:32:38 +00:00
|
|
|
} 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
|
2019-07-04 23:14:55 +00:00
|
|
|
let mut tree_samples = [None, None, None, None, None, None, None, None, None];
|
|
|
|
for i in 0..tree_samples.len() {
|
|
|
|
tree_samples[i] = Self::sample_column(
|
|
|
|
column_gen,
|
|
|
|
column_cache,
|
|
|
|
Vec2::from(sample.close_trees[i].0),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(ZCache {
|
|
|
|
sample,
|
|
|
|
tree_samples,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
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-06-09 10:24:18 +00:00
|
|
|
close_trees,
|
|
|
|
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 tree_samples = &z_cache?.tree_samples;
|
2019-06-09 10:24:18 +00:00
|
|
|
|
2019-07-05 20:37:38 +00:00
|
|
|
let wpos2d = Vec2::from(wpos);
|
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)
|
2019-06-21 12:32:38 +00:00
|
|
|
} 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))
|
|
|
|
.mul(115.0);
|
|
|
|
|
|
|
|
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)
|
|
|
|
};
|
|
|
|
|
|
|
|
(false, height, water_level + warp)
|
2019-06-21 12:32:38 +00:00
|
|
|
};
|
2019-06-21 00:53:11 +00:00
|
|
|
|
2019-06-09 10:24:18 +00:00
|
|
|
// Sample blocks
|
|
|
|
|
2019-06-12 09:02:18 +00:00
|
|
|
let stone_col = Rgb::new(200, 220, 255);
|
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-06-09 10:24:18 +00:00
|
|
|
let water = Block::new(1, Rgb::new(100, 150, 255));
|
|
|
|
|
2019-06-23 13:55:54 +00:00
|
|
|
let grass_depth = 2.0;
|
|
|
|
let block = if (wposf.z as f32) < height - grass_depth {
|
|
|
|
let col = Lerp::lerp(
|
|
|
|
sub_surface_color.map(|e| (e * 255.0) as u8),
|
|
|
|
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-06-12 10:34:44 +00:00
|
|
|
Some(Block::new(1, 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(
|
2019-06-21 12:32:38 +00:00
|
|
|
1,
|
2019-06-19 14:55:26 +00:00
|
|
|
stone_col
|
|
|
|
- Rgb::new(
|
|
|
|
field0.get(wpos) as u8 % 32,
|
|
|
|
field1.get(wpos) as u8 % 32,
|
|
|
|
field2.get(wpos) as u8 % 32,
|
|
|
|
),
|
|
|
|
))
|
2019-06-09 10:24:18 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2019-06-15 10:36:26 +00:00
|
|
|
fn block_from_structure(
|
|
|
|
sblock: StructureBlock,
|
|
|
|
pos: Vec3<i32>,
|
|
|
|
structure_pos: Vec2<i32>,
|
|
|
|
structure_seed: u32,
|
2019-07-01 18:40:41 +00:00
|
|
|
_sample: &ColumnSample,
|
2019-06-15 10:36:26 +00:00
|
|
|
) -> Block {
|
|
|
|
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.75
|
|
|
|
+ ((field.get(Vec3::from(pos)) % 256) as f32 / 256.0 - 0.5) * 0.2;
|
|
|
|
|
2019-06-12 20:22:16 +00:00
|
|
|
match sblock {
|
2019-06-15 10:36:26 +00:00
|
|
|
StructureBlock::TemperateLeaves => Block::new(
|
|
|
|
1,
|
|
|
|
Lerp::lerp(
|
|
|
|
Rgb::new(0.0, 80.0, 40.0),
|
|
|
|
Rgb::new(120.0, 255.0, 10.0),
|
|
|
|
lerp,
|
|
|
|
)
|
|
|
|
.map(|e| e as u8),
|
|
|
|
),
|
|
|
|
StructureBlock::PineLeaves => Block::new(
|
|
|
|
1,
|
|
|
|
Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp)
|
|
|
|
.map(|e| e as u8),
|
|
|
|
),
|
|
|
|
StructureBlock::PalmLeaves => Block::new(
|
|
|
|
1,
|
|
|
|
Lerp::lerp(
|
|
|
|
Rgb::new(30.0, 100.0, 30.0),
|
|
|
|
Rgb::new(120.0, 255.0, 0.0),
|
|
|
|
lerp,
|
|
|
|
)
|
|
|
|
.map(|e| e as u8),
|
|
|
|
),
|
2019-06-12 20:22:16 +00:00
|
|
|
StructureBlock::Block(block) => block,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 12:32:38 +00:00
|
|
|
let block = if definitely_underground {
|
|
|
|
block.unwrap_or(Block::empty())
|
|
|
|
} else {
|
|
|
|
match block {
|
|
|
|
Some(block) => block,
|
2019-07-04 23:14:55 +00:00
|
|
|
None => (&close_trees).iter().enumerate().fold(
|
|
|
|
air,
|
|
|
|
|block, (tree_idx, (tree_pos, tree_seed))| {
|
2019-06-23 19:43:02 +00:00
|
|
|
if !block.is_empty() {
|
|
|
|
block
|
|
|
|
} else {
|
2019-07-04 23:14:55 +00:00
|
|
|
match &tree_samples[tree_idx] {
|
2019-06-23 19:43:02 +00:00
|
|
|
Some(tree_sample)
|
2019-07-06 23:21:32 +00:00
|
|
|
if wpos2d.distance_squared(*tree_pos) < 28 * 28
|
|
|
|
&& tree_sample.tree_density
|
|
|
|
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2
|
2019-06-25 15:59:09 +00:00
|
|
|
&& tree_sample.alt > tree_sample.water_level
|
2019-07-06 23:21:32 +00:00
|
|
|
&& tree_sample.spawn_rate > 0.5 =>
|
2019-06-23 19:43:02 +00:00
|
|
|
{
|
|
|
|
let cliff_height = Self::get_cliff_height(
|
|
|
|
column_gen,
|
|
|
|
column_cache,
|
|
|
|
tree_pos.map(|e| e as f32),
|
|
|
|
&tree_sample.close_cliffs,
|
|
|
|
cliff_hill,
|
|
|
|
);
|
|
|
|
let height = tree_sample.alt.max(cliff_height);
|
|
|
|
let tree_pos3d =
|
|
|
|
Vec3::new(tree_pos.x, tree_pos.y, height as i32);
|
|
|
|
let rpos = wpos - tree_pos3d;
|
|
|
|
|
|
|
|
let trees = tree::kinds(tree_sample.forest_kind); // Choose tree kind
|
|
|
|
|
|
|
|
block.or(trees[*tree_seed as usize % trees.len()]
|
|
|
|
.get((rpos * 128) / 128) // Scaling
|
|
|
|
.map(|b| {
|
|
|
|
block_from_structure(
|
|
|
|
*b,
|
|
|
|
rpos,
|
|
|
|
*tree_pos,
|
|
|
|
*tree_seed,
|
|
|
|
&tree_sample,
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.unwrap_or(Block::empty()))
|
|
|
|
}
|
|
|
|
_ => block,
|
2019-06-21 12:32:38 +00:00
|
|
|
}
|
2019-06-09 10:24:18 +00:00
|
|
|
}
|
2019-07-04 23:14:55 +00:00
|
|
|
},
|
|
|
|
),
|
2019-06-21 12:32:38 +00:00
|
|
|
}
|
2019-06-09 10:24:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Some(block)
|
|
|
|
}
|
|
|
|
}
|
2019-07-04 23:14:55 +00:00
|
|
|
|
|
|
|
pub struct ZCache<'a> {
|
|
|
|
sample: ColumnSample<'a>,
|
|
|
|
tree_samples: [Option<ColumnSample<'a>>; 9],
|
|
|
|
}
|
|
|
|
|
|
|
|
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())
|
|
|
|
}
|
|
|
|
}
|