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
adb62f448e
commit
5a677b9c1e
@ -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/base-center.vox
(Stored with Git LFS)
BIN
assets/world/module/human/base-center.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/human/base-corner-blank.vox
(Stored with Git LFS)
BIN
assets/world/module/human/base-corner-blank.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/human/base-edge-blank.vox
(Stored with Git LFS)
BIN
assets/world/module/human/base-edge-blank.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/human/base-edge-door.vox
(Stored with Git LFS)
BIN
assets/world/module/human/base-edge-door.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/human/base-edge-window.vox
(Stored with Git LFS)
BIN
assets/world/module/human/base-edge-window.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/world/module/human/base-inner-blank.vox
(Stored with Git LFS)
BIN
assets/world/module/human/base-inner-blank.vox
(Stored with Git LFS)
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/example.vox
(Stored with Git LFS)
BIN
assets/world/module/human/example.vox
(Stored with Git LFS)
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.
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…
Reference in New Issue
Block a user