mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'zesterer/worldgen' into 'master'
Worldgen improvements See merge request veloren/veloren!457
This commit is contained in:
commit
6919886475
@ -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);
|
||||
|
@ -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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
BIN
assets/world/module/wall/single_top.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/world/structure/human/mage_tower.vox
(Stored with Git LFS)
BIN
assets/world/structure/human/mage_tower.vox
(Stored with Git LFS)
Binary file not shown.
1
server-cli/.gitignore
vendored
Normal file
1
server-cli/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
settings.ron
|
@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)]
|
||||
|
34
world/src/generator/mod.rs
Normal file
34
world/src/generator/mod.rs
Normal 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;
|
||||
}
|
655
world/src/generator/town/mod.rs
Normal file
655
world/src/generator/town/mod.rs
Normal 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]),
|
||||
]
|
||||
};
|
||||
}
|
42
world/src/generator/town/util.rs
Normal file
42
world/src/generator/town/util.rs
Normal 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)]
|
||||
}
|
227
world/src/generator/town/vol.rs
Normal file
227
world/src/generator/town/vol.rs
Normal 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),
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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 },
|
||||
}
|
||||
}
|
||||
|
||||
|
49
world/src/util/fast_noise.rs
Normal file
49
world/src/util/fast_noise.rs
Normal 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
74
world/src/util/grid.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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},
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
|
||||
|
@ -24,7 +24,7 @@ impl UnitChooser {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler for UnitChooser {
|
||||
impl Sampler<'static> for UnitChooser {
|
||||
type Index = u32;
|
||||
type Sample = (Vec2<i32>, Vec2<i32>);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user