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 8d0c91dc94
46 changed files with 1268 additions and 94 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);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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>);