Initial settlement generation work

This commit is contained in:
Joshua Barretto 2020-02-06 22:32:26 +00:00
parent ecf0b90da2
commit 944a37b848
10 changed files with 720 additions and 40 deletions

View File

@ -34,6 +34,15 @@ pub enum PathResult<T> {
Pending,
}
impl<T> PathResult<T> {
pub fn into_path(self) -> Option<Path<T>> {
match self {
PathResult::Path(path) => Some(path),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct Astar<S: Clone + Eq + Hash> {
iter: usize,

View File

@ -25,6 +25,7 @@ pub mod npc;
pub mod path;
pub mod ray;
pub mod region;
pub mod spiral;
pub mod state;
pub mod states;
pub mod sync;

View File

@ -33,6 +33,8 @@ impl<T> FromIterator<T> for Path<T> {
impl<T> Path<T> {
pub fn len(&self) -> usize { self.nodes.len() }
pub fn iter(&self) -> impl Iterator<Item = &T> { self.nodes.iter() }
pub fn start(&self) -> Option<&T> { self.nodes.first() }
pub fn end(&self) -> Option<&T> { self.nodes.last() }

37
common/src/spiral.rs Normal file
View File

@ -0,0 +1,37 @@
use vek::*;
/// An iterator of coordinates that create a rectangular spiral out from the
/// origin
#[derive(Clone)]
pub struct Spiral2d {
layer: i32,
i: i32,
}
impl Spiral2d {
pub fn new() -> Self { Self { layer: 0, i: 0 } }
}
impl Iterator for Spiral2d {
type Item = Vec2<i32>;
fn next(&mut self) -> Option<Self::Item> {
let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1);
if self.i >= layer_size {
self.layer += 1;
self.i = 0;
}
let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1);
let pos = Vec2::new(
-self.layer + (self.i - (layer_size / 4) * 0).max(0).min(self.layer * 2)
- (self.i - (layer_size / 4) * 2).max(0).min(self.layer * 2),
-self.layer + (self.i - (layer_size / 4) * 1).max(0).min(self.layer * 2)
- (self.i - (layer_size / 4) * 3).max(0).min(self.layer * 2),
);
self.i += 1;
Some(pos)
}
}

View File

@ -10,6 +10,7 @@ use super::SceneData;
use common::{
assets,
figure::Segment,
spiral::Spiral2D,
terrain::{Block, BlockKind, TerrainChunk},
vol::{BaseVol, ReadVol, RectRasterableVol, SampleVol, Vox},
volumes::vol_grid_2d::{VolGrid2d, VolGrid2dError},
@ -1487,37 +1488,3 @@ impl<V: RectRasterableVol> Terrain<V> {
});
}
}
#[derive(Clone)]
struct Spiral2d {
layer: i32,
i: i32,
}
impl Spiral2d {
pub fn new() -> Self { Self { layer: 0, i: 0 } }
}
impl Iterator for Spiral2d {
type Item = Vec2<i32>;
fn next(&mut self) -> Option<Self::Item> {
let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1);
if self.i >= layer_size {
self.layer += 1;
self.i = 0;
}
let layer_size = (self.layer * 8 + 4 * self.layer.min(1) - 4).max(1);
let pos = Vec2::new(
-self.layer + (self.i - (layer_size / 4) * 0).max(0).min(self.layer * 2)
- (self.i - (layer_size / 4) * 2).max(0).min(self.layer * 2),
-self.layer + (self.i - (layer_size / 4) * 1).max(0).min(self.layer * 2)
- (self.i - (layer_size / 4) * 3).max(0).min(self.layer * 2),
);
self.i += 1;
Some(pos)
}
}

View File

@ -0,0 +1,55 @@
use rand::thread_rng;
use vek::*;
use veloren_world::generator::settlement::Settlement;
const W: usize = 640;
const H: usize = 480;
fn main() {
let mut win =
minifb::Window::new("Settlement Viewer", W, H, minifb::WindowOptions::default()).unwrap();
let settlement = Settlement::generate(&mut thread_rng());
let mut focus = Vec2::<f32>::zero();
let mut zoom = 1.0;
while win.is_open() {
let mut buf = vec![0; W * H];
let win_to_pos =
|wp: Vec2<usize>| (wp.map(|e| e as f32) - Vec2::new(W as f32, H as f32) * 0.5) * zoom;
for i in 0..W {
for j in 0..H {
let pos = focus + win_to_pos(Vec2::new(i, j)) * zoom;
let color = settlement.get_color(pos);
buf[j * W + i] = u32::from_le_bytes([color.b, color.g, color.r, 255]);
}
}
let spd = 20.0;
if win.is_key_down(minifb::Key::W) {
focus.y -= spd * zoom;
}
if win.is_key_down(minifb::Key::A) {
focus.x -= spd * zoom;
}
if win.is_key_down(minifb::Key::S) {
focus.y += spd * zoom;
}
if win.is_key_down(minifb::Key::D) {
focus.x += spd * zoom;
}
if win.is_key_down(minifb::Key::Q) {
zoom *= 1.05;
}
if win.is_key_down(minifb::Key::E) {
zoom /= 1.05;
}
win.update_with_buffer(&buf).unwrap();
}
}

View File

@ -1,3 +1,4 @@
pub mod settlement;
mod town;
// Reexports

View File

@ -0,0 +1,604 @@
use crate::util::{Sampler, StructureGen2d};
use common::{astar::Astar, path::Path, spiral::Spiral2d};
use hashbrown::{HashMap, HashSet};
use rand::prelude::*;
use std::{collections::VecDeque, f32, marker::PhantomData};
use vek::*;
pub fn gradient(line: [Vec2<f32>; 2]) -> f32 {
let r = (line[0].y - line[1].y) / (line[0].x - line[1].x);
if r.is_nan() { 100000.0 } else { r }
}
pub fn intersect(a: [Vec2<f32>; 2], b: [Vec2<f32>; 2]) -> Option<Vec2<f32>> {
let ma = gradient(a);
let mb = gradient(b);
let ca = a[0].y - ma * a[0].x;
let cb = b[0].y - mb * b[0].x;
if (ma - mb).abs() < 0.0001 || (ca - cb).abs() < 0.0001 {
None
} else {
let x = (cb - ca) / (ma - mb);
let y = ma * x + ca;
Some(Vec2::new(x, y))
}
}
pub fn dist_to_line(line: [Vec2<f32>; 2], p: Vec2<f32>) -> f32 {
let lsq = line[0].distance_squared(line[1]);
if lsq == 0.0 {
line[0].distance(p)
} else {
let t = ((p - line[0]).dot(line[1] - line[0]) / lsq)
.max(0.0)
.min(1.0);
p.distance(line[0] + (line[1] - line[0]) * t)
}
}
pub fn center_of(p: [Vec2<f32>; 3]) -> Vec2<f32> {
let ma = -1.0 / gradient([p[0], p[1]]);
let mb = -1.0 / gradient([p[1], p[2]]);
let pa = (p[0] + p[1]) * 0.5;
let pb = (p[1] + p[2]) * 0.5;
let ca = pa.y - ma * pa.x;
let cb = pb.y - mb * pb.x;
let x = (cb - ca) / (ma - mb);
let y = ma * x + ca;
Vec2::new(x, y)
}
const AREA_SIZE: u32 = 32;
fn to_tile(e: i32) -> i32 { ((e as f32).div_euclid(AREA_SIZE as f32)).floor() as i32 }
pub enum StructureKind {
House,
}
pub struct Structure {
kind: StructureKind,
bounds: Aabr<i32>,
}
pub struct Settlement {
land: Land,
farms: Store<Farm>,
structures: Vec<Structure>,
town: Option<Town>,
}
pub struct Town {
base_tile: Vec2<i32>,
}
pub struct Farm {
base_tile: Vec2<i32>,
}
impl Settlement {
pub fn generate(rng: &mut impl Rng) -> Self {
let mut this = Self {
land: Land::new(rng),
farms: Store::default(),
structures: Vec::new(),
town: None,
};
this.place_river(rng);
this.place_farms(rng);
this.place_town(rng);
this.place_paths(rng);
this
}
pub fn place_river(&mut self, rng: &mut impl Rng) {
let river_dir = Vec2::new(rng.gen::<f32>() - 0.5, rng.gen::<f32>() - 0.5).normalized();
let radius = 500.0 + rng.gen::<f32>().powf(2.0) * 1000.0;
let river = self.land.new_plot(Plot::Water);
for theta in (0..500).map(|x| (x as f32) * f32::consts::PI / 250.0) {
let pos = river_dir * radius + Vec2::new(theta.sin(), theta.cos()) * radius;
if pos.magnitude() > 300.0 {
continue;
}
for dir in CARDINALS.iter() {
self.land.set(
pos.map2(*dir, |e, d| e.floor() as i32 + d * 12)
.map(to_tile),
river,
);
}
}
}
pub fn place_paths(&mut self, rng: &mut impl Rng) {
let mut dir = Vec2::zero();
for _ in 0..6 {
dir = (Vec2::new(rng.gen::<f32>() - 0.5, rng.gen::<f32>() - 0.5) * 2.0 - dir)
.try_normalized()
.unwrap_or(Vec2::zero());
let origin = dir.map(|e| (e * 20.0) as i32);
let origin = self
.land
.find_tile_near(origin, |plot| match plot {
Some(&Plot::Field { .. }) => true,
_ => false,
})
.unwrap();
if let Some(path) = self.town.as_ref().and_then(|town| {
self.land
.find_path(origin, town.base_tile, |from, to| match (from, to) {
(_, Some(b)) if self.land.plot(b.plot) == &Plot::Dirt => 0.0,
(_, Some(b)) if self.land.plot(b.plot) == &Plot::Water => 20.0,
(Some(a), Some(b)) if a.contains(WayKind::Wall) => {
if b.contains(WayKind::Wall) {
1000.0
} else {
10.0
}
},
(Some(_), Some(_)) => 1.0,
_ => 1000.0,
})
}) {
let path = path.iter().copied().collect::<Vec<_>>();
self.land.write_path(&path, WayKind::Path, |_| true, true);
}
}
}
pub fn place_town(&mut self, rng: &mut impl Rng) {
let mut origin = Vec2::new(rng.gen_range(-2, 3), rng.gen_range(-2, 3));
let town = self.land.new_plot(Plot::Town);
for i in 0..4 {
if let Some(base_tile) = self.land.find_tile_near(origin, |plot| match plot {
Some(Plot::Field { .. }) => true,
Some(Plot::Dirt) => true,
_ => false,
}) {
self.land.set(base_tile, town);
if i == 0 {
for dir in CARDINALS.iter() {
self.land.set(base_tile + *dir, town);
}
self.town = Some(Town { base_tile });
origin = base_tile;
}
}
}
// Border wall
let spokes = CARDINALS
.iter()
.filter_map(|dir| {
self.land.find_tile_dir(origin, *dir, |plot| match plot {
Some(Plot::Town) => false,
_ => true,
})
})
.collect::<Vec<_>>();
let mut wall_path = Vec::new();
for i in 0..spokes.len() {
self.land
.find_path(spokes[i], spokes[(i + 1) % spokes.len()], |_, to| match to
.map(|to| self.land.plot(to.plot))
{
Some(Plot::Town) => 1000.0,
_ => 1.0,
})
.map(|path| wall_path.extend(path.iter().copied()));
}
let grass = self.land.new_plot(Plot::Grass);
for pos in wall_path.iter() {
if self.land.tile_at(*pos).is_none() {
self.land.set(*pos, grass);
}
}
wall_path.push(wall_path[0]);
self.land.write_path(
&wall_path,
WayKind::Wall,
|plot| match plot {
Plot::Water => false,
_ => true,
},
true,
);
}
pub fn place_farms(&mut self, rng: &mut impl Rng) {
for _ in 0..6 {
if let Some(base_tile) = self
.land
.find_tile_near(Vec2::zero(), |plot| plot.is_none())
{
// Farm
let farmhouse = self.land.new_plot(Plot::Dirt);
self.land.set(base_tile, farmhouse);
// Farmhouses
for _ in 0..rng.gen_range(1, 4) {
let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2)
+ Vec2::new(rng.gen_range(-16, 16), rng.gen_range(-16, 16));
self.structures.push(Structure {
kind: StructureKind::House,
bounds: Aabr {
min: house_pos - Vec2::new(rng.gen_range(4, 6), rng.gen_range(4, 6)),
max: house_pos + Vec2::new(rng.gen_range(4, 6), rng.gen_range(4, 6)),
},
});
}
// Fields
let farmland = self.farms.insert(Farm { base_tile });
for _ in 0..5 {
self.place_field(farmland, base_tile, rng);
}
}
}
}
pub fn place_field(
&mut self,
farm: Id<Farm>,
origin: Vec2<i32>,
rng: &mut impl Rng,
) -> Option<Id<Plot>> {
let max_size = 7;
if let Some(center) = self.land.find_tile_near(origin, |plot| plot.is_none()) {
let field = self.land.new_plot(Plot::Field {
farm,
seed: rng.gen(),
});
let tiles = self
.land
.grow_from(center, rng.gen_range(1, max_size), rng, |plot| {
plot.is_none()
});
for pos in tiles.into_iter() {
self.land.set(pos, field);
}
Some(field)
} else {
None
}
}
pub fn get_color(&self, pos: Vec2<f32>) -> Rgb<u8> {
let pos = pos.map(|e| e.floor() as i32);
if let Some(structure) = self
.structures
.iter()
.find(|s| s.bounds.contains_point(pos))
{
return match structure.kind {
StructureKind::House => Rgb::new(200, 80, 50),
};
}
match self.land.get_at_block(pos) {
Sample::Wilderness => Rgb::zero(),
Sample::Way(WayKind::Path) => Rgb::new(130, 100, 0),
Sample::Way(WayKind::Hedge) => Rgb::new(0, 150, 0),
Sample::Way(WayKind::Wall) => Rgb::new(60, 60, 60),
Sample::Plot(Plot::Dirt) => Rgb::new(130, 100, 0),
Sample::Plot(Plot::Grass) => Rgb::new(100, 200, 0),
Sample::Plot(Plot::Water) => Rgb::new(100, 150, 250),
Sample::Plot(Plot::Town) => {
if pos.map(|e| e.rem_euclid(4) < 2).reduce(|x, y| x ^ y) {
Rgb::new(200, 130, 120)
} else {
Rgb::new(160, 150, 120)
}
},
Sample::Plot(Plot::Field { seed, .. }) => {
let furrow_dirs = [
Vec2::new(1, 0),
Vec2::new(0, 1),
Vec2::new(1, 1),
Vec2::new(-1, 1),
];
let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()];
let furrow = (pos * furrow_dir).sum().rem_euclid(4) < 2;
Rgb::new(
if furrow {
150
} else {
48 + seed.to_le_bytes()[0] % 64
},
128 + seed.to_le_bytes()[1] % 128,
16 + seed.to_le_bytes()[2] % 32,
)
},
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum Plot {
Dirt,
Grass,
Water,
Town,
Field { farm: Id<Farm>, seed: u32 },
}
const CARDINALS: [Vec2<i32>; 4] = [
Vec2::new(0, 1),
Vec2::new(1, 0),
Vec2::new(0, -1),
Vec2::new(-1, 0),
];
#[derive(Copy, Clone, PartialEq)]
pub enum WayKind {
Path,
Hedge,
Wall,
}
pub struct Tile {
plot: Id<Plot>,
ways: [Option<WayKind>; 4],
}
impl Tile {
pub fn contains(&self, kind: WayKind) -> bool { self.ways.iter().any(|way| way == &Some(kind)) }
}
pub enum Sample<'a> {
Wilderness,
Plot(&'a Plot),
Way(&'a WayKind),
}
pub struct Land {
tiles: HashMap<Vec2<i32>, Tile>,
plots: Store<Plot>,
sampler_warp: StructureGen2d,
}
impl Land {
pub fn new(rng: &mut impl Rng) -> Self {
Self {
tiles: HashMap::new(),
plots: Store::default(),
sampler_warp: StructureGen2d::new(rng.gen(), AREA_SIZE, 12),
}
}
pub fn get_at_block(&self, pos: Vec2<i32>) -> Sample {
let neighbors = self.sampler_warp.get(pos);
let closest = neighbors
.iter()
.min_by_key(|(center, _)| center.distance_squared(pos))
.unwrap()
.0;
let map = [1, 5, 7, 3];
for (i, dir) in CARDINALS.iter().enumerate() {
let line = [
neighbors[4].0.map(|e| e as f32),
neighbors[map[i]].0.map(|e| e as f32),
];
if dist_to_line(line, pos.map(|e| e as f32)) < 1.5 {
if let Some(way) = self
.tile_at(neighbors[4].0.map(to_tile))
.and_then(|tile| tile.ways[i].as_ref())
{
return Sample::Way(way);
}
}
}
let plot = self.plot_at(closest.map(to_tile));
plot.map(|plot| Sample::Plot(plot))
.unwrap_or(Sample::Wilderness)
}
pub fn tile_at(&self, pos: Vec2<i32>) -> Option<&Tile> { self.tiles.get(&pos) }
pub fn tile_at_mut(&mut self, pos: Vec2<i32>) -> Option<&mut Tile> { self.tiles.get_mut(&pos) }
pub fn plot(&self, id: Id<Plot>) -> &Plot { self.plots.get(id) }
pub fn plot_at(&self, pos: Vec2<i32>) -> Option<&Plot> {
self.tiles.get(&pos).map(|tile| self.plots.get(tile.plot))
}
pub fn plot_at_mut(&mut self, pos: Vec2<i32>) -> Option<&mut Plot> {
self.tiles
.get(&pos)
.map(|tile| tile.plot)
.map(move |plot| self.plots.get_mut(plot))
}
pub fn set(&mut self, pos: Vec2<i32>, plot: Id<Plot>) {
self.tiles.insert(pos, Tile {
plot,
ways: [None; 4],
});
}
fn find_tile_near(
&self,
origin: Vec2<i32>,
mut match_fn: impl FnMut(Option<&Plot>) -> bool,
) -> Option<Vec2<i32>> {
Spiral2d::new()
.map(|pos| origin + pos)
.find(|pos| match_fn(self.plot_at(*pos)))
}
fn find_tile_dir(
&self,
origin: Vec2<i32>,
dir: Vec2<i32>,
mut match_fn: impl FnMut(Option<&Plot>) -> bool,
) -> Option<Vec2<i32>> {
(0..)
.map(|i| origin + dir * i)
.find(|pos| match_fn(self.plot_at(*pos)))
}
fn find_path(
&self,
origin: Vec2<i32>,
dest: Vec2<i32>,
mut path_cost_fn: impl FnMut(Option<&Tile>, Option<&Tile>) -> f32,
) -> Option<Path<Vec2<i32>>> {
let heuristic = |pos: &Vec2<i32>| pos.distance_squared(dest) as f32;
let neighbors = |pos: &Vec2<i32>| {
let pos = *pos;
CARDINALS.iter().map(move |dir| pos + *dir)
};
let transition =
|from: &Vec2<i32>, to: &Vec2<i32>| path_cost_fn(self.tile_at(*from), self.tile_at(*to));
let satisfied = |pos: &Vec2<i32>| *pos == dest;
Astar::new(250, origin, heuristic)
.poll(250, heuristic, neighbors, transition, satisfied)
.into_path()
}
fn grow_from(
&self,
start: Vec2<i32>,
max_size: usize,
rng: &mut impl Rng,
mut match_fn: impl FnMut(Option<&Plot>) -> bool,
) -> HashSet<Vec2<i32>> {
let mut open = VecDeque::new();
open.push_back(start);
let mut closed = HashSet::new();
while open.len() + closed.len() < max_size {
let next_pos = if let Some(next_pos) = open.pop_front() {
closed.insert(next_pos);
next_pos
} else {
break;
};
let dirs = [
Vec2::new(1, 0),
Vec2::new(-1, 0),
Vec2::new(0, 1),
Vec2::new(0, -1),
];
for dir in dirs.iter() {
let neighbor = next_pos + dir;
if !closed.contains(&neighbor) && match_fn(self.plot_at(neighbor)) {
open.push_back(neighbor);
}
}
}
closed.into_iter().chain(open.into_iter()).collect()
}
fn write_path(
&mut self,
tiles: &[Vec2<i32>],
kind: WayKind,
mut permit_fn: impl FnMut(&Plot) -> bool,
overwrite: bool,
) {
for tiles in tiles.windows(2) {
let dir = tiles[1] - tiles[0];
let idx = if dir.y > 0 {
1
} else if dir.x > 0 {
2
} else if dir.y < 0 {
3
} else if dir.x < 0 {
0
} else {
continue;
};
let mut plots = &self.plots;
self.tiles
.get_mut(&tiles[1])
.filter(|tile| permit_fn(plots.get(tile.plot)))
.map(|tile| {
if overwrite || tile.ways[(idx + 2) % 4].is_none() {
tile.ways[(idx + 2) % 4] = Some(kind);
}
});
self.tiles
.get_mut(&tiles[0])
.filter(|tile| permit_fn(plots.get(tile.plot)))
.map(|tile| {
if overwrite || tile.ways[idx].is_none() {
tile.ways[idx] = Some(kind);
}
});
}
}
pub fn new_plot(&mut self, plot: Plot) -> Id<Plot> { self.plots.insert(plot) }
}
#[derive(Hash)]
pub struct Id<T>(usize, PhantomData<T>);
impl<T> Copy for Id<T> {}
impl<T> Clone for Id<T> {
fn clone(&self) -> Self { Self(self.0, PhantomData) }
}
impl<T> Eq for Id<T> {}
impl<T> PartialEq for Id<T> {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
pub struct Store<T> {
items: HashMap<usize, T>,
id_counter: usize,
}
impl<T> Default for Store<T> {
fn default() -> Self {
Self {
items: HashMap::new(),
id_counter: 0,
}
}
}
impl<T> Store<T> {
pub fn get(&self, id: Id<T>) -> &T { self.items.get(&id.0).unwrap() }
pub fn get_mut(&mut self, id: Id<T>) -> &mut T { self.items.get_mut(&id.0).unwrap() }
pub fn iter(&self) -> impl Iterator<Item = &T> { self.items.values() }
pub fn insert(&mut self, item: T) -> Id<T> {
self.id_counter += 1;
let id = Id(self.id_counter, PhantomData);
self.items.insert(id.0, item);
id
}
}

View File

@ -65,8 +65,8 @@ use vek::*;
// don't think we actually cast a chunk id to float, just coordinates... could
// be wrong though!
pub const WORLD_SIZE: Vec2<usize> = Vec2 {
x: 1024 * 1,
y: 1024 * 1,
x: 256 * 1,
y: 256 * 1,
};
/// A structure that holds cached noise values and cumulative distribution

View File

@ -54,10 +54,14 @@ impl StructureGen2d {
let pos = Vec3::from(center);
(
center
+ Vec2::new(
+ if spread_mul > 0 {
Vec2::new(
(x_field.get(pos) % spread_mul) as i32 - spread,
(y_field.get(pos) % spread_mul) as i32 - spread,
),
)
} else {
Vec2::zero()
},
seed_field.get(pos),
)
}