Better castles, initial light prop for lit objects

This commit is contained in:
Joshua Barretto 2020-06-29 13:43:16 +01:00
parent 568a3e375e
commit 41916d4594
8 changed files with 108 additions and 71 deletions

View File

@ -298,11 +298,6 @@ impl ChatCommand {
"View the server description",
NoAdmin,
),
ChatCommand::MakeBlock => cmd(
vec![Enum("block", BLOCK_KINDS.clone(), Required)],
"Make a block",
Admin,
),
ChatCommand::Object => cmd(
vec![Enum("object", OBJECTS.clone(), Required)],
"Spawn an object",

View File

@ -202,6 +202,14 @@ impl BlockKind {
}
}
pub fn get_glow(&self) -> Option<u8> {
match self {
BlockKind::StreetLamp | BlockKind::StreetLampTall => Some(20),
BlockKind::Velorite | BlockKind::VeloriteFrag => Some(10),
_ => None,
}
}
pub fn is_opaque(&self) -> bool {
match self {
BlockKind::Air => false,

View File

@ -4,7 +4,7 @@ use crate::{
};
use common::{
terrain::{Block, BlockKind},
vol::{ReadVol, RectRasterableVol, Vox},
vol::{DefaultVolIterator, ReadVol, RectRasterableVol, Vox},
volumes::vol_grid_2d::{CachedVolGrid2d, VolGrid2d},
};
use std::{collections::VecDeque, fmt::Debug};
@ -26,13 +26,16 @@ impl Blendable for BlockKind {
}
}
const SUNLIGHT: u8 = 24;
const MAX_LIGHT_DIST: i32 = SUNLIGHT as i32;
fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
bounds: Aabb<i32>,
vol: &VolGrid2d<V>,
lit_blocks: impl Iterator<Item = (Vec3<i32>, u8)>,
) -> impl FnMut(Vec3<i32>) -> f32 + '_ {
const UNKNOWN: u8 = 255;
const OPAQUE: u8 = 254;
const SUNLIGHT: u8 = 24;
let outer = Aabb {
min: bounds.min - Vec3::new(SUNLIGHT as i32 - 1, SUNLIGHT as i32 - 1, 1),
@ -47,7 +50,13 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
move |x, y, z| (z * h * w + x * h + y) as usize
};
// Light propagation queue
let mut prop_que = VecDeque::new();
let mut prop_que = lit_blocks
.map(|(pos, light)| {
let rpos = pos - outer.min;
light_map[lm_idx(rpos.x, rpos.y, rpos.z)] = light;
(rpos.x as u8, rpos.y as u8, rpos.z as u16)
})
.collect::<VecDeque<_>>();
// Start sun rays
for x in 0..outer.size().w {
for y in 0..outer.size().h {
@ -216,7 +225,13 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug>
&'a self,
range: Self::Supplement,
) -> (Mesh<Self::Pipeline>, Mesh<Self::TranslucentPipeline>) {
let mut light = calc_light(range, self);
// Find blocks that should glow
let lit_blocks =
DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow)));
// Calculate chunk lighting
let mut light = calc_light(range, self, lit_blocks);
let mut lowest_opaque = range.size().d;
let mut highest_opaque = 0;

View File

@ -2820,26 +2820,6 @@ impl<V: RectRasterableVol> Terrain<V> {
.iter()
.map(|(p, _)| *p)
{
let chunk_pos = scene_data.state.terrain().pos_key(pos);
// Only mesh if this chunk has all its neighbors
let mut neighbours = true;
for i in -1..2 {
for j in -1..2 {
neighbours &= scene_data
.state
.terrain()
.get_key(chunk_pos + Vec2::new(i, j))
.is_some();
}
}
if neighbours {
self.mesh_todo.insert(chunk_pos, ChunkMeshState {
pos: chunk_pos,
started_tick: current_tick,
active_worker: None,
});
}
// Handle block changes on chunk borders
// Remesh all neighbours because we have complex lighting now
// TODO: if lighting is on the server this can be updated to only remesh when
@ -2850,26 +2830,24 @@ impl<V: RectRasterableVol> Terrain<V> {
let neighbour_pos = pos + Vec3::new(x, y, 0);
let neighbour_chunk_pos = scene_data.state.terrain().pos_key(neighbour_pos);
if neighbour_chunk_pos != chunk_pos {
// Only remesh if this chunk has all its neighbors
let mut neighbours = true;
for i in -1..2 {
for j in -1..2 {
neighbours &= scene_data
.state
.terrain()
.get_key(neighbour_chunk_pos + Vec2::new(i, j))
.is_some();
}
}
if neighbours {
self.mesh_todo.insert(neighbour_chunk_pos, ChunkMeshState {
pos: neighbour_chunk_pos,
started_tick: current_tick,
active_worker: None,
});
// Only remesh if this chunk has all its neighbors
let mut neighbours = true;
for i in -1..2 {
for j in -1..2 {
neighbours &= scene_data
.state
.terrain()
.get_key(neighbour_chunk_pos + Vec2::new(i, j))
.is_some();
}
}
if neighbours {
self.mesh_todo.insert(neighbour_chunk_pos, ChunkMeshState {
pos: neighbour_chunk_pos,
started_tick: current_tick,
active_worker: None,
});
}
}
}
}

View File

@ -48,6 +48,7 @@ pub struct Castle {
radius: i32,
towers: Vec<Tower>,
segments: Vec<Segment>,
rounded_towers: bool,
}
pub struct GenCtx<'a, R: Rng> {
@ -106,6 +107,7 @@ impl Castle {
}
})
.collect(),
rounded_towers: ctx.rng.gen(),
segments: (0..0) //rng.gen_range(18, 24))
.map(|_| {
@ -159,7 +161,7 @@ impl Castle {
continue;
};
let (wall_dist, wall_pos, wall_alt, wall_ori) = (0..self.towers.len())
let (wall_dist, wall_pos, wall_alt, wall_ori, towers) = (0..self.towers.len())
.map(|i| {
let tower0 = &self.towers[i];
let tower1 = &self.towers[(i + 1) % self.towers.len()];
@ -171,7 +173,7 @@ impl Castle {
let projected = wall
.projected_point(rpos.map(|e| e as f32))
.map(|e| e as i32);
.map(|e| e.floor() as i32);
let tower0_dist = tower0
.offset
@ -195,11 +197,19 @@ impl Castle {
projected,
Lerp::lerp(tower0.alt as f32, tower1.alt as f32, tower_lerp) as i32,
wall_ori,
[tower0, tower1],
)
})
.min_by_key(|x| x.0)
.unwrap();
// Apply the dungeon entrance
let wall_sample = if let Some(col) = get_column(offs + wall_pos - rpos) {
col
} else {
col_sample
};
for z in -10..64 {
let wpos = Vec3::new(wpos2d.x, wpos2d.y, col_sample.alt as i32 + z);
@ -226,6 +236,7 @@ impl Castle {
&Attr {
height: 16,
is_tower: false,
rounded: true,
},
);
for tower in &self.towers {
@ -257,6 +268,7 @@ impl Castle {
&Attr {
height: 28,
is_tower: true,
rounded: self.rounded_towers,
},
));
}

View File

@ -85,6 +85,8 @@ pub struct Attr {
pub roof_style: RoofStyle,
pub mansard: i32,
pub pillar: Pillar,
pub levels: i32,
pub window: BlockKind,
}
impl Attr {
@ -103,9 +105,16 @@ impl Attr {
},
mansard: rng.gen_range(-7, 4).max(0),
pillar: match rng.gen_range(0, 4) {
0 => Pillar::Chimney(9 + locus + rng.gen_range(0, 4)),
0 => Pillar::Chimney(rng.gen_range(1, 5)),
_ => Pillar::None,
},
levels: rng.gen_range(1, 3),
window: match rng.gen_range(0, 4) {
0 => BlockKind::Window1,
1 => BlockKind::Window2,
2 => BlockKind::Window3,
_ => BlockKind::Window4,
},
}
}
}
@ -126,10 +135,11 @@ impl Archetype for House {
storey_fill: StoreyFill::All,
mansard: 0,
pillar: match rng.gen_range(0, 3) {
0 => Pillar::Chimney(10 + locus + rng.gen_range(0, 4)),
1 => Pillar::Tower(15 + locus + rng.gen_range(0, 4)),
0 => Pillar::Chimney(rng.gen_range(1, 5)),
1 => Pillar::Tower(5 + rng.gen_range(1, 5)),
_ => Pillar::None,
},
levels: rng.gen_range(1, 4),
..Attr::generate(rng, locus)
},
locus,
@ -224,12 +234,15 @@ impl Archetype for House {
let empty = BlockMask::nothing();
let internal = BlockMask::new(Block::empty(), internal_layer);
let end_window = BlockMask::new(
Block::new(BlockKind::Window1, make_meta(ori.flip())),
Block::new(attr.window, make_meta(ori.flip())),
structural_layer,
);
let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer);
let ceil_height = 6;
let storey_height = 6;
let storey = ((z - 1) / storey_height).min(attr.levels - 1);
let floor_height = storey_height * storey;
let ceil_height = storey_height * (storey + 1);
let lower_width = locus - 1;
let upper_width = locus;
let width = if profile.y >= ceil_height {
@ -238,9 +251,10 @@ impl Archetype for House {
lower_width
};
let foundation_height = 0 - (dist - width - 1).max(0);
let roof_top = 8 + width;
let roof_top = storey_height * attr.levels + 2 + width;
if let Pillar::Chimney(chimney_top) = attr.pillar {
if let Pillar::Chimney(chimney_height) = attr.pillar {
let chimney_top = roof_top + chimney_height;
// Chimney shaft
if center_offset.map(|e| e.abs()).reduce_max() == 0
&& profile.y >= foundation_height + 1
@ -331,12 +345,14 @@ impl Archetype for House {
&& bound_offset.x < width
&& profile.y < ceil_height
&& attr.storey_fill.has_lower()
&& storey == 0
{
return Some(
if (bound_offset.x == (width - 1) / 2
|| bound_offset.x == (width - 1) / 2 + 1)
&& profile.y <= foundation_height + 3
{
// Doors on first floor only
if profile.y == foundation_height + 1 {
BlockMask::new(
Block::new(
@ -377,7 +393,7 @@ impl Archetype for House {
} else {
(
Aabr {
min: Vec2::new(2, foundation_height + 2),
min: Vec2::new(2, floor_height + 2),
max: Vec2::new(width - 2, ceil_height - 2),
},
Vec2::new(1, 0),
@ -393,7 +409,7 @@ impl Archetype for House {
// Window
if (frame_bounds.size() + 1).reduce_min() > 2 {
// Window frame is large enough for a window
let surface_pos = Vec2::new(bound_offset.x, profile.y);
let surface_pos = Vec2::new(bound_offset.x, profile.y - floor_height);
if window_bounds.contains_point(surface_pos) {
return Some(end_window);
} else if frame_bounds.contains_point(surface_pos) {
@ -441,7 +457,8 @@ impl Archetype for House {
cblock = cblock.resolve_with(block);
}
if let Pillar::Tower(tower_top) = attr.pillar {
if let Pillar::Tower(tower_height) = attr.pillar {
let tower_top = roof_top + tower_height;
let profile = Vec2::new(center_offset.x.abs(), profile.y);
let dist = center_offset.map(|e| e.abs()).reduce_max();

View File

@ -14,6 +14,7 @@ pub struct Keep {
pub struct Attr {
pub height: i32,
pub is_tower: bool,
pub rounded: bool,
}
impl Archetype for Keep {
@ -29,6 +30,7 @@ impl Archetype for Keep {
attr: Attr {
height: rng.gen_range(12, 16),
is_tower: false,
rounded: true,
},
locus: 10 + rng.gen_range(0, 5),
border: 3,
@ -41,6 +43,7 @@ impl Archetype for Keep {
attr: Attr {
height: rng.gen_range(20, 28),
is_tower: true,
rounded: true,
},
locus: 4 + rng.gen_range(0, 5),
border: 3,
@ -89,7 +92,12 @@ impl Archetype for Keep {
let foundation = make_block(100, 100, 100);
let wall = make_block(100, 100, 110);
let floor = make_block(120, 80, 50).with_priority(important_layer);
let floor = make_block(
80 + (pos.y.abs() % 2) as u8 * 15,
60 + (pos.y.abs() % 2) as u8 * 15,
10 + (pos.y.abs() % 2) as u8 * 15,
)
.with_priority(important_layer);
let pole = make_block(90, 70, 50).with_priority(important_layer);
let flag = make_block(self.flag_color.r, self.flag_color.g, self.flag_color.b)
.with_priority(important_layer);
@ -106,12 +114,11 @@ impl Archetype for Keep {
pos.x
};
let rampart_height = ceil_height + if edge_pos % 2 == 0 { 3 } else { 4 };
let inner = Clamp::clamp(
center_offset,
Vec2::new(-5, -len / 2 - 5),
Vec2::new(5, len / 2 + 5),
);
let min_dist = bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32; //(bound_offset.distance_squared(inner) as f32).sqrt() as i32 + 5;//bound_offset.reduce_max();
let min_dist = if attr.rounded {
bound_offset.map(|e| e.pow(2) as f32).sum().powf(0.5) as i32
} else {
bound_offset.map(|e| e.abs()).reduce_max()
};
if profile.y <= 0 - (min_dist - width - 1).max(0) && min_dist < width + 3 {
// Foundations

View File

@ -390,6 +390,7 @@ impl Settlement {
district
.and_then(|d| self.town.as_ref().map(|t| t.districts().get(d)))
.map(|d| d.alt)
.filter(|_| false) // Temporary
.unwrap_or_else(|| {
ctx.sim
.and_then(|sim| sim.get_alt_approx(self.origin + house_pos))
@ -563,8 +564,10 @@ impl Settlement {
// District alt
if let Some(Plot::Town { district }) = sample.plot {
if let Some(d) =
district.and_then(|d| self.town.as_ref().map(|t| t.districts().get(d)))
if let Some(d) = district
.and_then(|d| self.town.as_ref().map(|t| t.districts().get(d)))
.filter(|_| false)
// Temporary
{
let other = self
.land
@ -576,6 +579,7 @@ impl Settlement {
.and_then(|d| {
self.town.as_ref().map(|t| t.districts().get(d).alt as f32)
})
.filter(|_| false)
.unwrap_or(surface_z as f32);
surface_z = Lerp::lerp(
(other + d.alt as f32) / 2.0,
@ -640,14 +644,15 @@ impl Settlement {
.rotated_z(f32::consts::PI / 2.0)
.normalized();
let is_lamp = if path_dir.x.abs() > path_dir.y.abs() {
wpos2d.x as f32 % 20.0 / path_dir.dot(Vec2::unit_y()).abs()
wpos2d.x as f32 % 30.0 / path_dir.dot(Vec2::unit_y()).abs()
<= 1.0
} else {
wpos2d.y as f32 % 20.0 / path_dir.dot(Vec2::unit_x()).abs()
(wpos2d.y as f32 + 10.0) % 30.0
/ path_dir.dot(Vec2::unit_x()).abs()
<= 1.0
};
if (col_sample.path.map(|(dist, _)| dist > 6.0 && dist < 7.0).unwrap_or(false) && is_lamp) //roll(0, 50) == 0)
|| roll(0, 2000) == 0
|| (roll(0, 2000) == 0 && col_sample.path.map(|(dist, _)| dist > 20.0).unwrap_or(true))
{
surface_block =
Some(Block::new(BlockKind::StreetLamp, Rgb::white()));