Merge branch 'zesterer/worldgen' into 'master'

Worldgen improvements

See merge request veloren/veloren!457
This commit is contained in:
Joshua Barretto 2019-08-31 21:37:54 +00:00
commit 6919886475
46 changed files with 1342 additions and 96 deletions

View File

@ -170,7 +170,7 @@ void main() {
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 = fxaa_color;
vec4 final_color = fxaa_color;
//vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a);
tgt_color = vec4(final_color.rgb, 1);

View File

@ -47,4 +47,4 @@ void main() {
proj_mat *
view_mat *
vec4(f_pos, 1);
}
}

BIN
assets/world/module/human/balcony_upstairs.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/chimney_roof.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/corner_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/corner_roof.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/corner_upstairs.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/door_big.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/door_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/floor_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/floor_roof.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/floor_upstairs.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/stair_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/wall_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/wall_roof.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/wall_upstairs.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/window_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/human/window_upstairs.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/corner_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/corner_mid.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/corner_top.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/edge_ground.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/edge_mid.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/edge_top.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/end_top.vox (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/world/module/wall/single_top.vox (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

1
server-cli/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
settings.ron

View File

@ -104,8 +104,8 @@ impl<V: BaseVol<Vox = Block> + ReadVol + Debug, S: VolSize + Clone>
.map(|vox| block_shadow_density(vox.kind()))
.unwrap_or((0.0, 0.0));
neighbour_light[0][i][j] =
(neighbour_light[0][i][j] * (1.0 - density)).max(cap);
neighbour_light[0][i][j] = (neighbour_light[0][i][j] * (1.0 - density))
.max(cap.min(neighbour_light[1][i][j]));
}
}

View File

@ -608,11 +608,9 @@ impl Terrain {
}
}
// Translucent
// Terrain sprites
for (pos, chunk) in &self.chunks {
if chunk.visible {
renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights);
const SPRITE_RENDER_DISTANCE: f32 = 128.0;
let chunk_center = pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
@ -632,5 +630,12 @@ impl Terrain {
}
}
}
// Translucent
for (_, chunk) in &self.chunks {
if chunk.visible {
renderer.render_fluid_chunk(&chunk.fluid_model, globals, &chunk.locals, lights);
}
}
}
}

View File

@ -2,6 +2,7 @@ mod natural;
use crate::{
column::{ColumnGen, ColumnSample, StructureData},
generator::{Generator, TownGen},
util::{HashCache, RandomField, Sampler, SamplerMut},
World, CONFIG,
};
@ -136,6 +137,7 @@ impl<'a> BlockGen<'a> {
column_gen,
} = self;
let sample = &z_cache?.sample;
let &ColumnSample {
alt,
chaos,
@ -155,8 +157,10 @@ impl<'a> BlockGen<'a> {
cliff_hill,
close_cliffs,
temp,
chunk,
..
} = &z_cache?.sample;
} = sample;
let structures = &z_cache?.structures;
@ -168,31 +172,21 @@ impl<'a> BlockGen<'a> {
(true, alt, CONFIG.sea_level /*water_level*/)
} else {
// 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)
let warp = (world.sim().gen_ctx.warp_nz.get(wposf.div(48.0)) as f32)
.mul((chaos - 0.1).max(0.0))
.mul(96.0);
.mul(48.0)
+ (world.sim().gen_ctx.warp_nz.get(wposf.div(15.0)) as f32)
.mul((chaos - 0.1).max(0.0))
.mul(24.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;
world.sim().gen_ctx.fast_turb_x_nz.get(wposf.div(25.0)) as f32,
world.sim().gen_ctx.fast_turb_y_nz.get(wposf.div(25.0)) as f32,
) * 8.0;
let wpos_turb = Vec2::from(wpos).map(|e: i32| e as f32) + turb;
let cliff_height = Self::get_cliff_height(
@ -352,6 +346,14 @@ impl<'a> BlockGen<'a> {
}
});
// Structures (like towns)
let block = chunk
.structures
.town
.as_ref()
.and_then(|town| TownGen.get((town, wpos, sample, height)))
.or(block);
let block = structures
.iter()
.find_map(|st| {
@ -406,11 +408,24 @@ impl<'a> ZCache<'a> {
.max(self.sample.water_level)
.max(CONFIG.sea_level + 2.0);
// Structures
let (min, max) = self
.sample
.chunk
.structures
.town
.as_ref()
.map(|town| {
let (town_min, town_max) = TownGen.get_z_limits(town, self.wpos, &self.sample);
(town_min.min(min), town_max.max(max))
})
.unwrap_or((min, max));
(min, max)
}
}
impl<'a> SamplerMut for BlockGen<'a> {
impl<'a> SamplerMut<'static> for BlockGen<'a> {
type Index = Vec3<i32>;
type Sample = Option<Block>;
@ -499,7 +514,7 @@ impl StructureInfo {
}
}
fn block_from_structure(
pub fn block_from_structure(
sblock: StructureBlock,
default_kind: BlockKind,
pos: Vec3<i32>,

View File

@ -30,6 +30,7 @@ pub fn structure_gen<'a>(
if (st_sample.tree_density as f64) < random_seed
|| st_sample.alt < st_sample.water_level
|| st_sample.spawn_rate < 0.5
|| !st_sample.spawn_rules.trees
{
return None;
}

View File

@ -1,7 +1,8 @@
use crate::{
all::ForestKind,
block::StructureMeta,
sim::{LocationInfo, SimChunk},
generator::{Generator, SpawnRules, TownGen},
sim::{LocationInfo, SimChunk, WorldSim},
util::{RandomPerm, Sampler, UnitChooser},
World, CONFIG,
};
@ -20,7 +21,7 @@ use std::{
use vek::*;
pub struct ColumnGen<'a> {
world: &'a World,
pub sim: &'a WorldSim,
}
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x700F4EC7);
@ -55,14 +56,13 @@ lazy_static! {
}
impl<'a> ColumnGen<'a> {
pub fn new(world: &'a World) -> Self {
Self { world }
pub fn new(sim: &'a WorldSim) -> Self {
Self { sim }
}
fn get_local_structure(&self, wpos: Vec2<i32>) -> Option<StructureData> {
let (pos, seed) = self
.world
.sim()
.sim
.gen_ctx
.region_gen
.get(wpos)
@ -74,7 +74,7 @@ impl<'a> ColumnGen<'a> {
let chunk_pos = pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
e / sz as i32
});
let chunk = self.world.sim().get(chunk_pos)?;
let chunk = self.sim.get(chunk_pos)?;
if seed % 5 == 2
&& chunk.temp > CONFIG.desert_temp
@ -102,8 +102,7 @@ impl<'a> ColumnGen<'a> {
fn gen_close_structures(&self, wpos: Vec2<i32>) -> [Option<StructureData>; 9] {
let mut metas = [None; 9];
self.world
.sim()
self.sim
.gen_ctx
.structure_gen
.get(wpos)
@ -121,7 +120,7 @@ impl<'a> ColumnGen<'a> {
}
}
impl<'a> Sampler for ColumnGen<'a> {
impl<'a> Sampler<'a> for ColumnGen<'a> {
type Index = Vec2<i32>;
type Sample = Option<ColumnSample<'a>>;
@ -131,7 +130,7 @@ impl<'a> Sampler for ColumnGen<'a> {
e / sz as i32
});
let sim = self.world.sim();
let sim = &self.sim;
let turb = Vec2::new(
sim.gen_ctx.turb_x_nz.get((wposf.div(48.0)).into_array()) as f32,
@ -142,7 +141,6 @@ impl<'a> Sampler for ColumnGen<'a> {
let alt_base = sim.get_interpolated(wpos, |chunk| chunk.alt_base)?;
let chaos = sim.get_interpolated(wpos, |chunk| chunk.chaos)?;
let temp = sim.get_interpolated(wpos, |chunk| chunk.temp)?;
let dryness = sim.get_interpolated(wpos, |chunk| chunk.dryness)?;
let humidity = sim.get_interpolated(wpos, |chunk| chunk.humidity)?;
let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?;
let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?;
@ -175,8 +173,16 @@ impl<'a> Sampler for ColumnGen<'a> {
.small_nz
.get((wposf_turb.div(150.0)).into_array()) as f32)
.abs()
.mul(chaos.max(0.15))
.mul(64.0);
.mul(chaos.max(0.025))
.mul(64.0)
+ (sim
.gen_ctx
.small_nz
.get((wposf_turb.div(450.0)).into_array()) as f32)
.abs()
.mul(1.0 - chaos)
.mul(1.0 - humidity)
.mul(96.0);
let is_cliffs = sim_chunk.is_cliffs;
let near_cliffs = sim_chunk.near_cliffs;
@ -376,6 +382,7 @@ impl<'a> Sampler for ColumnGen<'a> {
.add((marble_small - 0.5) * 0.5),
);
/*
// Work out if we're on a path or near a town
let dist_to_path = match &sim_chunk.location {
Some(loc) => {
@ -412,9 +419,11 @@ impl<'a> Sampler for ColumnGen<'a> {
} else {
(alt, ground)
};
*/
// Cities
// TODO: In a later MR
/*
let building = match &sim_chunk.location {
Some(loc) => {
let loc = &sim.locations[loc.loc_idx];
@ -433,6 +442,7 @@ impl<'a> Sampler for ColumnGen<'a> {
};
let alt = alt + building;
*/
// Caves
let cave_at = |wposf: Vec2<f64>| {
@ -507,6 +517,14 @@ impl<'a> Sampler for ColumnGen<'a> {
temp,
spawn_rate,
location: sim_chunk.location.as_ref(),
chunk: sim_chunk,
spawn_rules: sim_chunk
.structures
.town
.as_ref()
.map(|town| TownGen.spawn_rules(town, wpos))
.unwrap_or(SpawnRules::default()),
})
}
}
@ -534,6 +552,9 @@ pub struct ColumnSample<'a> {
pub temp: f32,
pub spawn_rate: f32,
pub location: Option<&'a LocationInfo>,
pub chunk: &'a SimChunk,
pub spawn_rules: SpawnRules,
}
#[derive(Copy, Clone)]

View File

@ -0,0 +1,34 @@
mod town;
// Reexports
pub use self::town::{TownGen, TownState};
use crate::{column::ColumnSample, util::Sampler};
use common::terrain::Block;
use vek::*;
#[derive(Copy, Clone, Debug)]
pub struct SpawnRules {
pub trees: bool,
}
impl Default for SpawnRules {
fn default() -> Self {
Self { trees: true }
}
}
impl SpawnRules {
pub fn and(self, other: Self) -> Self {
Self {
trees: self.trees && other.trees,
}
}
}
pub trait Generator<'a, T: 'a>:
Sampler<'a, Index = (&'a T, Vec3<i32>, &'a ColumnSample<'a>, f32), Sample = Option<Block>>
{
fn get_z_limits(&self, state: &'a T, wpos: Vec2<i32>, sample: &ColumnSample) -> (f32, f32);
fn spawn_rules(&self, town: &'a TownState, wpos: Vec2<i32>) -> SpawnRules;
}

View File

@ -0,0 +1,655 @@
mod util;
mod vol;
use super::{Generator, SpawnRules};
use crate::{
block::block_from_structure,
column::{ColumnGen, ColumnSample},
sim::WorldSim,
util::{seed_expan, Grid, Sampler, UnitChooser},
};
use common::{
assets,
terrain::{Block, BlockKind, Structure},
vol::{ReadVol, Vox, WriteVol},
};
use hashbrown::HashSet;
use lazy_static::lazy_static;
use rand::prelude::*;
use rand_chacha::ChaChaRng;
use std::{ops::Add, sync::Arc};
use vek::*;
use self::vol::{CellKind, ColumnKind, Module, TownCell, TownColumn, TownVol};
const CELL_SIZE: i32 = 9;
const CELL_HEIGHT: i32 = 9;
pub struct TownGen;
impl<'a> Sampler<'a> for TownGen {
type Index = (&'a TownState, Vec3<i32>, &'a ColumnSample<'a>, f32);
type Sample = Option<Block>;
fn get(&self, (town, wpos, sample, height): Self::Index) -> Self::Sample {
let cell_pos = (wpos - town.center)
.map2(Vec3::new(CELL_SIZE, CELL_SIZE, CELL_HEIGHT), |e, sz| {
e.div_euclid(sz)
})
.add(Vec3::from(town.vol.size() / 2));
let inner_pos = (wpos - town.center)
.map2(Vec3::new(CELL_SIZE, CELL_SIZE, CELL_HEIGHT), |e, sz| {
e.rem_euclid(sz)
});
let cell = town.vol.get(cell_pos).ok()?;
match (modules_from_kind(&cell.kind), &cell.module) {
(Some(module_list), Some(module)) => {
let transform = [
(Vec2::new(0, 0), Vec2::unit_x(), Vec2::unit_y()),
(Vec2::new(0, 1), -Vec2::unit_y(), Vec2::unit_x()),
(Vec2::new(1, 1), -Vec2::unit_x(), -Vec2::unit_y()),
(Vec2::new(1, 0), Vec2::unit_y(), -Vec2::unit_x()),
];
module_list[module.vol_idx]
.0
.get(
Vec3::from(
transform[module.dir].0 * (CELL_SIZE - 1)
+ transform[module.dir].1 * inner_pos.x
+ transform[module.dir].2 * inner_pos.y,
) + Vec3::unit_z() * inner_pos.z,
)
.ok()
.and_then(|sb| {
block_from_structure(*sb, BlockKind::Normal, wpos, wpos.into(), 0, sample)
})
}
_ => match cell.kind {
CellKind::Empty => None,
CellKind::Park => None,
CellKind::Rock => Some(Block::new(BlockKind::Normal, Rgb::broadcast(100))),
CellKind::Wall => Some(Block::new(BlockKind::Normal, Rgb::broadcast(175))),
CellKind::Road => {
if (wpos.z as f32) < height - 1.0 {
Some(Block::new(
BlockKind::Normal,
Lerp::lerp(
Rgb::new(150.0, 140.0, 50.0),
Rgb::new(100.0, 95.0, 30.0),
sample.marble_small,
)
.map(|e| e as u8),
))
} else {
Some(Block::empty())
}
}
CellKind::House(idx) => Some(Block::new(BlockKind::Normal, town.houses[idx].color)),
},
}
}
}
impl<'a> Generator<'a, TownState> for TownGen {
fn get_z_limits(
&self,
town: &'a TownState,
wpos: Vec2<i32>,
sample: &ColumnSample,
) -> (f32, f32) {
(sample.alt - 32.0, sample.alt + 75.0)
}
fn spawn_rules(&self, town: &'a TownState, wpos: Vec2<i32>) -> SpawnRules {
SpawnRules { trees: false }
}
}
struct House {
color: Rgb<u8>,
}
pub struct TownState {
center: Vec3<i32>,
radius: i32,
vol: TownVol,
houses: Vec<House>,
}
impl TownState {
pub fn generate(center: Vec2<i32>, gen: &mut ColumnGen, rng: &mut impl Rng) -> Option<Self> {
let radius = rng.gen_range(18, 20) * 9;
let size = Vec2::broadcast(radius * 2 / 9 - 2);
let alt = gen.get(center).map(|sample| sample.alt).unwrap_or(0.0) as i32;
let mut vol = TownVol::generate_from(
size,
|pos| {
let wpos = center + (pos - size / 2) * CELL_SIZE + CELL_SIZE / 2;
let rel_alt = gen.get(wpos).map(|sample| sample.alt).unwrap_or(0.0) as i32
+ CELL_HEIGHT / 2
- alt;
let col = TownColumn {
ground: rel_alt.div_euclid(CELL_HEIGHT),
kind: None,
};
(col.ground, col)
},
|(col, pos)| {
if pos.z >= col.ground {
TownCell::empty()
} else {
TownCell::from(CellKind::Rock)
}
},
);
// Generation passes
vol.setup(rng);
vol.gen_roads(rng, 30);
vol.gen_parks(rng, 3);
vol.emplace_columns();
let houses = vol.gen_houses(rng, 50);
vol.gen_walls(rng);
vol.resolve_modules(rng);
vol.cull_unused();
Some(Self {
center: Vec3::new(center.x, center.y, alt),
radius,
vol,
houses,
})
}
pub fn center(&self) -> Vec3<i32> {
self.center
}
pub fn radius(&self) -> i32 {
self.radius
}
}
impl TownVol {
fn floodfill(
&self,
limit: Option<usize>,
mut opens: HashSet<Vec2<i32>>,
mut f: impl FnMut(Vec2<i32>, &TownColumn) -> bool,
) -> HashSet<Vec2<i32>> {
let mut closed = HashSet::new();
while opens.len() > 0 {
let mut new_opens = HashSet::new();
'search: for open in opens.iter() {
for i in -1..2 {
for j in -1..2 {
let pos = *open + Vec2::new(i, j);
if let Some(col) = self.col(pos) {
if !closed.contains(&pos) && !opens.contains(&pos) && f(pos, col) {
match limit {
Some(limit)
if limit
<= new_opens.len() + closed.len() + opens.len() =>
{
break 'search
}
_ => {
new_opens.insert(pos);
}
}
}
}
}
}
}
closed = closed.union(&opens).copied().collect();
opens = new_opens;
}
closed
}
fn setup(&mut self, rng: &mut impl Rng) {
// Place a single road tile at first
let root_road = self
.size()
.map(|sz| (sz / 8) * 2 + rng.gen_range(0, sz / 4) * 2);
self.set_col_kind(root_road, Some(ColumnKind::Road));
}
fn gen_roads(&mut self, rng: &mut impl Rng, n: usize) {
const ATTEMPTS: usize = 5;
let mut junctions = HashSet::new();
junctions.insert(self.choose_column(rng, |_, col| col.is_road()).unwrap());
for road in 0..n {
for _ in 0..ATTEMPTS {
let start = *junctions.iter().choose(rng).unwrap();
//let start = self.choose_column(rng, |pos, col| pos.map(|e| e % 2 == 0).reduce_and() && col.is_road()).unwrap();
let dir = util::gen_dir(rng);
// If the direction we want to paint a path in is obstructed, abandon this attempt
if self
.col(start + dir)
.map(|col| !col.is_empty())
.unwrap_or(true)
{
continue;
}
// How long should this road be?
let len = rng.gen_range(1, 10) * 2 + 1;
// Paint the road until we hit an obstacle
let success = (1..len)
.map(|i| start + dir * i)
.try_for_each(|pos| {
if self.col(pos).map(|col| col.is_empty()).unwrap_or(false) {
self.set_col_kind(pos, Some(ColumnKind::Road));
Ok(())
} else {
junctions.insert(pos);
Err(())
}
})
.is_ok();
if success {
junctions.insert(start + dir * (len - 1));
}
break;
}
}
}
fn gen_parks(&mut self, rng: &mut impl Rng, n: usize) {
const ATTEMPTS: usize = 5;
for _ in 0..n {
for _ in 0..ATTEMPTS {
let start = self
.choose_column(rng, |pos, col| {
col.is_empty()
&& (0..4).any(|i| {
self.col(pos + util::dir(i))
.map(|col| col.is_road())
.unwrap_or(false)
})
})
.unwrap();
let mut park =
self.floodfill(Some(16), [start].iter().copied().collect(), |_, col| {
col.is_empty()
});
if park.len() < 4 {
continue;
}
for cell in park {
self.set_col_kind(cell, Some(ColumnKind::Internal));
let col = self.col(cell).unwrap();
let ground = col.ground;
for z in 0..2 {
self.set(Vec3::new(cell.x, cell.y, ground + z), CellKind::Park.into());
}
}
break;
}
}
}
fn gen_walls(&mut self, rng: &mut impl Rng) {
let mut outer = HashSet::new();
for i in 0..self.size().x {
outer.insert(Vec2::new(i, 0));
outer.insert(Vec2::new(i, self.size().y - 1));
}
for j in 0..self.size().y {
outer.insert(Vec2::new(0, j));
outer.insert(Vec2::new(self.size().x - 1, j));
}
let mut outer = self.floodfill(None, outer, |_, col| col.is_empty());
let mut walls = HashSet::new();
let inner = self.floodfill(
None,
[self.size() / 2].iter().copied().collect(),
|pos, _| {
if outer.contains(&pos) {
walls.insert(pos);
false
} else {
true
}
},
);
while let Some(wall) = walls
.iter()
.filter(|pos| {
let lateral_count = (0..4)
.filter(|i| walls.contains(&(**pos + util::dir(*i))))
.count();
let max_quadrant_count = (0..4)
.map(|i| {
let units = util::unit(i);
(0..2)
.map(|i| (0..2).map(move |j| (i, j)))
.flatten()
.filter(|(i, j)| walls.contains(&(**pos + units.0 * *i + units.1 * *j)))
.count()
})
.max()
.unwrap();
lateral_count < 2 || (lateral_count == 2 && max_quadrant_count == 4)
})
.next()
{
let wall = *wall;
walls.remove(&wall);
}
for wall in walls.iter() {
let col = self.col(*wall).unwrap();
let ground = col.ground;
for z in -1..3 {
self.set(Vec3::new(wall.x, wall.y, ground + z), CellKind::Wall.into());
}
}
}
fn emplace_columns(&mut self) {
for i in 0..self.size().x {
for j in 0..self.size().y {
let col = self.col(Vec2::new(i, j)).unwrap();
let ground = col.ground;
match col.kind {
None => {}
Some(ColumnKind::Internal) => {}
Some(ColumnKind::External) => {}
Some(ColumnKind::Road) => {
for z in -1..2 {
self.set(Vec3::new(i, j, ground + z), CellKind::Road.into());
}
}
_ => unimplemented!(),
}
}
}
}
fn gen_houses(&mut self, rng: &mut impl Rng, n: usize) -> Vec<House> {
const ATTEMPTS: usize = 10;
let mut houses = Vec::new();
for _ in 0..n {
for _ in 0..ATTEMPTS {
let entrance = {
let start = self.choose_cell(rng, |_, cell| cell.is_road()).unwrap();
let dir = Vec3::from(util::gen_dir(rng));
if self
.get(start + dir)
.map(|col| !col.is_empty())
.unwrap_or(true)
|| self
.get(start + dir - Vec3::unit_z())
.map(|col| !col.is_foundation())
.unwrap_or(true)
{
continue;
} else {
start + dir
}
};
let mut cells: HashSet<_> = Some(entrance).into_iter().collect();
let mut energy = 1000;
while energy > 0 {
energy -= 1;
let parent = *cells.iter().choose(rng).unwrap();
let dir = util::UNITS_3D
.choose_weighted(rng, |pos| 1 + pos.z.max(0))
.unwrap();
if self
.get(parent + dir)
.map(|cell| cell.is_empty())
.unwrap_or(false)
&& self
.get(parent + dir - Vec3::unit_z())
.map(|cell| {
cell.is_foundation()
|| cells.contains(&(parent + dir - Vec3::unit_z()))
})
.unwrap_or(false)
{
cells.insert(parent + dir);
energy -= 10;
}
}
// Remove cells that are too isolated
loop {
let cells_copy = cells.clone();
let mut any_removed = false;
cells.retain(|pos| {
let neighbour_count = (0..6)
.filter(|i| {
let neighbour = pos + util::dir_3d(*i);
cells_copy.contains(&neighbour)
})
.count();
if neighbour_count < 3 {
any_removed = true;
false
} else {
true
}
});
if !any_removed {
break;
}
}
// Get rid of houses that are too small
if cells.len() < 6 {
continue;
}
for cell in cells {
self.set(cell, CellKind::House(houses.len()).into());
self.set_col_kind(Vec2::from(cell), Some(ColumnKind::Internal));
}
houses.push(House {
color: Rgb::new(rng.gen(), rng.gen(), rng.gen()),
});
}
}
houses
}
fn cull_unused(&mut self) {
for x in 0..self.size().x {
for y in 0..self.size().y {
for z in self.col_range(Vec2::new(x, y)).unwrap().rev() {
let pos = Vec3::new(x, y, z);
// Remove foundations that don't have anything on top of them
if self.get(pos).unwrap().is_foundation()
&& self
.get(pos + Vec3::unit_z())
.map(TownCell::is_space)
.unwrap_or(true)
{
self.set(pos, TownCell::empty());
}
}
}
}
}
fn resolve_modules(&mut self, rng: &mut impl Rng) {
fn classify(cell: &TownCell, this_cell: &TownCell) -> ModuleKind {
match (&cell.kind, &this_cell.kind) {
(CellKind::House(a), CellKind::House(b)) if a == b => ModuleKind::This,
(CellKind::Wall, CellKind::Wall) => ModuleKind::This,
_ => ModuleKind::That,
}
}
for x in 0..self.size().x {
for y in 0..self.size().y {
for z in self.col_range(Vec2::new(x, y)).unwrap() {
let pos = Vec3::new(x, y, z);
let this_cell = if let Ok(this_cell) = self.get(pos) {
this_cell
} else {
continue;
};
let mut signature = [ModuleKind::That; 6];
for i in 0..6 {
signature[i] = self
.get(pos + util::dir_3d(i))
.map(|cell| classify(cell, this_cell))
.unwrap_or(ModuleKind::That);
}
let module_list = if let Some(modules) = modules_from_kind(&this_cell.kind) {
modules
} else {
continue;
};
let module = module_list
.iter()
.enumerate()
.filter_map(|(i, module)| {
let perms = [[0, 1, 2, 3], [3, 0, 1, 2], [2, 3, 0, 1], [1, 2, 3, 0]];
let mut rotated_signature = [ModuleKind::That; 6];
for (dir, perm) in perms.iter().enumerate() {
rotated_signature[perm[0]] = signature[0];
rotated_signature[perm[1]] = signature[1];
rotated_signature[perm[2]] = signature[2];
rotated_signature[perm[3]] = signature[3];
rotated_signature[4] = signature[4];
rotated_signature[5] = signature[5];
if &module.1[0..6] == &rotated_signature[0..6] {
return Some(Module { vol_idx: i, dir });
}
}
None
})
.choose(rng);
if let Some(module) = module {
let kind = this_cell.kind.clone();
self.set(
pos,
TownCell {
kind,
module: Some(module),
},
);
}
}
}
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum ModuleKind {
This,
That,
}
fn module(name: &str, sig: [ModuleKind; 6]) -> (Arc<Structure>, [ModuleKind; 6]) {
(
assets::load(&format!("world.module.{}", name)).unwrap(),
sig,
)
}
fn modules_from_kind(kind: &CellKind) -> Option<&'static [(Arc<Structure>, [ModuleKind; 6])]> {
match kind {
CellKind::House(_) => Some(&HOUSE_MODULES),
CellKind::Wall => Some(&WALL_MODULES),
_ => None,
}
}
lazy_static! {
pub static ref HOUSE_MODULES: Vec<(Arc<Structure>, [ModuleKind; 6])> = {
use ModuleKind::*;
vec![
module("human.floor_ground", [This, This, This, This, This, That]),
module("human.stair_ground", [This, This, This, This, This, That]),
module("human.corner_ground", [This, This, That, That, This, That]),
module("human.wall_ground", [This, This, This, That, This, That]),
module("human.door_ground", [This, This, This, That, This, That]),
module("human.window_ground", [This, This, This, That, This, That]),
module("human.floor_roof", [This, This, This, This, That, This]),
module("human.corner_roof", [This, This, That, That, That, This]),
module("human.chimney_roof", [This, This, That, That, That, This]),
module("human.wall_roof", [This, This, This, That, That, This]),
module("human.floor_upstairs", [This, This, This, This, This, This]),
module(
"human.balcony_upstairs",
[This, This, This, This, This, This],
),
module(
"human.corner_upstairs",
[This, This, That, That, This, This],
),
module("human.wall_upstairs", [This, This, This, That, This, This]),
module(
"human.window_upstairs",
[This, This, This, That, This, This],
),
]
};
pub static ref WALL_MODULES: Vec<(Arc<Structure>, [ModuleKind; 6])> = {
use ModuleKind::*;
vec![
module("wall.edge_ground", [This, That, This, That, This, That]),
module("wall.edge_mid", [This, That, This, That, This, This]),
module("wall.edge_top", [This, That, This, That, That, This]),
module("wall.corner_ground", [This, This, That, That, This, That]),
module("wall.corner_mid", [This, This, That, That, This, This]),
module("wall.corner_top", [This, This, That, That, That, This]),
module("wall.end_top", [That, This, That, That, That, This]),
module("wall.single_top", [That, That, That, That, That, This]),
]
};
}

View File

@ -0,0 +1,42 @@
use rand::prelude::*;
use vek::*;
pub const UNITS: [Vec2<i32>; 4] = [
Vec2 { x: 1, y: 0 },
Vec2 { x: 0, y: 1 },
Vec2 { x: -1, y: 0 },
Vec2 { x: 0, y: -1 },
];
pub fn dir(i: usize) -> Vec2<i32> {
UNITS[i % 4]
}
pub fn unit(i: usize) -> (Vec2<i32>, Vec2<i32>) {
(UNITS[i % 4], UNITS[(i + 1) % 4])
}
pub fn gen_unit(rng: &mut impl Rng) -> (Vec2<i32>, Vec2<i32>) {
unit(rng.gen_range(0, 4))
}
pub fn gen_dir(rng: &mut impl Rng) -> Vec2<i32> {
UNITS[rng.gen_range(0, 4)]
}
pub const UNITS_3D: [Vec3<i32>; 6] = [
Vec3 { x: 1, y: 0, z: 0 },
Vec3 { x: 0, y: 1, z: 0 },
Vec3 { x: -1, y: 0, z: 0 },
Vec3 { x: 0, y: -1, z: 0 },
Vec3 { x: 0, y: 0, z: 1 },
Vec3 { x: 0, y: 0, z: -1 },
];
pub fn dir_3d(i: usize) -> Vec3<i32> {
UNITS_3D[i % 6]
}
pub fn gen_dir_3d(rng: &mut impl Rng) -> Vec3<i32> {
UNITS_3D[rng.gen_range(0, 6)]
}

View File

@ -0,0 +1,227 @@
use crate::util::Grid;
use common::vol::{BaseVol, ReadVol, Vox, WriteVol};
use rand::prelude::*;
use std::ops::Range;
use vek::*;
#[derive(Clone)]
pub enum ColumnKind {
Road,
Wall,
Internal,
External, // Outside the boundary wall
}
#[derive(Clone, Default)]
pub struct TownColumn {
pub ground: i32,
pub kind: Option<ColumnKind>,
}
impl TownColumn {
pub fn is_empty(&self) -> bool {
self.kind.is_none()
}
pub fn is_road(&self) -> bool {
self.kind
.as_ref()
.map(|kind| match kind {
ColumnKind::Road => true,
_ => false,
})
.unwrap_or(false)
}
}
#[derive(Clone)]
pub struct Module {
pub vol_idx: usize,
pub dir: usize,
}
#[derive(Clone)]
pub enum CellKind {
Empty,
Park,
Rock,
Road,
Wall,
House(usize),
}
#[derive(Clone)]
pub struct TownCell {
pub kind: CellKind,
pub module: Option<Module>,
}
impl TownCell {
pub fn is_road(&self) -> bool {
match self.kind {
CellKind::Road => true,
_ => false,
}
}
pub fn is_space(&self) -> bool {
match self.kind {
CellKind::Empty => true,
CellKind::Park => true,
CellKind::Road => true,
_ => false,
}
}
pub fn is_foundation(&self) -> bool {
match self.kind {
CellKind::Rock => true,
_ => false,
}
}
}
impl Vox for TownCell {
fn empty() -> Self {
Self {
kind: CellKind::Empty,
module: None,
}
}
fn is_empty(&self) -> bool {
match self.kind {
CellKind::Empty => true,
_ => false,
}
}
}
impl From<CellKind> for TownCell {
fn from(kind: CellKind) -> Self {
Self { kind, module: None }
}
}
#[derive(Debug)]
pub enum TownError {
OutOfBounds,
}
const HEIGHT: usize = 24;
const UNDERGROUND_DEPTH: i32 = 5;
type GridItem = (i32, TownColumn, Vec<TownCell>);
pub struct TownVol {
grid: Grid<GridItem>,
}
impl TownVol {
pub fn generate_from(
size: Vec2<i32>,
mut f: impl FnMut(Vec2<i32>) -> (i32, TownColumn),
mut g: impl FnMut((&TownColumn, Vec3<i32>)) -> TownCell,
) -> Self {
let mut this = Self {
grid: Grid::new(
(0, TownColumn::default(), vec![TownCell::empty(); HEIGHT]),
size,
),
};
for (pos, (base, col, cells)) in this.grid.iter_mut() {
let column = f(pos);
*base = column.0;
*col = column.1;
for z in 0..HEIGHT {
cells[z] = g((
col,
Vec3::new(pos.x, pos.y, *base - UNDERGROUND_DEPTH + z as i32),
));
}
}
this
}
pub fn size(&self) -> Vec2<i32> {
self.grid.size()
}
pub fn set_col_kind(&mut self, pos: Vec2<i32>, kind: Option<ColumnKind>) {
self.grid.get_mut(pos).map(|col| col.1.kind = kind);
}
pub fn col(&self, pos: Vec2<i32>) -> Option<&TownColumn> {
self.grid.get(pos).map(|col| &col.1)
}
pub fn col_range(&self, pos: Vec2<i32>) -> Option<Range<i32>> {
self.grid.get(pos).map(|col| {
let lower = col.0 - UNDERGROUND_DEPTH;
lower..lower + HEIGHT as i32
})
}
pub fn choose_column(
&self,
rng: &mut impl Rng,
mut f: impl FnMut(Vec2<i32>, &TownColumn) -> bool,
) -> Option<Vec2<i32>> {
self.grid
.iter()
.filter(|(pos, col)| f(*pos, &col.1))
.choose(rng)
.map(|(pos, _)| pos)
}
pub fn choose_cell(
&self,
rng: &mut impl Rng,
mut f: impl FnMut(Vec3<i32>, &TownCell) -> bool,
) -> Option<Vec3<i32>> {
self.grid
.iter()
.map(|(pos, (base, _, cells))| {
cells.iter().enumerate().map(move |(i, cell)| {
(
Vec3::new(pos.x, pos.y, *base - UNDERGROUND_DEPTH + i as i32),
cell,
)
})
})
.flatten()
.filter(|(pos, cell)| f(*pos, *cell))
.choose(rng)
.map(|(pos, _)| pos)
}
}
impl BaseVol for TownVol {
type Vox = TownCell;
type Err = TownError;
}
impl ReadVol for TownVol {
fn get(&self, pos: Vec3<i32>) -> Result<&Self::Vox, Self::Err> {
match self.grid.get(Vec2::from(pos)) {
Some((base, _, cells)) => cells
.get((pos.z + UNDERGROUND_DEPTH - *base) as usize)
.ok_or(TownError::OutOfBounds),
None => Err(TownError::OutOfBounds),
}
}
}
impl WriteVol for TownVol {
fn set(&mut self, pos: Vec3<i32>, vox: Self::Vox) -> Result<(), Self::Err> {
match self.grid.get_mut(Vec2::from(pos)) {
Some((base, _, cells)) => cells
.get_mut((pos.z + UNDERGROUND_DEPTH - *base) as usize)
.map(|cell| *cell = vox)
.ok_or(TownError::OutOfBounds),
None => Err(TownError::OutOfBounds),
}
}
}

View File

@ -3,13 +3,15 @@
const_generics,
euclidean_division,
bind_by_move_pattern_guards,
option_flattening
option_flattening,
label_break_value
)]
mod all;
mod block;
mod column;
pub mod config;
pub mod generator;
pub mod sim;
pub mod util;
@ -56,11 +58,11 @@ impl World {
pub fn sample_columns(
&self,
) -> impl Sampler<Index = Vec2<i32>, Sample = Option<ColumnSample>> + '_ {
ColumnGen::new(self)
ColumnGen::new(&self.sim)
}
pub fn sample_blocks(&self) -> BlockGen {
BlockGen::new(self, ColumnGen::new(self))
BlockGen::new(self, ColumnGen::new(&self.sim))
}
pub fn generate_chunk(&self, chunk_pos: Vec2<i32>) -> (TerrainChunk, ChunkSupplement) {

View File

@ -11,7 +11,9 @@ use self::util::{
use crate::{
all::ForestKind,
util::{seed_expan, Sampler, StructureGen2d},
column::ColumnGen,
generator::TownState,
util::{seed_expan, FastNoise, Sampler, StructureGen2d},
CONFIG,
};
use common::{
@ -24,8 +26,10 @@ use noise::{
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use std::{
collections::HashMap,
f32,
ops::{Add, Div, Mul, Neg, Sub},
sync::Arc,
};
use vek::*;
@ -73,7 +77,7 @@ pub(crate) struct GenCtx {
pub small_nz: BasicMulti,
pub rock_nz: HybridMulti,
pub cliff_nz: HybridMulti,
pub warp_nz: BasicMulti,
pub warp_nz: FastNoise,
pub tree_nz: BasicMulti,
pub cave_0_nz: SuperSimplex,
@ -82,6 +86,11 @@ pub(crate) struct GenCtx {
pub structure_gen: StructureGen2d,
pub region_gen: StructureGen2d,
pub cliff_gen: StructureGen2d,
pub fast_turb_x_nz: FastNoise,
pub fast_turb_y_nz: FastNoise,
pub town_gen: StructureGen2d,
}
pub struct WorldSim {
@ -115,7 +124,7 @@ impl WorldSim {
small_nz: BasicMulti::new().set_octaves(2).set_seed(gen_seed()),
rock_nz: HybridMulti::new().set_persistence(0.3).set_seed(gen_seed()),
cliff_nz: HybridMulti::new().set_persistence(0.3).set_seed(gen_seed()),
warp_nz: BasicMulti::new().set_octaves(3).set_seed(gen_seed()),
warp_nz: FastNoise::new(gen_seed()), //BasicMulti::new().set_octaves(3).set_seed(gen_seed()),
tree_nz: BasicMulti::new()
.set_octaves(12)
.set_persistence(0.75)
@ -133,6 +142,11 @@ impl WorldSim {
// .set_octaves(6)
// .set_persistence(0.5)
.set_seed(gen_seed()),
fast_turb_x_nz: FastNoise::new(gen_seed()),
fast_turb_y_nz: FastNoise::new(gen_seed()),
town_gen: StructureGen2d::new(gen_seed(), 2048, 1024),
};
// "Base" of the chunk, to be multiplied by CONFIG.mountain_scale (multiplied value is
@ -202,7 +216,7 @@ impl WorldSim {
// maximal at 0. Also to be multiplied by CONFIG.mountain_scale.
let alt_main = (gen_ctx.alt_nz.get((wposf.div(2_000.0)).into_array()) as f32)
.abs()
.powf(1.45);
.powf(1.35);
(0.0 + alt_main
+ (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32)
@ -410,6 +424,40 @@ impl WorldSim {
}
}
// Stage 2 - towns!
let mut maybe_towns = HashMap::new();
for i in 0..WORLD_SIZE.x {
for j in 0..WORLD_SIZE.y {
let chunk_pos = Vec2::new(i as i32, j as i32);
let wpos = chunk_pos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
e * sz as i32 + sz as i32 / 2
});
let near_towns = self.gen_ctx.town_gen.get(wpos);
let town = near_towns
.iter()
.min_by_key(|(pos, seed)| wpos.distance_squared(*pos));
if let Some((pos, _)) = town {
let maybe_town = maybe_towns
.entry(*pos)
.or_insert_with(|| {
TownState::generate(*pos, &mut ColumnGen::new(self), &mut rng)
.map(|t| Arc::new(t))
})
.as_mut()
// Only care if we're close to the town
.filter(|town| {
Vec2::from(town.center()).distance_squared(wpos)
< town.radius().add(64).pow(2)
})
.cloned();
self.get_mut(chunk_pos).unwrap().structures.town = maybe_town;
}
}
}
self.rng = rng;
self.locations = locations;
}
@ -425,6 +473,12 @@ impl WorldSim {
}
}
pub fn get_wpos(&self, wpos: Vec2<i32>) -> Option<&SimChunk> {
self.get(wpos.map2(Vec2::from(TerrainChunkSize::SIZE), |e, sz: u32| {
e / sz as i32
}))
}
pub fn get_mut(&mut self, chunk_pos: Vec2<i32>) -> Option<&mut SimChunk> {
if chunk_pos
.map2(WORLD_SIZE, |e, sz| e >= 0 && e < sz as i32)
@ -491,7 +545,6 @@ pub struct SimChunk {
pub alt_base: f32,
pub alt: f32,
pub temp: f32,
pub dryness: f32,
pub humidity: f32,
pub rockiness: f32,
pub is_cliffs: bool,
@ -500,6 +553,8 @@ pub struct SimChunk {
pub forest_kind: ForestKind,
pub spawn_rate: f32,
pub location: Option<LocationInfo>,
pub structures: Structures,
}
#[derive(Copy, Clone)]
@ -516,27 +571,16 @@ pub struct LocationInfo {
pub near: Vec<RegionInfo>,
}
#[derive(Clone)]
pub struct Structures {
pub town: Option<Arc<TownState>>,
}
impl SimChunk {
fn generate(posi: usize, gen_ctx: &mut GenCtx, gen_cdf: &GenCdf) -> Self {
let pos = uniform_idx_as_vec2(posi);
let wposf = (pos * TerrainChunkSize::SIZE.map(|e| e as i32)).map(|e| e as f64);
// FIXME: Currently unused, but should represent fresh groundwater level.
// Should be correlated a little with humidity, somewhat negatively with altitude,
// and very negatively with difference in temperature from zero.
let dryness = gen_ctx.dry_nz.get(
(wposf
.add(Vec2::new(
gen_ctx
.dry_nz
.get((wposf.add(10000.0).div(500.0)).into_array())
* 150.0,
gen_ctx.dry_nz.get((wposf.add(0.0).div(500.0)).into_array()) * 150.0,
))
.div(2_000.0))
.into_array(),
) as f32;
let (_, alt_base) = gen_cdf.alt_base[posi];
let map_edge_factor = map_edge_factor(posi);
let (_, chaos) = gen_cdf.chaos[posi];
@ -606,17 +650,13 @@ impl SimChunk {
alt_base,
alt,
temp,
dryness,
humidity,
rockiness: (gen_ctx.rock_nz.get((wposf.div(1024.0)).into_array()) as f32)
.sub(0.1)
.mul(1.3)
.max(0.0),
is_cliffs: cliff > 0.5
&& dryness > 0.05
&& alt > CONFIG.sea_level + 5.0
&& dryness.abs() > 0.075,
near_cliffs: cliff > 0.25,
is_cliffs: cliff > 0.5 && alt > CONFIG.sea_level + 5.0,
near_cliffs: cliff > 0.2,
tree_density,
forest_kind: if temp > 0.0 {
if temp > CONFIG.desert_temp {
@ -678,6 +718,8 @@ impl SimChunk {
},
spawn_rate: 1.0,
location: None,
structures: Structures { town: None },
}
}

View File

@ -0,0 +1,49 @@
use super::{RandomField, Sampler};
use std::f32;
use vek::*;
pub struct FastNoise {
noise: RandomField,
}
impl FastNoise {
pub const fn new(seed: u32) -> Self {
Self {
noise: RandomField::new(seed),
}
}
fn noise_at(&self, pos: Vec3<i32>) -> f32 {
(self.noise.get(pos) % 4096) as f32 / 4096.0
}
}
impl Sampler<'static> for FastNoise {
type Index = Vec3<f64>;
type Sample = f32;
fn get(&self, pos: Self::Index) -> Self::Sample {
let near_pos = pos.map(|e| e.floor() as i32);
let v000 = self.noise_at(near_pos + Vec3::new(0, 0, 0));
let v100 = self.noise_at(near_pos + Vec3::new(1, 0, 0));
let v010 = self.noise_at(near_pos + Vec3::new(0, 1, 0));
let v110 = self.noise_at(near_pos + Vec3::new(1, 1, 0));
let v001 = self.noise_at(near_pos + Vec3::new(0, 0, 1));
let v101 = self.noise_at(near_pos + Vec3::new(1, 0, 1));
let v011 = self.noise_at(near_pos + Vec3::new(0, 1, 1));
let v111 = self.noise_at(near_pos + Vec3::new(1, 1, 1));
let factor = pos.map(|e| 0.5 - (e.fract() as f32 * f32::consts::PI).cos() * 0.5);
let x00 = Lerp::lerp(v000, v100, factor.x);
let x10 = Lerp::lerp(v010, v110, factor.x);
let x01 = Lerp::lerp(v001, v101, factor.x);
let x11 = Lerp::lerp(v011, v111, factor.x);
let y0 = Lerp::lerp(x00, x10, factor.y);
let y1 = Lerp::lerp(x01, x11, factor.y);
Lerp::lerp(y0, y1, factor.z) * 2.0 - 1.0
}
}

74
world/src/util/grid.rs Normal file
View File

@ -0,0 +1,74 @@
use vek::*;
pub struct Grid<T> {
cells: Vec<T>,
size: Vec2<i32>,
}
impl<T: Clone> Grid<T> {
pub fn new(default_cell: T, size: Vec2<i32>) -> Self {
Self {
cells: vec![default_cell; size.product() as usize],
size,
}
}
fn idx(&self, pos: Vec2<i32>) -> Option<usize> {
if pos.map2(self.size, |e, sz| e >= 0 && e < sz).reduce_and() {
Some((pos.y * self.size.x + pos.x) as usize)
} else {
None
}
}
pub fn size(&self) -> Vec2<i32> {
self.size
}
pub fn get(&self, pos: Vec2<i32>) -> Option<&T> {
self.cells.get(self.idx(pos)?)
}
pub fn get_mut(&mut self, pos: Vec2<i32>) -> Option<&mut T> {
let idx = self.idx(pos)?;
self.cells.get_mut(idx)
}
pub fn set(&mut self, pos: Vec2<i32>, cell: T) -> Option<()> {
let idx = self.idx(pos)?;
self.cells.get_mut(idx).map(|c| *c = cell)
}
pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &T)> + '_ {
let w = self.size.x;
self.cells
.iter()
.enumerate()
.map(move |(i, cell)| (Vec2::new(i as i32 % w, i as i32 / w), cell))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Vec2<i32>, &mut T)> + '_ {
let w = self.size.x;
self.cells
.iter_mut()
.enumerate()
.map(move |(i, cell)| (Vec2::new(i as i32 % w, i as i32 / w), cell))
}
pub fn iter_area(
&self,
pos: Vec2<i32>,
size: Vec2<i32>,
) -> impl Iterator<Item = Option<(Vec2<i32>, &T)>> + '_ {
(0..size.x)
.map(move |x| {
(0..size.y).map(move |y| {
Some((
pos + Vec2::new(x, y),
&self.cells[self.idx(pos + Vec2::new(x, y))?],
))
})
})
.flatten()
}
}

View File

@ -1,3 +1,5 @@
pub mod fast_noise;
pub mod grid;
pub mod hash_cache;
pub mod random;
pub mod sampler;
@ -7,6 +9,8 @@ pub mod unit_chooser;
// Reexports
pub use self::{
fast_noise::FastNoise,
grid::Grid,
hash_cache::HashCache,
random::{RandomField, RandomPerm},
sampler::{Sampler, SamplerMut},

View File

@ -11,26 +11,26 @@ impl RandomField {
}
}
impl Sampler for RandomField {
impl Sampler<'static> for RandomField {
type Index = Vec3<i32>;
type Sample = u32;
fn get(&self, pos: Self::Index) -> Self::Sample {
let pos = pos.map(|e| (e * 13 + (1 << 31)) as u32);
let pos = pos.map(|e| u32::from_le_bytes(e.to_le_bytes()));
let mut a = self.seed;
a = (a ^ 61) ^ (a >> 16);
a = a + (a << 3);
a = a.wrapping_add(a << 3);
a = a ^ pos.x;
a = a ^ (a >> 4);
a = a * 0x27d4eb2d;
a = a.wrapping_mul(0x27d4eb2d);
a = a ^ (a >> 15);
a = a ^ pos.y;
a = (a ^ 61) ^ (a >> 16);
a = a + (a << 3);
a = a.wrapping_add(a << 3);
a = a ^ (a >> 4);
a = a ^ pos.z;
a = a * 0x27d4eb2d;
a = a.wrapping_mul(0x27d4eb2d);
a = a ^ (a >> 15);
a
}
@ -46,17 +46,19 @@ impl RandomPerm {
}
}
impl Sampler for RandomPerm {
impl Sampler<'static> 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
let a = self
.seed
.wrapping_mul(3471)
.wrapping_add(perm)
.wrapping_add(0x3BE7172B)
.wrapping_mul(perm)
.wrapping_add(0x172A3BE1);
let b = a.wrapping_mul(a);
b ^ (a >> 17) ^ b >> 15
}
}

View File

@ -1,13 +1,13 @@
pub trait Sampler: Sized {
type Index;
type Sample;
pub trait Sampler<'a>: Sized {
type Index: 'a;
type Sample: 'a;
fn get(&self, index: Self::Index) -> Self::Sample;
}
pub trait SamplerMut: Sized {
type Index;
type Sample;
pub trait SamplerMut<'a>: Sized {
type Index: 'a;
type Sample: 'a;
fn get(&mut self, index: Self::Index) -> Self::Sample;
}

View File

@ -21,7 +21,7 @@ impl StructureGen2d {
}
}
impl Sampler for StructureGen2d {
impl Sampler<'static> for StructureGen2d {
type Index = Vec2<i32>;
type Sample = [(Vec2<i32>, u32); 9];

View File

@ -24,7 +24,7 @@ impl UnitChooser {
}
}
impl Sampler for UnitChooser {
impl Sampler<'static> for UnitChooser {
type Index = u32;
type Sample = (Vec2<i32>, Vec2<i32>);