mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added modular building generation
This commit is contained in:
parent
99c5171258
commit
5a61d563d8
@ -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
Normal file
BIN
assets/world/module/human/balcony_upstairs.vox
Normal file
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.
BIN
assets/world/module/human/chimney_roof.vox
Normal file
BIN
assets/world/module/human/chimney_roof.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/corner_ground.vox
Normal file
BIN
assets/world/module/human/corner_ground.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/corner_roof.vox
Normal file
BIN
assets/world/module/human/corner_roof.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/corner_upstairs.vox
Normal file
BIN
assets/world/module/human/corner_upstairs.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/door_big.vox
Normal file
BIN
assets/world/module/human/door_big.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/door_ground.vox
Normal file
BIN
assets/world/module/human/door_ground.vox
Normal file
Binary file not shown.
Binary file not shown.
BIN
assets/world/module/human/floor_ground.vox
Normal file
BIN
assets/world/module/human/floor_ground.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/floor_roof.vox
Normal file
BIN
assets/world/module/human/floor_roof.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/floor_upstairs.vox
Normal file
BIN
assets/world/module/human/floor_upstairs.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/stair_ground.vox
Normal file
BIN
assets/world/module/human/stair_ground.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/wall_ground.vox
Normal file
BIN
assets/world/module/human/wall_ground.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/wall_roof.vox
Normal file
BIN
assets/world/module/human/wall_roof.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/wall_upstairs.vox
Normal file
BIN
assets/world/module/human/wall_upstairs.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/window_ground.vox
Normal file
BIN
assets/world/module/human/window_ground.vox
Normal file
Binary file not shown.
BIN
assets/world/module/human/window_upstairs.vox
Normal file
BIN
assets/world/module/human/window_upstairs.vox
Normal file
Binary file not shown.
1
server-cli/.gitignore
vendored
Normal file
1
server-cli/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
settings.ron
|
@ -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,6 +1,7 @@
|
||||
use crate::{
|
||||
all::ForestKind,
|
||||
block::StructureMeta,
|
||||
generator::{Generator, SpawnRules, TownGen},
|
||||
sim::{LocationInfo, SimChunk, WorldSim},
|
||||
util::{RandomPerm, Sampler, UnitChooser},
|
||||
World, CONFIG,
|
||||
@ -518,6 +519,12 @@ impl<'a> Sampler<'a> for ColumnGen<'a> {
|
||||
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()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -547,6 +554,7 @@ pub struct ColumnSample<'a> {
|
||||
pub location: Option<&'a LocationInfo>,
|
||||
|
||||
pub chunk: &'a SimChunk,
|
||||
pub spawn_rules: SpawnRules,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -7,8 +7,28 @@ 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;
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
use super::Generator;
|
||||
mod util;
|
||||
mod vol;
|
||||
|
||||
use super::{Generator, SpawnRules};
|
||||
use crate::{
|
||||
block::block_from_structure,
|
||||
column::{ColumnGen, ColumnSample},
|
||||
@ -8,14 +11,617 @@ use crate::{
|
||||
use common::{
|
||||
assets,
|
||||
terrain::{Block, BlockKind, Structure},
|
||||
vol::{ReadVol, Vox},
|
||||
vol::{ReadVol, Vox, WriteVol},
|
||||
};
|
||||
use hashbrown::HashSet;
|
||||
use lazy_static::lazy_static;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
use std::{ops::Add, sync::Arc};
|
||||
use vek::*;
|
||||
|
||||
use self::vol::{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)
|
||||
});
|
||||
|
||||
match town.vol.get(cell_pos).unwrap_or(&TownCell::Empty) {
|
||||
TownCell::Empty => None,
|
||||
TownCell::Park => None,
|
||||
TownCell::Rock => Some(Block::new(BlockKind::Normal, Rgb::broadcast(100))),
|
||||
TownCell::Wall => Some(Block::new(BlockKind::Normal, Rgb::broadcast(175))),
|
||||
TownCell::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())
|
||||
}
|
||||
}
|
||||
TownCell::House { idx, module } => {
|
||||
if let Some(module) = 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()),
|
||||
];
|
||||
|
||||
MODULES[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,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
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(12, 24) * 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::Rock
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Generation passes
|
||||
vol.setup(rng);
|
||||
vol.gen_roads(rng, 30);
|
||||
//vol.gen_parks(rng, 8);
|
||||
vol.emplace_columns();
|
||||
let houses = vol.gen_houses(rng, 60);
|
||||
vol.gen_walls();
|
||||
vol.resolve_modules(rng);
|
||||
|
||||
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,
|
||||
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();
|
||||
|
||||
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) {
|
||||
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 energy = 50;
|
||||
let mut park = self.floodfill([start].iter().copied().collect(), |_, col| {
|
||||
if col.is_empty() && energy > 0 {
|
||||
energy -= 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
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), TownCell::Park);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_walls(&mut self) {
|
||||
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(outer, |_, col| col.is_empty());
|
||||
|
||||
let mut walls = HashSet::new();
|
||||
self.floodfill([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| {
|
||||
(0..4)
|
||||
.filter(|i| walls.contains(&(**pos + util::dir(*i))))
|
||||
.count()
|
||||
< 2
|
||||
})
|
||||
.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..2 {
|
||||
self.set(Vec3::new(wall.x, wall.y, ground + z), TownCell::Wall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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), TownCell::Road);
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_houses(&mut self, rng: &mut impl Rng, n: usize) -> Vec<House> {
|
||||
const ATTEMPTS: usize = 20;
|
||||
|
||||
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,
|
||||
TownCell::House {
|
||||
idx: houses.len(),
|
||||
module: None,
|
||||
},
|
||||
);
|
||||
self.set_col_kind(Vec2::from(cell), Some(ColumnKind::Internal));
|
||||
}
|
||||
|
||||
houses.push(House {
|
||||
color: Rgb::new(rng.gen(), rng.gen(), rng.gen()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
houses
|
||||
}
|
||||
|
||||
fn resolve_modules(&mut self, rng: &mut impl Rng) {
|
||||
fn classify(cell: &TownCell, this_house: usize) -> ModuleKind {
|
||||
match cell {
|
||||
TownCell::House { idx, .. } if *idx == this_house => ModuleKind::This,
|
||||
_ => ModuleKind::That,
|
||||
}
|
||||
}
|
||||
|
||||
for x in 0..self.size().x {
|
||||
for y in 0..self.size().y {
|
||||
'cell: for z in self.col_range(Vec2::new(x, y)).unwrap() {
|
||||
let pos = Vec3::new(x, y, z);
|
||||
let this_idx = if let Ok(TownCell::House { idx, module }) = self.get(pos) {
|
||||
idx
|
||||
} 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_idx))
|
||||
.unwrap_or(ModuleKind::That);
|
||||
}
|
||||
|
||||
let module = MODULES
|
||||
.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);
|
||||
|
||||
self.set(
|
||||
pos,
|
||||
TownCell::House {
|
||||
idx: *this_idx,
|
||||
module,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum ModuleKind {
|
||||
This,
|
||||
That,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref MODULES: Vec<(Arc<Structure>, [ModuleKind; 6])> = {
|
||||
use ModuleKind::*;
|
||||
vec![
|
||||
(
|
||||
assets::load("world.module.human.floor_ground").unwrap(),
|
||||
[This, This, This, This, This, That],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.stair_ground").unwrap(),
|
||||
[This, This, This, This, This, That],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.corner_ground").unwrap(),
|
||||
[This, This, That, That, This, That],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.wall_ground").unwrap(),
|
||||
[This, This, This, That, This, That],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.door_ground").unwrap(),
|
||||
[This, This, This, That, This, That],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.window_ground").unwrap(),
|
||||
[This, This, This, That, This, That],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.floor_roof").unwrap(),
|
||||
[This, This, This, This, That, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.corner_roof").unwrap(),
|
||||
[This, This, That, That, That, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.chimney_roof").unwrap(),
|
||||
[This, This, That, That, That, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.wall_roof").unwrap(),
|
||||
[This, This, This, That, That, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.floor_upstairs").unwrap(),
|
||||
[This, This, This, This, This, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.balcony_upstairs").unwrap(),
|
||||
[This, This, This, This, This, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.corner_upstairs").unwrap(),
|
||||
[This, This, That, That, This, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.wall_upstairs").unwrap(),
|
||||
[This, This, This, That, This, This],
|
||||
),
|
||||
(
|
||||
assets::load("world.module.human.window_upstairs").unwrap(),
|
||||
[This, This, This, That, This, This],
|
||||
),
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
const CELL_SIZE: i32 = 11;
|
||||
|
||||
static UNIT_CHOOSER: UnitChooser = UnitChooser::new(0x100F4E37);
|
||||
@ -113,7 +719,7 @@ impl TownState {
|
||||
return None;
|
||||
}
|
||||
|
||||
let radius = 192;
|
||||
let radius = 200;
|
||||
|
||||
let mut grid = Grid::new(
|
||||
TownCell::Empty,
|
||||
@ -144,7 +750,7 @@ impl TownState {
|
||||
let idx = rng.gen_range(0, 4);
|
||||
Vec2::new(dirs[idx], dirs[idx + 1])
|
||||
};
|
||||
let road_len = 2 + rng.gen_range(1, 3) * 2 + 1;
|
||||
let road_len = 2 + rng.gen_range(1, 5) * 2 + 1;
|
||||
|
||||
// Make sure we aren't trying to create a road where a road already exists!
|
||||
match grid.get(start_pos + road_dir) {
|
||||
@ -175,7 +781,7 @@ impl TownState {
|
||||
};
|
||||
|
||||
// Create roads
|
||||
for _ in 0..25 {
|
||||
for _ in 0..radius.pow(2) / 2000 {
|
||||
create_road();
|
||||
}
|
||||
|
||||
@ -317,7 +923,7 @@ impl TownState {
|
||||
break;
|
||||
};
|
||||
|
||||
for _ in 0..40 {
|
||||
for _ in 0..radius.pow(2) / 1000 {
|
||||
place_house();
|
||||
}
|
||||
|
||||
@ -467,7 +1073,7 @@ impl TownState {
|
||||
};
|
||||
|
||||
for _ in 0..100 {
|
||||
variate_walls();
|
||||
//variate_walls();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -662,3 +1268,4 @@ impl<'a> Generator<'a, TownState> for TownGen {
|
||||
(sample.alt - 32.0, sample.alt + 75.0)
|
||||
}
|
||||
}
|
||||
*/
|
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)]
|
||||
}
|
203
world/src/generator/town/vol.rs
Normal file
203
world/src/generator/town/vol.rs
Normal file
@ -0,0 +1,203 @@
|
||||
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 TownCell {
|
||||
Empty,
|
||||
Park,
|
||||
Rock,
|
||||
Road,
|
||||
Wall,
|
||||
House { idx: usize, module: Option<Module> },
|
||||
}
|
||||
|
||||
impl TownCell {
|
||||
pub fn is_road(&self) -> bool {
|
||||
match self {
|
||||
TownCell::Road => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_foundation(&self) -> bool {
|
||||
match self {
|
||||
TownCell::Rock => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Vox for TownCell {
|
||||
fn empty() -> Self {
|
||||
TownCell::Empty
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
TownCell::Empty => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
}
|
||||
}
|
||||
}
|
@ -438,17 +438,18 @@ impl WorldSim {
|
||||
.iter()
|
||||
.min_by_key(|(pos, seed)| wpos.distance_squared(*pos));
|
||||
|
||||
if let Some((pos, seed)) = town {
|
||||
if let Some((pos, _)) = town {
|
||||
let maybe_town = maybe_towns
|
||||
.entry(*pos)
|
||||
.or_insert_with(|| {
|
||||
TownState::generate(*pos, *seed, &mut ColumnGen::new(self), &mut rng)
|
||||
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| {
|
||||
town.center.distance_squared(wpos) < town.radius.add(64).pow(2)
|
||||
Vec2::from(town.center()).distance_squared(wpos)
|
||||
< town.radius().add(64).pow(2)
|
||||
})
|
||||
.cloned();
|
||||
|
||||
|
@ -40,16 +40,19 @@ impl<T: Clone> Grid<T> {
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (Vec2<i32>, &T)> + '_ {
|
||||
(0..self.size.x)
|
||||
.map(move |x| {
|
||||
(0..self.size.y).map(move |y| {
|
||||
(
|
||||
Vec2::new(x, y),
|
||||
&self.cells[self.idx(Vec2::new(x, y)).unwrap()],
|
||||
)
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user