mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Better castles, initial light prop for lit objects
This commit is contained in:
parent
568a3e375e
commit
41916d4594
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()));
|
||||
|
Loading…
Reference in New Issue
Block a user