mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Use new dungeons in dungeon_voxel_export, remove old dungeon sampling function, and add more of world/examples to CI.
This commit is contained in:
parent
f6782e21b8
commit
957ba5d218
@ -6,7 +6,7 @@ code-quality:
|
|||||||
script:
|
script:
|
||||||
- ln -s /dockercache/target target
|
- ln -s /dockercache/target target
|
||||||
- rm -r target/debug/incremental/* || echo "all good" # TMP FIX FOR 2021-03-22-nightly
|
- rm -r target/debug/incremental/* || echo "all good" # TMP FIX FOR 2021-03-22-nightly
|
||||||
- cargo clippy --all-targets --locked --features="bin_csv,bin_graphviz,bin_bot,asset_tweak" -- -D warnings
|
- cargo clippy --all-targets --locked --features="bin_compression,bin_csv,bin_graphviz,bin_bot,asset_tweak" -- -D warnings
|
||||||
# Ensure that the veloren-voxygen default-publish feature builds as it excludes some default features
|
# Ensure that the veloren-voxygen default-publish feature builds as it excludes some default features
|
||||||
- cargo clippy -p veloren-voxygen --locked --no-default-features --features="default-publish" -- -D warnings
|
- cargo clippy -p veloren-voxygen --locked --no-default-features --features="default-publish" -- -D warnings
|
||||||
- cargo fmt --all -- --check
|
- cargo fmt --all -- --check
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#![allow(clippy::type_complexity)]
|
||||||
use common::{
|
use common::{
|
||||||
spiral::Spiral2d,
|
spiral::Spiral2d,
|
||||||
terrain::{chonk::Chonk, Block, BlockKind, SpriteKind},
|
terrain::{chonk::Chonk, Block, BlockKind, SpriteKind},
|
||||||
@ -15,6 +16,7 @@ use common_net::msg::compression::{
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use image::ImageBuffer;
|
use image::ImageBuffer;
|
||||||
use num_traits::cast::FromPrimitive;
|
use num_traits::cast::FromPrimitive;
|
||||||
|
use rayon::ThreadPoolBuilder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
@ -56,8 +58,7 @@ fn do_deflate_rle(data: &[u8]) -> Vec<u8> {
|
|||||||
|
|
||||||
let mut encoder = DeflateEncoder::new(Vec::new(), CompressionOptions::rle());
|
let mut encoder = DeflateEncoder::new(Vec::new(), CompressionOptions::rle());
|
||||||
encoder.write_all(data).expect("Write error!");
|
encoder.write_all(data).expect("Write error!");
|
||||||
let compressed_data = encoder.finish().expect("Failed to finish compression!");
|
encoder.finish().expect("Failed to finish compression!")
|
||||||
compressed_data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separate function so that it shows up differently on the flamegraph
|
// Separate function so that it shows up differently on the flamegraph
|
||||||
@ -66,8 +67,7 @@ fn do_deflate_flate2_zero(data: &[u8]) -> Vec<u8> {
|
|||||||
|
|
||||||
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::new(0));
|
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::new(0));
|
||||||
encoder.write_all(data).expect("Write error!");
|
encoder.write_all(data).expect("Write error!");
|
||||||
let compressed_data = encoder.finish().expect("Failed to finish compression!");
|
encoder.finish().expect("Failed to finish compression!")
|
||||||
compressed_data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_deflate_flate2<const LEVEL: u32>(data: &[u8]) -> Vec<u8> {
|
fn do_deflate_flate2<const LEVEL: u32>(data: &[u8]) -> Vec<u8> {
|
||||||
@ -75,8 +75,7 @@ fn do_deflate_flate2<const LEVEL: u32>(data: &[u8]) -> Vec<u8> {
|
|||||||
|
|
||||||
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::new(LEVEL));
|
let mut encoder = DeflateEncoder::new(Vec::new(), Compression::new(LEVEL));
|
||||||
encoder.write_all(data).expect("Write error!");
|
encoder.write_all(data).expect("Write error!");
|
||||||
let compressed_data = encoder.finish().expect("Failed to finish compression!");
|
encoder.finish().expect("Failed to finish compression!")
|
||||||
compressed_data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chonk_to_dyna<V: Clone, S: RectVolSize, M: Clone, A: Access>(
|
fn chonk_to_dyna<V: Clone, S: RectVolSize, M: Clone, A: Access>(
|
||||||
@ -489,6 +488,7 @@ impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
fn histogram_to_dictionary(histogram: &HashMap<Vec<u8>, usize>, dictionary: &mut Vec<u8>) {
|
fn histogram_to_dictionary(histogram: &HashMap<Vec<u8>, usize>, dictionary: &mut Vec<u8>) {
|
||||||
let mut tmp: Vec<(Vec<u8>, usize)> = histogram.iter().map(|(k, v)| (k.clone(), *v)).collect();
|
let mut tmp: Vec<(Vec<u8>, usize)> = histogram.iter().map(|(k, v)| (k.clone(), *v)).collect();
|
||||||
tmp.sort_by_key(|(_, count)| *count);
|
tmp.sort_by_key(|(_, count)| *count);
|
||||||
@ -507,13 +507,17 @@ fn histogram_to_dictionary(histogram: &HashMap<Vec<u8>, usize>, dictionary: &mut
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let pool = ThreadPoolBuilder::new().build().unwrap();
|
||||||
common_frontend::init_stdout(None);
|
common_frontend::init_stdout(None);
|
||||||
println!("Loading world");
|
println!("Loading world");
|
||||||
let (world, index) = World::generate(59686, WorldOpts {
|
let (world, index) = World::generate(
|
||||||
seed_elements: true,
|
59686,
|
||||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
WorldOpts {
|
||||||
..WorldOpts::default()
|
seed_elements: true,
|
||||||
});
|
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||||
|
},
|
||||||
|
&pool,
|
||||||
|
);
|
||||||
println!("Loaded world");
|
println!("Loaded world");
|
||||||
const HISTOGRAMS: bool = false;
|
const HISTOGRAMS: bool = false;
|
||||||
let mut histogram: HashMap<Vec<u8>, usize> = HashMap::new();
|
let mut histogram: HashMap<Vec<u8>, usize> = HashMap::new();
|
||||||
@ -523,45 +527,45 @@ fn main() {
|
|||||||
let k = 32;
|
let k = 32;
|
||||||
let sz = world.sim().get_size();
|
let sz = world.sim().get_size();
|
||||||
|
|
||||||
let mut sites = Vec::new();
|
let sites = vec![
|
||||||
|
("center", sz / 2),
|
||||||
sites.push(("center", sz / 2));
|
(
|
||||||
sites.push((
|
"dungeon",
|
||||||
"dungeon",
|
world
|
||||||
world
|
.civs()
|
||||||
.civs()
|
.sites()
|
||||||
.sites()
|
.find(|s| s.is_dungeon())
|
||||||
.find(|s| s.is_dungeon())
|
.map(|s| s.center.as_())
|
||||||
.map(|s| s.center.as_())
|
.unwrap(),
|
||||||
.unwrap(),
|
),
|
||||||
));
|
(
|
||||||
sites.push((
|
"town",
|
||||||
"town",
|
world
|
||||||
world
|
.civs()
|
||||||
.civs()
|
.sites()
|
||||||
.sites()
|
.find(|s| s.is_settlement())
|
||||||
.find(|s| s.is_settlement())
|
.map(|s| s.center.as_())
|
||||||
.map(|s| s.center.as_())
|
.unwrap(),
|
||||||
.unwrap(),
|
),
|
||||||
));
|
(
|
||||||
sites.push((
|
"castle",
|
||||||
"castle",
|
world
|
||||||
world
|
.civs()
|
||||||
.civs()
|
.sites()
|
||||||
.sites()
|
.find(|s| s.is_castle())
|
||||||
.find(|s| s.is_castle())
|
.map(|s| s.center.as_())
|
||||||
.map(|s| s.center.as_())
|
.unwrap(),
|
||||||
.unwrap(),
|
),
|
||||||
));
|
(
|
||||||
sites.push((
|
"tree",
|
||||||
"tree",
|
world
|
||||||
world
|
.civs()
|
||||||
.civs()
|
.sites()
|
||||||
.sites()
|
.find(|s| matches!(s.kind, SiteKind::Tree))
|
||||||
.find(|s| matches!(s.kind, SiteKind::Tree))
|
.map(|s| s.center.as_())
|
||||||
.map(|s| s.center.as_())
|
.unwrap(),
|
||||||
.unwrap(),
|
),
|
||||||
));
|
];
|
||||||
|
|
||||||
const SKIP_DEFLATE_2_5: bool = false;
|
const SKIP_DEFLATE_2_5: bool = false;
|
||||||
const SKIP_DYNA: bool = true;
|
const SKIP_DYNA: bool = true;
|
||||||
@ -600,6 +604,7 @@ fn main() {
|
|||||||
let lz4chonk_pre = Instant::now();
|
let lz4chonk_pre = Instant::now();
|
||||||
let lz4_chonk = lz4_with_dictionary(&bincode::serialize(&chunk).unwrap(), &[]);
|
let lz4_chonk = lz4_with_dictionary(&bincode::serialize(&chunk).unwrap(), &[]);
|
||||||
let lz4chonk_post = Instant::now();
|
let lz4chonk_post = Instant::now();
|
||||||
|
#[allow(clippy::reversed_empty_ranges)]
|
||||||
for _ in 0..ITERS {
|
for _ in 0..ITERS {
|
||||||
let _deflate0_chonk =
|
let _deflate0_chonk =
|
||||||
do_deflate_flate2_zero(&bincode::serialize(&chunk).unwrap());
|
do_deflate_flate2_zero(&bincode::serialize(&chunk).unwrap());
|
||||||
@ -1024,7 +1029,7 @@ fn main() {
|
|||||||
for (name, value) in totals.iter() {
|
for (name, value) in totals.iter() {
|
||||||
println!("Average {}: {}", name, *value / count as f32);
|
println!("Average {}: {}", name, *value / count as f32);
|
||||||
}
|
}
|
||||||
println!("");
|
println!();
|
||||||
for (name, time) in total_timings.iter() {
|
for (name, time) in total_timings.iter() {
|
||||||
println!("Average {} nanos: {:02}", name, *time / count as f32);
|
println!("Average {} nanos: {:02}", name, *time / count as f32);
|
||||||
}
|
}
|
||||||
|
@ -6,28 +6,65 @@ use std::{
|
|||||||
type Result = std::io::Result<()>;
|
type Result = std::io::Result<()>;
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind, SpriteKind},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
vol::{BaseVol, ReadVol, RectSizedVol, WriteVol},
|
||||||
};
|
};
|
||||||
|
use rayon::ThreadPoolBuilder;
|
||||||
use vek::{Vec2, Vec3};
|
use vek::{Vec2, Vec3};
|
||||||
use veloren_world::{index::Index, IndexOwned, Land};
|
use veloren_world::{
|
||||||
|
sim::{FileOpts, WorldOpts, DEFAULT_WORLD_MAP},
|
||||||
|
site2::{plot::PlotKind, Structure},
|
||||||
|
CanvasInfo, Land, World,
|
||||||
|
};
|
||||||
|
|
||||||
/// This exports a dungeon (structure only, no entities or sprites) to a
|
/// This exports a dungeon (structure only, no entities or sprites) to a
|
||||||
/// MagicaVoxel .vox file
|
/// MagicaVoxel .vox file
|
||||||
|
|
||||||
fn main() -> Result {
|
fn main() -> Result {
|
||||||
|
common_frontend::init_stdout(None);
|
||||||
|
let pool = ThreadPoolBuilder::new().build().unwrap();
|
||||||
|
println!("Loading world");
|
||||||
|
let (world, index) = World::generate(
|
||||||
|
59686,
|
||||||
|
WorldOpts {
|
||||||
|
seed_elements: true,
|
||||||
|
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||||
|
},
|
||||||
|
&pool,
|
||||||
|
);
|
||||||
|
println!("Loaded world");
|
||||||
let export_path = "dungeon.vox";
|
let export_path = "dungeon.vox";
|
||||||
let seed = 0;
|
|
||||||
|
|
||||||
println!("Saving into {}", export_path);
|
println!("Saving into {}", export_path);
|
||||||
let mut volume = ExportVol::new();
|
let mut volume = ExportVol::new();
|
||||||
let index = IndexOwned::new(Index::new(seed));
|
let wpos = volume.size_xy().map(|p| p as i32 / 2);
|
||||||
let dungeon = veloren_world::site2::plot::Dungeon::generate(
|
let site =
|
||||||
volume.size_xy().map(|p| p as i32 / 2),
|
veloren_world::site2::Site::generate_dungeon(&Land::empty(), &mut rand::thread_rng(), wpos);
|
||||||
&Land::empty(),
|
CanvasInfo::with_mock_canvas_info(index.as_index_ref(), world.sim(), |canvas| {
|
||||||
&mut rand::thread_rng(),
|
for plot in site.plots() {
|
||||||
);
|
if let PlotKind::Dungeon(dungeon) = plot.kind() {
|
||||||
dungeon.apply_to(index.as_index_ref(), Vec2::new(0, 0), |_| None, &mut volume);
|
let (prim_tree, fills) = dungeon.render_collect(&site);
|
||||||
|
|
||||||
|
for (prim, fill) in fills {
|
||||||
|
let aabb = fill.get_bounds(&prim_tree, prim);
|
||||||
|
|
||||||
|
for x in aabb.min.x..aabb.max.x {
|
||||||
|
for y in aabb.min.y..aabb.max.y {
|
||||||
|
for z in aabb.min.z..aabb.max.z {
|
||||||
|
let pos = Vec3::new(x, y, z);
|
||||||
|
|
||||||
|
if let Some(block) = fill.sample_at(&prim_tree, prim, pos, &canvas)
|
||||||
|
{
|
||||||
|
let _ = volume.set(pos, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
volume.write(&mut File::create(export_path)?)
|
volume.write(&mut File::create(export_path)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +194,9 @@ impl ExportVol {
|
|||||||
write_chunk(file, "RGBA", &|file| {
|
write_chunk(file, "RGBA", &|file| {
|
||||||
file.write_all(&[220, 220, 255, 0])?; // Air
|
file.write_all(&[220, 220, 255, 0])?; // Air
|
||||||
file.write_all(&[100, 100, 100, 0])?; // Rock
|
file.write_all(&[100, 100, 100, 0])?; // Rock
|
||||||
file.write_all(&[0; 4 * (256 - 2)])
|
file.write_all(&[255, 0, 0, 0])?; // Sprite
|
||||||
|
file.write_all(&[255, 0, 255, 0])?; // GlowingRock
|
||||||
|
file.write_all(&[0; 4 * (256 - 4)])
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let chunks_end = file.stream_position()?;
|
let chunks_end = file.stream_position()?;
|
||||||
@ -200,9 +239,16 @@ impl WriteVol for ExportVol {
|
|||||||
.entry(model_pos)
|
.entry(model_pos)
|
||||||
.or_default()
|
.or_default()
|
||||||
.extend_from_slice(&[rel_pos.x, rel_pos.y, rel_pos.z, match vox.kind() {
|
.extend_from_slice(&[rel_pos.x, rel_pos.y, rel_pos.z, match vox.kind() {
|
||||||
BlockKind::Air => 1,
|
BlockKind::Air => {
|
||||||
|
if !matches!(vox.get_sprite(), Some(SpriteKind::Empty)) {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
},
|
||||||
BlockKind::Rock => 2,
|
BlockKind::Rock => 2,
|
||||||
_ => 3,
|
BlockKind::GlowingRock => 4,
|
||||||
|
_ => 5,
|
||||||
}]);
|
}]);
|
||||||
Ok(vox)
|
Ok(vox)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use image::{
|
|||||||
codecs::png::{CompressionType, FilterType, PngEncoder},
|
codecs::png::{CompressionType, FilterType, PngEncoder},
|
||||||
ImageBuffer,
|
ImageBuffer,
|
||||||
};
|
};
|
||||||
|
use rayon::ThreadPoolBuilder;
|
||||||
use std::{fs::File, io::Write};
|
use std::{fs::File, io::Write};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use veloren_world::{
|
use veloren_world::{
|
||||||
@ -116,12 +117,16 @@ fn image_with_autorange<F: Fn(f32, f32, f32) -> [u8; 3], G: FnMut(u32, u32) -> f
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
common_frontend::init_stdout(None);
|
common_frontend::init_stdout(None);
|
||||||
|
let pool = ThreadPoolBuilder::new().build().unwrap();
|
||||||
println!("Loading world");
|
println!("Loading world");
|
||||||
let (world, _index) = World::generate(59686, WorldOpts {
|
let (world, _index) = World::generate(
|
||||||
seed_elements: true,
|
59686,
|
||||||
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
WorldOpts {
|
||||||
..WorldOpts::default()
|
seed_elements: true,
|
||||||
});
|
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
|
||||||
|
},
|
||||||
|
&pool,
|
||||||
|
);
|
||||||
println!("Loaded world");
|
println!("Loaded world");
|
||||||
|
|
||||||
let land = Land::from_sim(world.sim());
|
let land = Land::from_sim(world.sim());
|
||||||
|
@ -49,6 +49,44 @@ impl<'a> CanvasInfo<'a> {
|
|||||||
pub fn chunks(&self) -> &'a WorldSim { self.chunks }
|
pub fn chunks(&self) -> &'a WorldSim { self.chunks }
|
||||||
|
|
||||||
pub fn land(&self) -> Land<'_> { Land::from_sim(self.chunks) }
|
pub fn land(&self) -> Land<'_> { Land::from_sim(self.chunks) }
|
||||||
|
|
||||||
|
pub fn with_mock_canvas_info<A, F: for<'b> FnOnce(&CanvasInfo<'b>) -> A>(
|
||||||
|
index: IndexRef<'a>,
|
||||||
|
sim: &'a WorldSim,
|
||||||
|
f: F,
|
||||||
|
) -> A {
|
||||||
|
let zcache_grid = Grid::populate_from(Vec2::broadcast(1), |_| None);
|
||||||
|
let sim_chunk = SimChunk {
|
||||||
|
chaos: 0.0,
|
||||||
|
alt: 0.0,
|
||||||
|
basement: 0.0,
|
||||||
|
water_alt: 0.0,
|
||||||
|
downhill: None,
|
||||||
|
flux: 0.0,
|
||||||
|
temp: 0.0,
|
||||||
|
humidity: 0.0,
|
||||||
|
rockiness: 0.0,
|
||||||
|
tree_density: 0.0,
|
||||||
|
forest_kind: crate::all::ForestKind::Palm,
|
||||||
|
spawn_rate: 0.0,
|
||||||
|
river: Default::default(),
|
||||||
|
surface_veg: 0.0,
|
||||||
|
sites: Vec::new(),
|
||||||
|
place: None,
|
||||||
|
path: Default::default(),
|
||||||
|
cave: Default::default(),
|
||||||
|
cliff_height: 0.0,
|
||||||
|
contains_waypoint: false,
|
||||||
|
};
|
||||||
|
f(&CanvasInfo {
|
||||||
|
wpos: Vec2::broadcast(0),
|
||||||
|
column_grid: &zcache_grid,
|
||||||
|
column_grid_border: 0,
|
||||||
|
chunks: &sim,
|
||||||
|
index,
|
||||||
|
chunk: &sim_chunk,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Canvas<'a> {
|
pub struct Canvas<'a> {
|
||||||
|
@ -157,7 +157,7 @@ impl Fill {
|
|||||||
tree: &Store<Primitive>,
|
tree: &Store<Primitive>,
|
||||||
prim: Id<Primitive>,
|
prim: Id<Primitive>,
|
||||||
pos: Vec3<i32>,
|
pos: Vec3<i32>,
|
||||||
canvas: &Canvas,
|
canvas_info: &crate::CanvasInfo,
|
||||||
) -> Option<Block> {
|
) -> Option<Block> {
|
||||||
if self.contains_at(tree, prim, pos) {
|
if self.contains_at(tree, prim, pos) {
|
||||||
match self {
|
match self {
|
||||||
@ -169,10 +169,9 @@ impl Fill {
|
|||||||
% *range as u32) as u8,
|
% *range as u32) as u8,
|
||||||
)),
|
)),
|
||||||
Fill::Prefab(p, tr, seed) => p.get(pos - tr).ok().and_then(|sb| {
|
Fill::Prefab(p, tr, seed) => p.get(pos - tr).ok().and_then(|sb| {
|
||||||
let info = canvas.info;
|
let col_sample = canvas_info.col(canvas_info.wpos)?;
|
||||||
let col_sample = info.col(info.wpos)?;
|
|
||||||
block_from_structure(
|
block_from_structure(
|
||||||
canvas.index,
|
canvas_info.index,
|
||||||
*sb,
|
*sb,
|
||||||
pos - tr,
|
pos - tr,
|
||||||
p.get_bounds().center().xy(),
|
p.get_bounds().center().xy(),
|
||||||
|
@ -2,10 +2,10 @@ mod gen;
|
|||||||
pub mod plot;
|
pub mod plot;
|
||||||
mod tile;
|
mod tile;
|
||||||
|
|
||||||
use self::{
|
use self::tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE};
|
||||||
|
pub use self::{
|
||||||
gen::{aabr_with_z, Fill, Primitive, Structure},
|
gen::{aabr_with_z, Fill, Primitive, Structure},
|
||||||
plot::{Plot, PlotKind},
|
plot::{Plot, PlotKind},
|
||||||
tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE},
|
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
site::SpawnRules,
|
site::SpawnRules,
|
||||||
@ -772,7 +772,8 @@ impl Site {
|
|||||||
for z in aabb.min.z..aabb.max.z {
|
for z in aabb.min.z..aabb.max.z {
|
||||||
let pos = Vec3::new(x, y, z);
|
let pos = Vec3::new(x, y, z);
|
||||||
|
|
||||||
if let Some(block) = fill.sample_at(&prim_tree, prim, pos, &canvas) {
|
if let Some(block) = fill.sample_at(&prim_tree, prim, pos, &canvas.info)
|
||||||
|
{
|
||||||
canvas.set(pos, block);
|
canvas.set(pos, block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ impl Plot {
|
|||||||
b.expanded_to_contain_point(*t)
|
b.expanded_to_contain_point(*t)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kind(&self) -> &PlotKind { &self.kind }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum PlotKind {
|
pub enum PlotKind {
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
use super::SpawnRules;
|
use super::SpawnRules;
|
||||||
use crate::{
|
use crate::{
|
||||||
block::block_from_structure,
|
site::namegen::NameGen,
|
||||||
column::ColumnSample,
|
|
||||||
site::{namegen::NameGen, BlockMask},
|
|
||||||
site2::{self, aabr_with_z, Fill, Primitive, Structure as SiteStructure},
|
site2::{self, aabr_with_z, Fill, Primitive, Structure as SiteStructure},
|
||||||
util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS},
|
util::{attempt, Grid, RandomField, Sampler, CARDINALS, DIRS},
|
||||||
IndexRef, Land,
|
Land,
|
||||||
};
|
};
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
@ -15,7 +13,7 @@ use common::{
|
|||||||
generation::{ChunkSupplement, EntityInfo},
|
generation::{ChunkSupplement, EntityInfo},
|
||||||
store::{Id, Store},
|
store::{Id, Store},
|
||||||
terrain::{Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize},
|
||||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, WriteVol},
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use core::{f32, hash::BuildHasherDefault};
|
use core::{f32, hash::BuildHasherDefault};
|
||||||
use fxhash::FxHasher64;
|
use fxhash::FxHasher64;
|
||||||
@ -128,79 +126,6 @@ impl Dungeon {
|
|||||||
|
|
||||||
pub fn difficulty(&self) -> u32 { self.difficulty }
|
pub fn difficulty(&self) -> u32 { self.difficulty }
|
||||||
|
|
||||||
pub fn apply_to<'a>(
|
|
||||||
&'a self,
|
|
||||||
index: IndexRef,
|
|
||||||
wpos2d: Vec2<i32>,
|
|
||||||
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
|
|
||||||
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
|
|
||||||
) {
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref ENTRANCES: AssetHandle<StructuresGroup> =
|
|
||||||
Structure::load_group("dungeon_entrances");
|
|
||||||
}
|
|
||||||
|
|
||||||
let entrances = ENTRANCES.read();
|
|
||||||
let entrance = &entrances[self.seed as usize % entrances.len()];
|
|
||||||
|
|
||||||
for y in 0..vol.size_xy().y as i32 {
|
|
||||||
for x in 0..vol.size_xy().x as i32 {
|
|
||||||
let offs = Vec2::new(x, y);
|
|
||||||
|
|
||||||
let wpos2d = wpos2d + offs;
|
|
||||||
let rpos = wpos2d - self.origin;
|
|
||||||
|
|
||||||
// Apply the dungeon entrance
|
|
||||||
if let Some(col_sample) = get_column(offs) {
|
|
||||||
for z in entrance.get_bounds().min.z..entrance.get_bounds().max.z {
|
|
||||||
let wpos = Vec3::new(offs.x, offs.y, self.alt + z + ALT_OFFSET);
|
|
||||||
let spos = Vec3::new(rpos.x - TILE_SIZE / 2, rpos.y - TILE_SIZE / 2, z);
|
|
||||||
if let Some(block) = entrance
|
|
||||||
.get(spos)
|
|
||||||
.ok()
|
|
||||||
.copied()
|
|
||||||
.map(|sb| {
|
|
||||||
block_from_structure(
|
|
||||||
index,
|
|
||||||
sb,
|
|
||||||
spos,
|
|
||||||
self.origin,
|
|
||||||
self.seed,
|
|
||||||
col_sample,
|
|
||||||
// TODO: Take environment into account.
|
|
||||||
Block::air,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or(None)
|
|
||||||
{
|
|
||||||
let _ = vol.set(wpos, block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply the dungeon internals
|
|
||||||
let mut z = self.alt + ALT_OFFSET;
|
|
||||||
for floor in &self.floors {
|
|
||||||
z -= floor.total_depth();
|
|
||||||
|
|
||||||
let mut sampler = floor.col_sampler(
|
|
||||||
index,
|
|
||||||
rpos,
|
|
||||||
z,
|
|
||||||
// TODO: Take environment into account.
|
|
||||||
Block::air,
|
|
||||||
);
|
|
||||||
|
|
||||||
for rz in 0..floor.total_depth() {
|
|
||||||
if let Some(block) = sampler(rz).finish() {
|
|
||||||
let _ = vol.set(Vec3::new(offs.x, offs.y, z + rz), block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
|
||||||
pub fn apply_supplement<'a>(
|
pub fn apply_supplement<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
@ -695,10 +620,6 @@ impl Floor {
|
|||||||
|
|
||||||
fn total_depth(&self) -> i32 { self.solid_depth + self.hollow_depth }
|
fn total_depth(&self) -> i32 { self.solid_depth + self.hollow_depth }
|
||||||
|
|
||||||
fn nearest_wall(&self, rpos: Vec2<i32>) -> Option<Vec2<i32>> {
|
|
||||||
tilegrid_nearest_wall(&self.tiles, rpos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find orientation of a position relative to another position
|
// Find orientation of a position relative to another position
|
||||||
#[allow(clippy::collapsible_else_if)]
|
#[allow(clippy::collapsible_else_if)]
|
||||||
fn relative_ori(pos1: Vec2<i32>, pos2: Vec2<i32>) -> u8 {
|
fn relative_ori(pos1: Vec2<i32>, pos2: Vec2<i32>) -> u8 {
|
||||||
@ -708,223 +629,6 @@ impl Floor {
|
|||||||
if pos1.x > pos2.x { 2 } else { 6 }
|
if pos1.x > pos2.x { 2 } else { 6 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unnested_or_patterns)] // TODO: Pending review in #587
|
|
||||||
fn col_sampler<'a>(
|
|
||||||
&'a self,
|
|
||||||
index: IndexRef<'a>,
|
|
||||||
pos: Vec2<i32>,
|
|
||||||
_floor_z: i32,
|
|
||||||
mut with_sprite: impl FnMut(SpriteKind) -> Block,
|
|
||||||
) -> impl FnMut(i32) -> BlockMask + 'a {
|
|
||||||
let rpos = pos - self.tile_offset * TILE_SIZE;
|
|
||||||
let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE));
|
|
||||||
let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2;
|
|
||||||
let rtile_pos = rpos - tile_center;
|
|
||||||
|
|
||||||
let colors = &index.colors.site.dungeon;
|
|
||||||
|
|
||||||
let vacant = BlockMask::new(with_sprite(SpriteKind::Empty), 1);
|
|
||||||
let stone = BlockMask::new(Block::new(BlockKind::Rock, colors.stone.into()), 5);
|
|
||||||
|
|
||||||
let make_spiral_staircase =
|
|
||||||
move |pos: Vec3<i32>, radius: f32, inner_radius: f32, stretch: f32| {
|
|
||||||
if (pos.xy().magnitude_squared() as f32) < inner_radius.powi(2) {
|
|
||||||
stone
|
|
||||||
} else if (pos.xy().magnitude_squared() as f32) < radius.powi(2) {
|
|
||||||
if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch
|
|
||||||
+ pos.z as f32)
|
|
||||||
.rem_euclid(stretch)
|
|
||||||
< 1.5
|
|
||||||
{
|
|
||||||
stone
|
|
||||||
} else {
|
|
||||||
vacant
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BlockMask::nothing()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let make_wall_staircase =
|
|
||||||
move |pos: Vec3<i32>, radius: f32, stretch: f32, height_limit: i32| {
|
|
||||||
if (pos.x.abs().max(pos.y.abs())) as f32 > 0.6 * radius && pos.z <= height_limit {
|
|
||||||
if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch
|
|
||||||
+ pos.z as f32)
|
|
||||||
.rem_euclid(stretch)
|
|
||||||
< 1.0
|
|
||||||
{
|
|
||||||
stone
|
|
||||||
} else {
|
|
||||||
vacant
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vacant
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let make_staircase = move |kind: &StairsKind,
|
|
||||||
pos: Vec3<i32>,
|
|
||||||
radius: f32,
|
|
||||||
inner_radius: f32,
|
|
||||||
stretch: f32,
|
|
||||||
height_limit: i32| {
|
|
||||||
match kind {
|
|
||||||
StairsKind::Spiral => make_spiral_staircase(pos, radius, inner_radius, stretch),
|
|
||||||
StairsKind::WallSpiral => {
|
|
||||||
make_wall_staircase(pos, radius, stretch * 3.0, height_limit)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let wall_thickness = 3.0;
|
|
||||||
let dist_to_wall = self
|
|
||||||
.nearest_wall(rpos)
|
|
||||||
.map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt())
|
|
||||||
.unwrap_or(TILE_SIZE as f32);
|
|
||||||
let tunnel_dist =
|
|
||||||
1.0 - (dist_to_wall - wall_thickness).max(0.0) / (TILE_SIZE as f32 - wall_thickness);
|
|
||||||
|
|
||||||
let floor_sprite = if RandomField::new(7331).chance(Vec3::from(pos), 0.001) {
|
|
||||||
BlockMask::new(
|
|
||||||
with_sprite(
|
|
||||||
match (RandomField::new(1337).get(Vec3::from(pos)) / 2) % 30 {
|
|
||||||
0 => SpriteKind::Apple,
|
|
||||||
1 => SpriteKind::VeloriteFrag,
|
|
||||||
2 => SpriteKind::Velorite,
|
|
||||||
3..=8 => SpriteKind::Mushroom,
|
|
||||||
9..=15 => SpriteKind::FireBowlGround,
|
|
||||||
_ => SpriteKind::ShortGrass,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
1,
|
|
||||||
)
|
|
||||||
} else if let Some(Tile::Room(room)) | Some(Tile::DownStair(room)) =
|
|
||||||
self.tiles.get(tile_pos)
|
|
||||||
{
|
|
||||||
let room = &self.rooms[*room];
|
|
||||||
if RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density * 0.5) {
|
|
||||||
match room.difficulty {
|
|
||||||
0 => BlockMask::new(with_sprite(SpriteKind::DungeonChest0), 1),
|
|
||||||
1 => BlockMask::new(with_sprite(SpriteKind::DungeonChest1), 1),
|
|
||||||
2 => BlockMask::new(with_sprite(SpriteKind::DungeonChest2), 1),
|
|
||||||
3 => BlockMask::new(with_sprite(SpriteKind::DungeonChest3), 1),
|
|
||||||
4 => BlockMask::new(with_sprite(SpriteKind::DungeonChest4), 1),
|
|
||||||
5 => BlockMask::new(with_sprite(SpriteKind::DungeonChest5), 1),
|
|
||||||
_ => BlockMask::new(with_sprite(SpriteKind::Chest), 1),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vacant
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vacant
|
|
||||||
};
|
|
||||||
|
|
||||||
let tunnel_height = if self.final_level { 16.0 } else { 8.0 };
|
|
||||||
let pillar_thickness: i32 = 4;
|
|
||||||
|
|
||||||
move |z| match self.tiles.get(tile_pos) {
|
|
||||||
Some(Tile::Solid) => BlockMask::nothing(),
|
|
||||||
Some(Tile::Tunnel) => {
|
|
||||||
let light_offset: i32 = 7;
|
|
||||||
if (dist_to_wall - wall_thickness) as i32 == 1
|
|
||||||
&& rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor()
|
|
||||||
&& z == 1
|
|
||||||
{
|
|
||||||
let ori =
|
|
||||||
Floor::relative_ori(rpos, self.nearest_wall(rpos).unwrap_or_default());
|
|
||||||
let furniture = SpriteKind::WallSconce;
|
|
||||||
BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1)
|
|
||||||
} else if dist_to_wall >= wall_thickness
|
|
||||||
&& (z as f32) < tunnel_height * (1.0 - tunnel_dist.powi(4))
|
|
||||||
{
|
|
||||||
if z == 0 { floor_sprite } else { vacant }
|
|
||||||
} else {
|
|
||||||
BlockMask::nothing()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(Tile::Room(room)) | Some(Tile::DownStair(room))
|
|
||||||
if dist_to_wall < wall_thickness
|
|
||||||
|| z as f32
|
|
||||||
>= self.rooms[*room].height as f32 * (1.0 - tunnel_dist.powi(4)) =>
|
|
||||||
{
|
|
||||||
BlockMask::nothing()
|
|
||||||
},
|
|
||||||
|
|
||||||
Some(Tile::Room(room)) | Some(Tile::DownStair(room))
|
|
||||||
if self.rooms[*room]
|
|
||||||
.pillars
|
|
||||||
.map(|pillar_space| {
|
|
||||||
tile_pos
|
|
||||||
.map(|e| e.rem_euclid(pillar_space) == 0)
|
|
||||||
.reduce_and()
|
|
||||||
&& rtile_pos.map(|e| e as f32).magnitude_squared()
|
|
||||||
< (pillar_thickness as f32 + 0.5).powi(2)
|
|
||||||
})
|
|
||||||
.unwrap_or(false) =>
|
|
||||||
{
|
|
||||||
if z == 1 && rtile_pos.product() == 0 && rtile_pos.sum().abs() == pillar_thickness {
|
|
||||||
let ori = Floor::relative_ori(rtile_pos, Vec2::zero());
|
|
||||||
let furniture = SpriteKind::WallSconce;
|
|
||||||
BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1)
|
|
||||||
} else if z < self.rooms[*room].height
|
|
||||||
&& rtile_pos.map(|e| e as f32).magnitude_squared()
|
|
||||||
> (pillar_thickness as f32 - 0.5).powi(2)
|
|
||||||
{
|
|
||||||
vacant
|
|
||||||
} else {
|
|
||||||
BlockMask::nothing()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Tile::Room(_)) => {
|
|
||||||
let light_offset = 7;
|
|
||||||
if z == 0 {
|
|
||||||
floor_sprite
|
|
||||||
} else if dist_to_wall as i32 == 4
|
|
||||||
&& rtile_pos.map(|e| e % light_offset == 0).reduce_bitxor()
|
|
||||||
&& z == 1
|
|
||||||
{
|
|
||||||
let ori = Floor::relative_ori(
|
|
||||||
rpos,
|
|
||||||
self.nearest_wall(rpos).unwrap_or_else(Vec2::zero),
|
|
||||||
);
|
|
||||||
let furniture = SpriteKind::WallSconce;
|
|
||||||
BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1)
|
|
||||||
} else {
|
|
||||||
vacant
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Some(Tile::DownStair(_)) => vacant,
|
|
||||||
Some(Tile::UpStair(room, kind)) => {
|
|
||||||
let inner_radius: f32 = 0.5;
|
|
||||||
let stretch = 9;
|
|
||||||
let block = make_staircase(
|
|
||||||
kind,
|
|
||||||
Vec3::new(rtile_pos.x, rtile_pos.y, z),
|
|
||||||
TILE_SIZE as f32 / 2.0,
|
|
||||||
inner_radius,
|
|
||||||
stretch as f32,
|
|
||||||
self.total_depth(),
|
|
||||||
);
|
|
||||||
let furniture = SpriteKind::WallSconce;
|
|
||||||
let ori = Floor::relative_ori(Vec2::zero(), rtile_pos);
|
|
||||||
if z < self.rooms[*room].height {
|
|
||||||
block.resolve_with(vacant)
|
|
||||||
} else if z % stretch == 0 && rtile_pos.x == 0 && rtile_pos.y == -TILE_SIZE / 2 {
|
|
||||||
BlockMask::new(Block::air(furniture).with_ori(ori).unwrap(), 1)
|
|
||||||
} else {
|
|
||||||
make_staircase(
|
|
||||||
kind,
|
|
||||||
Vec3::new(rtile_pos.x, rtile_pos.y, z),
|
|
||||||
TILE_SIZE as f32 / 2.0,
|
|
||||||
inner_radius,
|
|
||||||
stretch as f32,
|
|
||||||
self.total_depth(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => BlockMask::nothing(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
|
fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
|
||||||
|
Loading…
Reference in New Issue
Block a user