mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/worldgen' into 'master'
Worldgen overhaul See merge request veloren/veloren!321
This commit is contained in:
commit
1506898baa
BIN
assets/world/structure/natural/ribcage-large.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structure/natural/ribcage-large.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/structure/natural/ribcage-small.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structure/natural/ribcage-small.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/structure/natural/skull-large.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structure/natural/skull-large.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/structure/natural/tower-ruin.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structure/natural/tower-ruin.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/structure/natural/witch-hut.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/structure/natural/witch-hut.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/fruit/1.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/fruit/1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/fruit/2.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/fruit/2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/fruit/3.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/fruit/3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/fruit/4.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/fruit/4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/fruit/5.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/fruit/5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/tree/fruit/6.vox
(Stored with Git LFS)
Normal file
BIN
assets/world/tree/fruit/6.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -219,7 +219,7 @@ impl<'a> System<'a> for Sys {
|
||||
pos.0 += pos_delta / increments;
|
||||
|
||||
// While the player is colliding with the terrain...
|
||||
while collision_with(pos.0, near_iter.clone()) && attempts < 32 {
|
||||
while collision_with(pos.0, near_iter.clone()) && attempts < 16 {
|
||||
// Calculate the player's AABB
|
||||
let player_aabb = Aabb {
|
||||
min: pos.0 + Vec3::new(-player_rad, -player_rad, 0.0),
|
||||
@ -279,9 +279,11 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// When the resolution direction is non-vertical, we must be colliding with a wall
|
||||
// If the space above is free...
|
||||
if !collision_with(pos.0 + Vec3::unit_z() * 1.1, near_iter.clone())
|
||||
if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), near_iter.clone())
|
||||
// ...and we're being pushed out horizontally...
|
||||
&& resolve_dir.z == 0.0
|
||||
// ...and the vertical resolution direction is sufficiently great...
|
||||
&& -dir.z > 0.1
|
||||
// ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)...
|
||||
&& (vel.0.z <= 0.0 || terrain
|
||||
.get((pos.0 - Vec3::unit_z()).map(|e| e.floor() as i32))
|
||||
@ -294,16 +296,19 @@ impl<'a> System<'a> for Sys {
|
||||
)
|
||||
{
|
||||
// ...block-hop!
|
||||
pos.0.z = (pos.0.z + 1.0).ceil();
|
||||
pos.0.z = (pos.0.z + 0.1).ceil();
|
||||
on_ground = true;
|
||||
break;
|
||||
} else {
|
||||
// Correct the velocity
|
||||
vel.0 = vel.0.map2(
|
||||
resolve_dir,
|
||||
|e, d| if d * e.signum() < 0.0 { 0.0 } else { e },
|
||||
);
|
||||
}
|
||||
|
||||
// Resolve the collision normally
|
||||
pos.0 += resolve_dir;
|
||||
vel.0 = vel
|
||||
.0
|
||||
.map2(resolve_dir, |e, d| if d == 0.0 { e } else { 0.0 });
|
||||
}
|
||||
|
||||
attempts += 1;
|
||||
}
|
||||
@ -311,10 +316,10 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
if on_ground {
|
||||
let _ = on_grounds.insert(entity, OnGround);
|
||||
// If we're not on the ground but the space below us is free, then "snap" to the ground
|
||||
// If the space below us is free, then "snap" to the ground
|
||||
} else if collision_with(pos.0 - Vec3::unit_z() * 1.05, near_iter.clone())
|
||||
&& vel.0.z < 0.0
|
||||
&& vel.0.z > -1.0
|
||||
&& vel.0.z > -1.5
|
||||
&& was_on_ground
|
||||
{
|
||||
pos.0.z = (pos.0.z - 0.05).floor();
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::Block;
|
||||
use crate::{
|
||||
assets::{self, Asset},
|
||||
vol::{BaseVol, ReadVol, Vox, WriteVol},
|
||||
vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol},
|
||||
volumes::dyna::{Dyna, DynaErr},
|
||||
};
|
||||
use dot_vox::DotVoxData;
|
||||
@ -12,7 +12,9 @@ use vek::*;
|
||||
pub enum StructureBlock {
|
||||
TemperateLeaves,
|
||||
PineLeaves,
|
||||
Acacia,
|
||||
PalmLeaves,
|
||||
Fruit,
|
||||
Block(Block),
|
||||
}
|
||||
|
||||
@ -44,6 +46,13 @@ impl Structure {
|
||||
self.center = center;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_bounds(&self) -> Aabb<i32> {
|
||||
Aabb {
|
||||
min: -self.center,
|
||||
max: self.vol.get_size().map(|e| e as i32) - self.center,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BaseVol for Structure {
|
||||
@ -83,6 +92,8 @@ impl Asset for Structure {
|
||||
0 => StructureBlock::TemperateLeaves,
|
||||
1 => StructureBlock::PineLeaves,
|
||||
2 => StructureBlock::PalmLeaves,
|
||||
4 => StructureBlock::Acacia,
|
||||
7 => StructureBlock::Fruit,
|
||||
index => {
|
||||
let color = palette
|
||||
.get(index as usize)
|
||||
|
@ -84,7 +84,7 @@ impl Server {
|
||||
let mut state = State::default();
|
||||
state
|
||||
.ecs_mut()
|
||||
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 305.0)));
|
||||
.add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 380.0)));
|
||||
|
||||
let this = Self {
|
||||
state,
|
||||
|
@ -1,17 +1,17 @@
|
||||
const float PI = 3.141592;
|
||||
|
||||
const vec3 SKY_DAY_TOP = vec3(0.2, 0.3, 0.9);
|
||||
const vec3 SKY_DAY_MID = vec3(0.15, 0.2, 0.8);
|
||||
const vec3 SKY_DAY_TOP = vec3(0.35, 0.45, 0.9);
|
||||
const vec3 SKY_DAY_MID = vec3(0.25, 0.35, 0.8);
|
||||
const vec3 SKY_DAY_BOT = vec3(0.02, 0.1, 0.3);
|
||||
const vec3 DAY_LIGHT = vec3(0.5, 0.5, 1.0);
|
||||
|
||||
const vec3 SKY_DUSK_TOP = vec3(0.1, 0.15, 0.3);
|
||||
const vec3 SKY_DUSK_MID = vec3(0.9, 0.3, 0.2);
|
||||
const vec3 SKY_DUSK_MID = vec3(0.8, 0.25, 0.2);
|
||||
const vec3 SKY_DUSK_BOT = vec3(0.01, 0.05, 0.15);
|
||||
const vec3 DUSK_LIGHT = vec3(0.9, 0.2, 0.1);
|
||||
const vec3 DUSK_LIGHT = vec3(0.9, 0.4, 0.3);
|
||||
|
||||
const vec3 SKY_NIGHT_TOP = vec3(0.002, 0.002, 0.005);
|
||||
const vec3 SKY_NIGHT_MID = vec3(0.002, 0.01, 0.03);
|
||||
const vec3 SKY_NIGHT_TOP = vec3(0.001, 0.001, 0.0025);
|
||||
const vec3 SKY_NIGHT_MID = vec3(0.001, 0.005, 0.02);
|
||||
const vec3 SKY_NIGHT_BOT = vec3(0.002, 0.002, 0.005);
|
||||
const vec3 NIGHT_LIGHT = vec3(0.002, 0.01, 0.03);
|
||||
|
||||
@ -31,7 +31,7 @@ float get_sun_brightness(vec3 sun_dir) {
|
||||
const float PERSISTENT_AMBIANCE = 0.008;
|
||||
|
||||
vec3 get_sun_diffuse(vec3 norm, float time_of_day) {
|
||||
const float SUN_AMBIANCE = 0.1;
|
||||
const float SUN_AMBIANCE = 0.075;
|
||||
|
||||
vec3 sun_dir = get_sun_dir(time_of_day);
|
||||
|
||||
|
@ -167,8 +167,7 @@ void main() {
|
||||
//vec4 fxaa_color = texture(src_color, uv);
|
||||
|
||||
vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a);
|
||||
hsva_color.y *= 1.3;
|
||||
hsva_color.x -= 0.015;
|
||||
hsva_color.y *= 1.45;
|
||||
hsva_color.z *= 0.85;
|
||||
//hsva_color.z = 1.0 - 1.0 / (1.0 * hsva_color.z + 1.0);
|
||||
vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a);
|
||||
|
@ -50,7 +50,7 @@ impl Vertex {
|
||||
pos_norm: 0
|
||||
| ((pos.x as u32) & 0x00FF) << 0
|
||||
| ((pos.y as u32) & 0x00FF) << 8
|
||||
| ((pos.z as u32) & 0x1FFF) << 16
|
||||
| ((pos.z.max(0.0).min((1 << 13) as f32) as u32) & 0x1FFF) << 16
|
||||
| ((norm_bits as u32) & 0x7) << 29,
|
||||
col_light: 0
|
||||
| ((col.r.mul(200.0) as u32) & 0xFF) << 8
|
||||
|
@ -1,6 +1,7 @@
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ForestKind {
|
||||
Palm,
|
||||
Savannah,
|
||||
Oak,
|
||||
Pine,
|
||||
SnowPine,
|
||||
|
@ -1,12 +1,12 @@
|
||||
mod tree;
|
||||
mod natural;
|
||||
|
||||
use crate::{
|
||||
column::{ColumnGen, ColumnSample},
|
||||
column::{ColumnGen, ColumnSample, StructureData},
|
||||
util::{HashCache, RandomField, Sampler, SamplerMut},
|
||||
World,
|
||||
World, CONFIG,
|
||||
};
|
||||
use common::{
|
||||
terrain::{structure::StructureBlock, Block},
|
||||
terrain::{structure::StructureBlock, Block, Structure},
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
use noise::NoiseFn;
|
||||
@ -52,7 +52,7 @@ impl<'a> BlockGen<'a> {
|
||||
cache,
|
||||
Vec2::from(*cliff_pos),
|
||||
) {
|
||||
Some(cliff_sample) if cliff_sample.cliffs && cliff_sample.spawn_rate > 0.5 => {
|
||||
Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => {
|
||||
let cliff_pos3d = Vec3::from(*cliff_pos);
|
||||
|
||||
let height = RandomField::new(seed + 1).get(cliff_pos3d) % 48;
|
||||
@ -86,18 +86,45 @@ impl<'a> BlockGen<'a> {
|
||||
let sample = Self::sample_column(column_gen, column_cache, wpos)?;
|
||||
|
||||
// Tree samples
|
||||
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(
|
||||
let mut structure_samples = [None, None, None, None, None, None, None, None, None];
|
||||
for i in 0..structure_samples.len() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
let mut structures = [None, None, None, None, None, None, None, None, None];
|
||||
for i in 0..structures.len() {
|
||||
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,
|
||||
Vec2::from(sample.close_trees[i].0),
|
||||
);
|
||||
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,
|
||||
}),
|
||||
};
|
||||
|
||||
if let Some(st_info) = st_info {
|
||||
structures[i] = Some((st_info, st_sample));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(ZCache {
|
||||
wpos,
|
||||
sample,
|
||||
tree_samples,
|
||||
structures,
|
||||
})
|
||||
}
|
||||
|
||||
@ -117,7 +144,7 @@ impl<'a> BlockGen<'a> {
|
||||
sub_surface_color,
|
||||
//tree_density,
|
||||
//forest_kind,
|
||||
close_trees,
|
||||
//close_structures,
|
||||
cave_xy,
|
||||
cave_alt,
|
||||
rock,
|
||||
@ -128,9 +155,8 @@ impl<'a> BlockGen<'a> {
|
||||
..
|
||||
} = &z_cache?.sample;
|
||||
|
||||
let tree_samples = &z_cache?.tree_samples;
|
||||
let structures = &z_cache?.structures;
|
||||
|
||||
let wpos2d = Vec2::from(wpos);
|
||||
let wposf = wpos.map(|e| e as f64);
|
||||
|
||||
let (definitely_underground, height, water_height) =
|
||||
@ -146,7 +172,7 @@ impl<'a> BlockGen<'a> {
|
||||
.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);
|
||||
.mul(96.0);
|
||||
|
||||
let height = if (wposf.z as f32) < alt + warp - 10.0 {
|
||||
// Shortcut cliffs
|
||||
@ -177,12 +203,12 @@ impl<'a> BlockGen<'a> {
|
||||
(alt + warp).max(cliff_height)
|
||||
};
|
||||
|
||||
(false, height, water_level + warp)
|
||||
(false, height, (water_level + warp).max(CONFIG.sea_level))
|
||||
};
|
||||
|
||||
// Sample blocks
|
||||
|
||||
let stone_col = Rgb::new(200, 220, 255);
|
||||
let stone_col = Rgb::new(240, 230, 220);
|
||||
// let dirt_col = Rgb::new(79, 67, 60);
|
||||
|
||||
let air = Block::empty();
|
||||
@ -193,7 +219,7 @@ impl<'a> BlockGen<'a> {
|
||||
// let warm_stone = Block::new(1, Rgb::new(165, 165, 130));
|
||||
let water = Block::new(1, Rgb::new(100, 150, 255));
|
||||
|
||||
let grass_depth = 2.0;
|
||||
let grass_depth = 1.5 + 2.0 * chaos;
|
||||
let block = if (wposf.z as f32) < height - grass_depth {
|
||||
let col = Lerp::lerp(
|
||||
sub_surface_color.map(|e| (e * 255.0) as u8),
|
||||
@ -263,99 +289,39 @@ impl<'a> BlockGen<'a> {
|
||||
}
|
||||
});
|
||||
|
||||
fn block_from_structure(
|
||||
sblock: StructureBlock,
|
||||
pos: Vec3<i32>,
|
||||
structure_pos: Vec2<i32>,
|
||||
structure_seed: u32,
|
||||
_sample: &ColumnSample,
|
||||
) -> 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;
|
||||
|
||||
match sblock {
|
||||
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),
|
||||
),
|
||||
StructureBlock::Block(block) => block,
|
||||
}
|
||||
}
|
||||
|
||||
let block = if definitely_underground {
|
||||
block.unwrap_or(Block::empty())
|
||||
} else {
|
||||
match block {
|
||||
Some(block) => block,
|
||||
None => (&close_trees).iter().enumerate().fold(
|
||||
air,
|
||||
|block, (tree_idx, (tree_pos, tree_seed))| {
|
||||
if !block.is_empty() {
|
||||
block
|
||||
} else {
|
||||
match &tree_samples[tree_idx] {
|
||||
Some(tree_sample)
|
||||
if wpos2d.distance_squared(*tree_pos) < 28 * 28
|
||||
&& tree_sample.tree_density
|
||||
> 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2
|
||||
&& tree_sample.alt > tree_sample.water_level
|
||||
&& tree_sample.spawn_rate > 0.5 =>
|
||||
{
|
||||
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;
|
||||
.or_else(|| {
|
||||
structures.iter().find_map(|st| {
|
||||
let (st, st_sample) = st.as_ref()?;
|
||||
|
||||
let trees = tree::kinds(tree_sample.forest_kind); // Choose tree kind
|
||||
st.get(wpos, st_sample)
|
||||
|
||||
block.or(trees[*tree_seed as usize % trees.len()]
|
||||
.get((rpos * 128) / 128) // Scaling
|
||||
/*
|
||||
let rpos = wpos - st.pos;
|
||||
let block_pos = Vec3::unit_z() * rpos.z
|
||||
+ Vec3::from(st.units.0) * rpos.x
|
||||
+ Vec3::from(st.units.1) * rpos.y;
|
||||
|
||||
st.volume
|
||||
.get((block_pos * 128) / 128) // Scaling
|
||||
.map(|b| {
|
||||
block_from_structure(
|
||||
*b,
|
||||
rpos,
|
||||
*tree_pos,
|
||||
*tree_seed,
|
||||
&tree_sample,
|
||||
block_pos,
|
||||
st.pos.into(),
|
||||
st.seed,
|
||||
st_sample,
|
||||
)
|
||||
})
|
||||
.unwrap_or(Block::empty()))
|
||||
}
|
||||
_ => block,
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
}
|
||||
.ok()
|
||||
.filter(|block| !block.is_empty())
|
||||
*/
|
||||
})
|
||||
})
|
||||
.unwrap_or(Block::empty())
|
||||
};
|
||||
|
||||
Some(block)
|
||||
@ -363,8 +329,46 @@ impl<'a> BlockGen<'a> {
|
||||
}
|
||||
|
||||
pub struct ZCache<'a> {
|
||||
wpos: Vec2<i32>,
|
||||
sample: ColumnSample<'a>,
|
||||
tree_samples: [Option<ColumnSample<'a>>; 9],
|
||||
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;
|
||||
let structure = self.structures.iter().filter_map(|st| st.as_ref()).fold(
|
||||
0,
|
||||
|a, (st_info, st_sample)| {
|
||||
let bounds = st_info.get_bounds();
|
||||
let st_area = Aabr {
|
||||
min: Vec2::from(bounds.min),
|
||||
max: Vec2::from(bounds.max),
|
||||
};
|
||||
|
||||
if st_area.contains_point(self.wpos - st_info.pos) {
|
||||
a.max(bounds.max.z)
|
||||
} else {
|
||||
a
|
||||
}
|
||||
},
|
||||
) as f32;
|
||||
|
||||
let max = (self.sample.alt + cliff + structure + warp + 8.0)
|
||||
.max(self.sample.water_level)
|
||||
.max(CONFIG.sea_level + 2.0);
|
||||
|
||||
(min, max)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SamplerMut for BlockGen<'a> {
|
||||
@ -376,3 +380,127 @@ impl<'a> SamplerMut for BlockGen<'a> {
|
||||
self.get_with_z_cache(wpos, z_cache.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[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()
|
||||
{
|
||||
Some(Block::new(2, Rgb::new(180, 140, 90)))
|
||||
} 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
|
||||
.map(|b| {
|
||||
block_from_structure(*b, block_pos, self.pos.into(), self.seed, sample)
|
||||
})
|
||||
.ok()
|
||||
.filter(|block| !block.is_empty())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn block_from_structure(
|
||||
sblock: StructureBlock,
|
||||
pos: Vec3<i32>,
|
||||
structure_pos: Vec2<i32>,
|
||||
structure_seed: u32,
|
||||
_sample: &ColumnSample,
|
||||
) -> 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.65
|
||||
+ ((field.get(Vec3::from(pos)) % 256) as f32 / 256.0 - 0.5) * 0.15;
|
||||
|
||||
match sblock {
|
||||
StructureBlock::TemperateLeaves => Block::new(
|
||||
1,
|
||||
Lerp::lerp(Rgb::new(0.0, 70.0, 35.0), Rgb::new(100.0, 140.0, 0.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(15.0, 100.0, 30.0),
|
||||
Rgb::new(55.0, 220.0, 0.0),
|
||||
lerp,
|
||||
)
|
||||
.map(|e| e as u8),
|
||||
),
|
||||
StructureBlock::Acacia => Block::new(
|
||||
1,
|
||||
Lerp::lerp(
|
||||
Rgb::new(35.0, 100.0, 10.0),
|
||||
Rgb::new(70.0, 190.0, 25.0),
|
||||
lerp,
|
||||
)
|
||||
.map(|e| e as u8),
|
||||
),
|
||||
StructureBlock::Fruit => Block::new(
|
||||
1,
|
||||
Lerp::lerp(Rgb::new(255.0, 0.0, 0.0), Rgb::new(200.0, 255.0, 6.0), lerp)
|
||||
.map(|e| e as u8),
|
||||
),
|
||||
StructureBlock::Block(block) => block,
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,90 @@
|
||||
use crate::all::ForestKind;
|
||||
use super::{BlockGen, StructureInfo, StructureMeta, ZCache};
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
column::{ColumnGen, ColumnSample},
|
||||
util::{HashCache, RandomPerm, Sampler},
|
||||
CONFIG,
|
||||
};
|
||||
use common::{assets, terrain::Structure};
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Arc;
|
||||
use vek::*;
|
||||
|
||||
pub fn kinds(forest_kind: ForestKind) -> &'static [Arc<Structure>] {
|
||||
match forest_kind {
|
||||
static VOLUME_RAND: RandomPerm = RandomPerm::new(0xDB21C052);
|
||||
static UNIT_RAND: RandomPerm = RandomPerm::new(0x700F4EC7);
|
||||
static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F);
|
||||
|
||||
pub fn structure_gen<'a>(
|
||||
column_gen: &ColumnGen<'a>,
|
||||
column_cache: &mut HashCache<Vec2<i32>, Option<ColumnSample<'a>>>,
|
||||
idx: usize,
|
||||
st_pos: Vec2<i32>,
|
||||
st_seed: u32,
|
||||
structure_samples: &[Option<ColumnSample>; 9],
|
||||
) -> Option<StructureInfo> {
|
||||
let st_sample = &structure_samples[idx].as_ref()?;
|
||||
|
||||
// Assuming it's a tree... figure out when it SHOULDN'T spawn
|
||||
if st_sample.tree_density < 0.5 + (st_seed as f32 / 1000.0).fract() * 0.2
|
||||
|| st_sample.alt < st_sample.water_level
|
||||
|| st_sample.spawn_rate < 0.5
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let cliff_height = BlockGen::get_cliff_height(
|
||||
column_gen,
|
||||
column_cache,
|
||||
st_pos.map(|e| e as f32),
|
||||
&st_sample.close_cliffs,
|
||||
st_sample.cliff_hill,
|
||||
);
|
||||
|
||||
let wheight = st_sample.alt.max(cliff_height);
|
||||
let st_pos3d = Vec3::new(st_pos.x, st_pos.y, wheight as i32);
|
||||
|
||||
let volumes: &'static [_] = if QUIRKY_RAND.get(st_seed) % 64 == 17 {
|
||||
if st_sample.temp > CONFIG.desert_temp {
|
||||
&QUIRKY_DRY
|
||||
} else {
|
||||
&QUIRKY
|
||||
}
|
||||
} else {
|
||||
match st_sample.forest_kind {
|
||||
ForestKind::Palm => &PALMS,
|
||||
ForestKind::Savannah => &ACACIAS,
|
||||
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 16 == 7 => &OAK_STUMPS,
|
||||
ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 8 == 7 => &FRUIT_TREES,
|
||||
ForestKind::Oak => &OAKS,
|
||||
ForestKind::Pine => &PINES,
|
||||
ForestKind::SnowPine => &SNOW_PINES,
|
||||
}
|
||||
};
|
||||
|
||||
const UNIT_CHOICES: [(Vec2<i32>, Vec2<i32>); 8] = [
|
||||
(Vec2 { x: 1, y: 0 }, Vec2 { x: 0, y: 1 }),
|
||||
(Vec2 { x: 1, y: 0 }, Vec2 { x: 0, y: -1 }),
|
||||
(Vec2 { x: -1, y: 0 }, Vec2 { x: 0, y: 1 }),
|
||||
(Vec2 { x: -1, y: 0 }, Vec2 { x: 0, y: -1 }),
|
||||
(Vec2 { x: 0, y: 1 }, Vec2 { x: 1, y: 0 }),
|
||||
(Vec2 { x: 0, y: 1 }, Vec2 { x: -1, y: 0 }),
|
||||
(Vec2 { x: 0, y: -1 }, Vec2 { x: 1, y: 0 }),
|
||||
(Vec2 { x: 0, y: -1 }, Vec2 { x: -1, y: 0 }),
|
||||
];
|
||||
|
||||
Some(StructureInfo {
|
||||
pos: st_pos3d,
|
||||
seed: st_seed,
|
||||
meta: StructureMeta::Volume {
|
||||
units: UNIT_CHOICES[UNIT_RAND.get(st_seed) as usize % UNIT_CHOICES.len()],
|
||||
volume: &volumes[(VOLUME_RAND.get(st_seed) / 13) as usize % volumes.len()],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn st_asset(path: &str, offset: impl Into<Vec3<i32>>) -> Arc<Structure> {
|
||||
assets::load_map(path, |s: Structure| s.with_center(offset.into()))
|
||||
.expect("Failed to load structure asset")
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
@ -43,6 +117,9 @@ lazy_static! {
|
||||
assets::load_map("world/tree/oak_green/9.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(26, 26, 14)))
|
||||
.unwrap(),
|
||||
];
|
||||
|
||||
pub static ref OAK_STUMPS: Vec<Arc<Structure>> = vec![
|
||||
// oak stumps
|
||||
assets::load_map("world/tree/oak_stump/1.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 18, 10)))
|
||||
@ -278,31 +355,35 @@ lazy_static! {
|
||||
|
||||
pub static ref SNOW_PINES: Vec<Arc<Structure>> = vec![
|
||||
// snow pines
|
||||
assets::load_map("world/tree/snow_pine/1.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 15, 14)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/snow_pine/2.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(15, 15, 14)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/snow_pine/3.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(17, 15, 12)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/snow_pine/4.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(10, 8, 12)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/snow_pine/5.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(12, 12, 12)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/snow_pine/6.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(11, 10, 12)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/snow_pine/7.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(16, 15, 12)))
|
||||
.unwrap(),
|
||||
assets::load_map("world/tree/snow_pine/8.vox", |s: Structure| s
|
||||
.with_center(Vec3::new(12, 10, 12)))
|
||||
.unwrap(),
|
||||
st_asset("world/tree/snow_pine/1.vox", (15, 15, 14)),
|
||||
st_asset("world/tree/snow_pine/2.vox", (15, 15, 14)),
|
||||
st_asset("world/tree/snow_pine/3.vox", (17, 15, 12)),
|
||||
st_asset("world/tree/snow_pine/4.vox", (10, 8, 12)),
|
||||
st_asset("world/tree/snow_pine/5.vox", (12, 12, 12)),
|
||||
st_asset("world/tree/snow_pine/6.vox", (11, 10, 12)),
|
||||
st_asset("world/tree/snow_pine/7.vox", (16, 15, 12)),
|
||||
st_asset("world/tree/snow_pine/8.vox", (12, 10, 12)),
|
||||
];
|
||||
|
||||
pub static ref ACACIAS: Vec<Arc<Structure>> = vec![
|
||||
// snow pines
|
||||
st_asset("world/tree/acacia/1.vox", (16, 17, 1)),
|
||||
st_asset("world/tree/acacia/2.vox", (5, 6, 1)),
|
||||
st_asset("world/tree/acacia/3.vox", (5, 6, 1)),
|
||||
st_asset("world/tree/acacia/4.vox", (15, 16, 1)),
|
||||
st_asset("world/tree/acacia/5.vox", (19, 18, 1)),
|
||||
];
|
||||
|
||||
pub static ref FRUIT_TREES: Vec<Arc<Structure>> = vec![
|
||||
// snow pines
|
||||
st_asset("world/tree/fruit/1.vox", (5, 5, 7)),
|
||||
st_asset("world/tree/fruit/2.vox", (5, 5, 7)),
|
||||
st_asset("world/tree/fruit/3.vox", (5, 5, 7)),
|
||||
st_asset("world/tree/fruit/4.vox", (5, 5, 7)),
|
||||
st_asset("world/tree/fruit/5.vox", (5, 5, 7)),
|
||||
st_asset("world/tree/fruit/6.vox", (5, 5, 7)),
|
||||
];
|
||||
|
||||
/*
|
||||
// snow birches -> need roots!
|
||||
assets::load_map("world/tree/snow_birch/1.vox", |s: Structure| s
|
||||
@ -350,4 +431,15 @@ lazy_static! {
|
||||
.unwrap(),
|
||||
];
|
||||
*/
|
||||
|
||||
pub static ref QUIRKY: Vec<Arc<Structure>> = vec![
|
||||
st_asset("world/structure/natural/tower-ruin.vox", (11, 14, 5)),
|
||||
st_asset("world/structure/natural/witch-hut.vox", (10, 13, 9)),
|
||||
];
|
||||
|
||||
pub static ref QUIRKY_DRY: Vec<Arc<Structure>> = vec![
|
||||
st_asset("world/structure/natural/ribcage-small.vox", (7, 13, 4)),
|
||||
st_asset("world/structure/natural/ribcage-large.vox", (13, 19, 8)),
|
||||
st_asset("world/structure/natural/skull-large.vox", (15, 20, 4)),
|
||||
];
|
||||
}
|
@ -1,4 +1,10 @@
|
||||
use crate::{all::ForestKind, sim::LocationInfo, util::Sampler, World, CONFIG};
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
block::StructureMeta,
|
||||
sim::{LocationInfo, SimChunk},
|
||||
util::Sampler,
|
||||
World, CONFIG,
|
||||
};
|
||||
use common::{terrain::TerrainChunkSize, vol::VolSize};
|
||||
use noise::NoiseFn;
|
||||
use std::{
|
||||
@ -15,6 +21,51 @@ impl<'a> ColumnGen<'a> {
|
||||
pub fn new(world: &'a World) -> Self {
|
||||
Self { world }
|
||||
}
|
||||
|
||||
fn get_local_structure(&self, wpos: Vec2<i32>) -> Option<StructureData> {
|
||||
let (pos, seed) = self
|
||||
.world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.region_gen
|
||||
.get(wpos)
|
||||
.iter()
|
||||
.copied()
|
||||
.min_by_key(|(pos, _)| pos.distance_squared(wpos))
|
||||
.unwrap();
|
||||
|
||||
let chunk = self.world.sim().get(pos)?;
|
||||
|
||||
if seed % 5 == 2 && chunk.temp > CONFIG.desert_temp && chunk.alt > CONFIG.sea_level + 5.0 {
|
||||
Some(StructureData {
|
||||
pos,
|
||||
seed,
|
||||
meta: Some(StructureMeta::Pyramid { height: 140 }),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_close_structures(&self, wpos: Vec2<i32>) -> [Option<StructureData>; 9] {
|
||||
let mut metas = [None; 9];
|
||||
self.world
|
||||
.sim()
|
||||
.gen_ctx
|
||||
.structure_gen
|
||||
.get(wpos)
|
||||
.into_iter()
|
||||
.copied()
|
||||
.enumerate()
|
||||
.for_each(|(i, (pos, seed))| {
|
||||
metas[i] = self.get_local_structure(pos).or(Some(StructureData {
|
||||
pos,
|
||||
seed,
|
||||
meta: None,
|
||||
}));
|
||||
});
|
||||
metas
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sampler for ColumnGen<'a> {
|
||||
@ -47,6 +98,7 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
|
||||
const RIVER_PROPORTION: f32 = 0.025;
|
||||
|
||||
/*
|
||||
let river = dryness
|
||||
.abs()
|
||||
.neg()
|
||||
@ -54,6 +106,8 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
.div(RIVER_PROPORTION)
|
||||
.max(0.0)
|
||||
.mul((1.0 - (chaos - 0.15) * 20.0).max(0.0).min(1.0));
|
||||
*/
|
||||
let river = 0.0;
|
||||
|
||||
let cliff_hill =
|
||||
(sim.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32).mul(16.0);
|
||||
@ -61,10 +115,11 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
let riverless_alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?
|
||||
+ (sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32)
|
||||
.abs()
|
||||
.mul(chaos.max(0.2))
|
||||
.mul(chaos.max(0.15))
|
||||
.mul(64.0);
|
||||
|
||||
let cliffs = sim_chunk.cliffs;
|
||||
let is_cliffs = sim_chunk.is_cliffs;
|
||||
let near_cliffs = sim_chunk.near_cliffs;
|
||||
|
||||
let alt = riverless_alt
|
||||
- (1.0 - river)
|
||||
@ -74,7 +129,7 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
.mul(0.5)
|
||||
.mul(24.0);
|
||||
|
||||
let water_level = (riverless_alt - 4.0 - 5.0 * chaos).max(CONFIG.sea_level);
|
||||
let water_level = riverless_alt - 4.0 - 5.0 * chaos;
|
||||
|
||||
let rock = (sim.gen_ctx.small_nz.get(
|
||||
Vec3::new(wposf.x, wposf.y, alt as f64)
|
||||
@ -102,28 +157,32 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
let warm_grass = Rgb::new(0.18, 0.65, 0.0);
|
||||
let cold_stone = Rgb::new(0.55, 0.7, 0.75);
|
||||
let warm_stone = Rgb::new(0.65, 0.65, 0.35);
|
||||
let beach_sand = Rgb::new(0.93, 0.84, 0.4);
|
||||
let desert_sand = Rgb::new(0.98, 0.8, 0.15);
|
||||
let beach_sand = Rgb::new(0.9, 0.85, 0.3);
|
||||
let desert_sand = Rgb::new(1.0, 0.7, 0.15);
|
||||
let snow = Rgb::broadcast(1.0);
|
||||
|
||||
let dirt = Lerp::lerp(Rgb::new(0.2, 0.1, 0.05), Rgb::new(0.4, 0.25, 0.0), marble);
|
||||
let cliff = Rgb::lerp(cold_stone, warm_stone, marble);
|
||||
|
||||
let grass = Rgb::lerp(cold_grass, warm_grass, marble);
|
||||
let sand = Rgb::lerp(beach_sand, desert_sand, marble);
|
||||
let cliff = Rgb::lerp(cold_stone, warm_stone, marble);
|
||||
|
||||
let dirt = Lerp::lerp(Rgb::new(0.2, 0.1, 0.05), Rgb::new(0.4, 0.25, 0.0), marble);
|
||||
|
||||
let turf = grass;
|
||||
let tropical = Rgb::lerp(
|
||||
grass,
|
||||
Rgb::new(0.85, 0.4, 0.2),
|
||||
marble_small.sub(0.5).mul(0.2).add(0.75),
|
||||
);
|
||||
|
||||
let ground = Rgb::lerp(
|
||||
Rgb::lerp(
|
||||
snow,
|
||||
turf,
|
||||
grass,
|
||||
temp.sub(CONFIG.snow_temp)
|
||||
.sub((marble - 0.5) * 0.05)
|
||||
.mul(256.0),
|
||||
),
|
||||
sand,
|
||||
temp.sub(CONFIG.desert_temp).mul(32.0),
|
||||
Rgb::lerp(tropical, sand, temp.sub(CONFIG.desert_temp).mul(32.0)),
|
||||
temp.sub(CONFIG.tropical_temp).mul(16.0),
|
||||
);
|
||||
|
||||
// Work out if we're on a path or near a town
|
||||
@ -227,25 +286,27 @@ impl<'a> Sampler for ColumnGen<'a> {
|
||||
cliff,
|
||||
snow,
|
||||
(alt - CONFIG.sea_level
|
||||
- 0.35 * CONFIG.mountain_scale
|
||||
- 0.4 * CONFIG.mountain_scale
|
||||
- alt_base
|
||||
- temp * 96.0
|
||||
- marble * 24.0)
|
||||
/ 12.0,
|
||||
),
|
||||
(alt - CONFIG.sea_level - 0.3 * CONFIG.mountain_scale + marble * 128.0) / 100.0,
|
||||
(alt - CONFIG.sea_level - 0.25 * CONFIG.mountain_scale + marble * 128.0)
|
||||
/ 100.0,
|
||||
),
|
||||
// Beach
|
||||
((alt - CONFIG.sea_level - 2.0) / 5.0).min(1.0 - river * 2.0),
|
||||
((alt - CONFIG.sea_level - 1.0) / 2.0).min(1.0 - river * 2.0),
|
||||
),
|
||||
sub_surface_color: dirt,
|
||||
tree_density,
|
||||
forest_kind: sim_chunk.forest_kind,
|
||||
close_trees: sim.gen_ctx.tree_gen.get(wpos),
|
||||
close_structures: self.gen_close_structures(wpos),
|
||||
cave_xy,
|
||||
cave_alt,
|
||||
rock,
|
||||
cliffs,
|
||||
is_cliffs,
|
||||
near_cliffs,
|
||||
cliff_hill,
|
||||
close_cliffs: sim.gen_ctx.cliff_gen.get(wpos),
|
||||
temp,
|
||||
@ -265,14 +326,22 @@ pub struct ColumnSample<'a> {
|
||||
pub sub_surface_color: Rgb<f32>,
|
||||
pub tree_density: f32,
|
||||
pub forest_kind: ForestKind,
|
||||
pub close_trees: [(Vec2<i32>, u32); 9],
|
||||
pub close_structures: [Option<StructureData>; 9],
|
||||
pub cave_xy: f32,
|
||||
pub cave_alt: f32,
|
||||
pub rock: f32,
|
||||
pub cliffs: bool,
|
||||
pub is_cliffs: bool,
|
||||
pub near_cliffs: bool,
|
||||
pub cliff_hill: f32,
|
||||
pub close_cliffs: [(Vec2<i32>, u32); 9],
|
||||
pub temp: f32,
|
||||
pub spawn_rate: f32,
|
||||
pub location: Option<&'a LocationInfo>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructureData {
|
||||
pub pos: Vec2<i32>,
|
||||
pub seed: u32,
|
||||
pub meta: Option<StructureMeta>,
|
||||
}
|
||||
|
@ -2,12 +2,14 @@ pub struct Config {
|
||||
pub sea_level: f32,
|
||||
pub mountain_scale: f32,
|
||||
pub snow_temp: f32,
|
||||
pub tropical_temp: f32,
|
||||
pub desert_temp: f32,
|
||||
}
|
||||
|
||||
pub const CONFIG: Config = Config {
|
||||
sea_level: 140.0,
|
||||
mountain_scale: 1200.0,
|
||||
mountain_scale: 1000.0,
|
||||
snow_temp: -0.4,
|
||||
desert_temp: 0.4,
|
||||
tropical_temp: 0.25,
|
||||
desert_temp: 0.45,
|
||||
};
|
||||
|
@ -71,12 +71,19 @@ impl World {
|
||||
.and_then(|base_z| self.sim.get(chunk_pos).map(|sim_chunk| (base_z, sim_chunk)))
|
||||
{
|
||||
Some((base_z, sim_chunk)) => (base_z as i32, sim_chunk),
|
||||
None => return TerrainChunk::new(0, water, air, TerrainChunkMeta::void()),
|
||||
None => {
|
||||
return TerrainChunk::new(
|
||||
CONFIG.sea_level as i32,
|
||||
water,
|
||||
air,
|
||||
TerrainChunkMeta::void(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let meta = TerrainChunkMeta::new(sim_chunk.get_name(&self.sim), sim_chunk.get_biome());
|
||||
|
||||
let mut chunk = TerrainChunk::new(base_z - 8, stone, air, meta);
|
||||
let mut chunk = TerrainChunk::new(base_z, stone, air, meta);
|
||||
|
||||
let mut sampler = self.sample_blocks();
|
||||
|
||||
@ -84,26 +91,24 @@ impl World {
|
||||
for y in 0..TerrainChunkSize::SIZE.y as i32 {
|
||||
let wpos2d = Vec2::new(x, y)
|
||||
+ Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
|
||||
let _wposf2d = wpos2d.map(|e| e as f64);
|
||||
|
||||
let min_z = self
|
||||
.sim
|
||||
.get_interpolated(wpos2d, |chunk| chunk.get_min_z())
|
||||
.unwrap_or(0.0) as i32;
|
||||
let z_cache = match sampler.get_z_cache(wpos2d) {
|
||||
Some(z_cache) => z_cache,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let max_z = self
|
||||
.sim
|
||||
.get_interpolated(wpos2d, |chunk| chunk.get_max_z())
|
||||
.unwrap_or(0.0) as i32;
|
||||
let (min_z, max_z) = z_cache.get_z_limits();
|
||||
|
||||
let z_cache = sampler.get_z_cache(wpos2d);
|
||||
for z in base_z..min_z as i32 {
|
||||
let _ = chunk.set(Vec3::new(x, y, z), stone);
|
||||
}
|
||||
|
||||
for z in min_z..max_z {
|
||||
for z in min_z as i32..max_z as i32 {
|
||||
let lpos = Vec3::new(x, y, z);
|
||||
let wpos =
|
||||
lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32);
|
||||
|
||||
if let Some(block) = sampler.get_with_z_cache(wpos, z_cache.as_ref()) {
|
||||
if let Some(block) = sampler.get_with_z_cache(wpos, Some(&z_cache)) {
|
||||
let _ = chunk.set(lpos, block);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use common::{
|
||||
};
|
||||
use noise::{BasicMulti, HybridMulti, MultiFractal, NoiseFn, RidgedMulti, Seedable, SuperSimplex};
|
||||
use rand::{prng::XorShiftRng, Rng, SeedableRng};
|
||||
use std::ops::{Add, Div, Mul, Sub};
|
||||
use std::ops::{Add, Div, Mul, Neg, Sub};
|
||||
use vek::*;
|
||||
|
||||
pub const WORLD_SIZE: Vec2<usize> = Vec2 { x: 1024, y: 1024 };
|
||||
@ -38,7 +38,8 @@ pub(crate) struct GenCtx {
|
||||
pub cave_0_nz: SuperSimplex,
|
||||
pub cave_1_nz: SuperSimplex,
|
||||
|
||||
pub tree_gen: StructureGen2d,
|
||||
pub structure_gen: StructureGen2d,
|
||||
pub region_gen: StructureGen2d,
|
||||
pub cliff_gen: StructureGen2d,
|
||||
}
|
||||
|
||||
@ -75,8 +76,9 @@ impl WorldSim {
|
||||
cave_0_nz: SuperSimplex::new().set_seed(seed + 13),
|
||||
cave_1_nz: SuperSimplex::new().set_seed(seed + 14),
|
||||
|
||||
tree_gen: StructureGen2d::new(seed, 32, 24),
|
||||
cliff_gen: StructureGen2d::new(seed, 80, 56),
|
||||
structure_gen: StructureGen2d::new(seed, 32, 24),
|
||||
region_gen: StructureGen2d::new(seed + 1, 400, 96),
|
||||
cliff_gen: StructureGen2d::new(seed + 2, 80, 56),
|
||||
};
|
||||
|
||||
let mut chunks = Vec::new();
|
||||
@ -306,8 +308,6 @@ impl WorldSim {
|
||||
}
|
||||
}
|
||||
|
||||
const Z_TOLERANCE: (f32, f32) = (100.0, 128.0);
|
||||
|
||||
pub struct SimChunk {
|
||||
pub chaos: f32,
|
||||
pub alt_base: f32,
|
||||
@ -315,7 +315,7 @@ pub struct SimChunk {
|
||||
pub temp: f32,
|
||||
pub dryness: f32,
|
||||
pub rockiness: f32,
|
||||
pub cliffs: bool,
|
||||
pub is_cliffs: bool,
|
||||
pub near_cliffs: bool,
|
||||
pub tree_density: f32,
|
||||
pub forest_kind: ForestKind,
|
||||
@ -353,6 +353,8 @@ impl SimChunk {
|
||||
.add(0.3)
|
||||
.max(0.0);
|
||||
|
||||
let temp = gen_ctx.temp_nz.get((wposf.div(12000.0)).into_array()) as f32;
|
||||
|
||||
let dryness = gen_ctx.dry_nz.get(
|
||||
(wposf
|
||||
.add(Vec2::new(
|
||||
@ -366,44 +368,53 @@ impl SimChunk {
|
||||
.into_array(),
|
||||
) as f32;
|
||||
|
||||
let chaos = (gen_ctx.chaos_nz.get((wposf.div(4_000.0)).into_array()) as f32)
|
||||
let chaos = (gen_ctx.chaos_nz.get((wposf.div(3_000.0)).into_array()) as f32)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(
|
||||
(gen_ctx.chaos_nz.get((wposf.div(6_000.0)).into_array()) as f32)
|
||||
.powf(2.0)
|
||||
.add(0.5)
|
||||
.abs()
|
||||
.max(0.25)
|
||||
.min(1.0),
|
||||
)
|
||||
.powf(1.5)
|
||||
.add(0.1 * hill);
|
||||
.add(0.15 * hill)
|
||||
.mul(
|
||||
temp.sub(CONFIG.desert_temp)
|
||||
.neg()
|
||||
.mul(12.0)
|
||||
.max(0.35)
|
||||
.min(1.0),
|
||||
)
|
||||
.max(0.1);
|
||||
|
||||
let chaos = chaos + chaos.mul(16.0).sin().mul(0.02);
|
||||
|
||||
let alt_base = gen_ctx.alt_nz.get((wposf.div(6_000.0)).into_array()) as f32;
|
||||
let alt_base = alt_base
|
||||
.mul(0.4)
|
||||
.add(alt_base.mul(128.0).sin().mul(0.005))
|
||||
.mul(400.0);
|
||||
let alt_base = (gen_ctx.alt_nz.get((wposf.div(12_000.0)).into_array()) as f32)
|
||||
.mul(250.0)
|
||||
.sub(25.0);
|
||||
|
||||
let alt_main = (gen_ctx.alt_nz.get((wposf.div(2_000.0)).into_array()) as f32)
|
||||
.abs()
|
||||
.powf(1.8);
|
||||
.powf(1.35);
|
||||
|
||||
let alt = CONFIG.sea_level
|
||||
let map_edge_factor = pos
|
||||
.map2(WORLD_SIZE.map(|e| e as i32), |e, sz| {
|
||||
(sz / 2 - (e - sz / 2).abs()) as f32 / 16.0
|
||||
})
|
||||
.reduce_partial_min()
|
||||
.max(0.0)
|
||||
.min(1.0);
|
||||
|
||||
let alt = (CONFIG.sea_level
|
||||
+ alt_base
|
||||
+ (0.0
|
||||
+ alt_main
|
||||
+ gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32
|
||||
* alt_main.max(0.1)
|
||||
* chaos
|
||||
* 1.6)
|
||||
+ (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32)
|
||||
.mul(alt_main.max(0.25))
|
||||
.mul(1.6))
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(chaos)
|
||||
.mul(CONFIG.mountain_scale);
|
||||
|
||||
let temp = gen_ctx.temp_nz.get((wposf.div(8192.0)).into_array()) as f32;
|
||||
.mul(CONFIG.mountain_scale))
|
||||
* map_edge_factor;
|
||||
|
||||
let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + chaos * 0.2;
|
||||
|
||||
@ -417,24 +428,28 @@ impl SimChunk {
|
||||
.sub(0.1)
|
||||
.mul(1.3)
|
||||
.max(0.0),
|
||||
cliffs: cliff > 0.5
|
||||
is_cliffs: cliff > 0.5
|
||||
&& dryness > 0.05
|
||||
&& alt > CONFIG.sea_level + 5.0
|
||||
&& dryness.abs() > 0.075,
|
||||
near_cliffs: cliff > 0.4,
|
||||
near_cliffs: cliff > 0.25,
|
||||
tree_density: (gen_ctx.tree_nz.get((wposf.div(1024.0)).into_array()) as f32)
|
||||
.mul(1.5)
|
||||
.add(1.0)
|
||||
.mul(0.5)
|
||||
.mul(1.2 - chaos * 0.95)
|
||||
.add(0.1)
|
||||
.add(0.05)
|
||||
.mul(if alt > CONFIG.sea_level + 5.0 {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}),
|
||||
})
|
||||
.max(0.0),
|
||||
forest_kind: if temp > 0.0 {
|
||||
if temp > CONFIG.desert_temp {
|
||||
ForestKind::Palm
|
||||
} else if temp > CONFIG.tropical_temp {
|
||||
ForestKind::Savannah
|
||||
} else {
|
||||
ForestKind::Oak
|
||||
}
|
||||
@ -451,16 +466,7 @@ impl SimChunk {
|
||||
}
|
||||
|
||||
pub fn get_base_z(&self) -> f32 {
|
||||
self.alt - Z_TOLERANCE.0 * self.chaos
|
||||
}
|
||||
|
||||
pub fn get_min_z(&self) -> f32 {
|
||||
self.alt - Z_TOLERANCE.0 * (self.chaos * 1.2 + 0.3)
|
||||
}
|
||||
|
||||
pub fn get_max_z(&self) -> f32 {
|
||||
(self.alt + Z_TOLERANCE.1 * if self.near_cliffs { 1.0 } else { 0.5 })
|
||||
.max(CONFIG.sea_level + 2.0)
|
||||
self.alt - self.chaos * 50.0 - 16.0
|
||||
}
|
||||
|
||||
pub fn get_name(&self, world: &WorldSim) -> Option<String> {
|
||||
|
@ -6,7 +6,7 @@ pub mod structure;
|
||||
// Reexports
|
||||
pub use self::{
|
||||
hash_cache::HashCache,
|
||||
random::RandomField,
|
||||
random::{RandomField, RandomPerm},
|
||||
sampler::{Sampler, SamplerMut},
|
||||
structure::StructureGen2d,
|
||||
};
|
||||
|
@ -6,7 +6,7 @@ pub struct RandomField {
|
||||
}
|
||||
|
||||
impl RandomField {
|
||||
pub fn new(seed: u32) -> Self {
|
||||
pub const fn new(seed: u32) -> Self {
|
||||
Self { seed }
|
||||
}
|
||||
}
|
||||
@ -35,3 +35,28 @@ impl Sampler for RandomField {
|
||||
a
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RandomPerm {
|
||||
seed: u32,
|
||||
}
|
||||
|
||||
impl RandomPerm {
|
||||
pub const fn new(seed: u32) -> Self {
|
||||
Self { seed }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler for RandomPerm {
|
||||
type Index = u32;
|
||||
type Sample = u32;
|
||||
|
||||
fn get(&self, perm: Self::Index) -> Self::Sample {
|
||||
let mut a = perm;
|
||||
a = (a ^ 61) ^ (a >> 16);
|
||||
a = a + (a << 3);
|
||||
a = a ^ (a >> 4);
|
||||
a = a * 0x27d4eb2d;
|
||||
a = a ^ (a >> 15);
|
||||
a
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user