mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Update kiddo from 0.2 to 4.2.0
This commit is contained in:
parent
a168ea8dcd
commit
7edbb4449e
98
Cargo.lock
generated
98
Cargo.lock
generated
@ -450,6 +450,12 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.71"
|
||||
@ -1842,6 +1848,12 @@ dependencies = [
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "divrem"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69dde51e8fef5e12c1d65e0929b03d66e4c0c18282bc30ed2ca050ad6f44dd82"
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
@ -1851,6 +1863,12 @@ dependencies = [
|
||||
"libloading 0.8.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "dot_vox"
|
||||
version = "5.1.1"
|
||||
@ -1930,6 +1948,12 @@ version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "elapsed"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f4e5af126dafd0741c2ad62d47f68b28602550102e5f0dd45c8a97fc8b49c29"
|
||||
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.23.0"
|
||||
@ -2166,6 +2190,19 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f6d018fb95a0b59f854aed68ecd96ce2b80af7911b92b1fed3c4b1fa516b91b"
|
||||
|
||||
[[package]]
|
||||
name = "fixed"
|
||||
version = "1.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fc715d38bea7b5bf487fcd79bcf8c209f0b58014f3018a7a19c2b855f472048"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bytemuck",
|
||||
"half",
|
||||
"num-traits",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.1.9"
|
||||
@ -2445,6 +2482,19 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"log",
|
||||
"rustversion",
|
||||
"windows 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generator"
|
||||
version = "0.8.1"
|
||||
@ -3157,6 +3207,12 @@ version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
||||
|
||||
[[package]]
|
||||
name = "init_with"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0175f63815ce00183bf755155ad0cb48c65226c5d17a724e369c25418d2b7699"
|
||||
|
||||
[[package]]
|
||||
name = "inline_tweak"
|
||||
version = "1.1.1"
|
||||
@ -3433,11 +3489,25 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||
|
||||
[[package]]
|
||||
name = "kiddo"
|
||||
version = "0.2.5"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ced2e69cfc5f22f86ccc9ce4ecff9f19917f3083a4bac0f402bdab034d73f1"
|
||||
checksum = "9d2f8d9e1bc7c6919ad2cdc83472a9a4b5ed2ea2c5392c9514fdf958a7920f9a"
|
||||
dependencies = [
|
||||
"az",
|
||||
"divrem",
|
||||
"doc-comment",
|
||||
"elapsed",
|
||||
"fixed",
|
||||
"generator 0.7.5",
|
||||
"init_with",
|
||||
"itertools 0.12.1",
|
||||
"log",
|
||||
"num-traits",
|
||||
"ordered-float 4.2.0",
|
||||
"sorted-vec",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"ubyte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3620,7 +3690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"generator",
|
||||
"generator 0.8.1",
|
||||
"scoped-tls",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@ -6132,6 +6202,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sorted-vec"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6734caf0b6f51addd5eeacca12fb39b2c6c14e8d4f3ac42f3a78955c0467458"
|
||||
|
||||
[[package]]
|
||||
name = "specs"
|
||||
version = "0.20.0"
|
||||
@ -6880,6 +6956,12 @@ version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "ubyte"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f720def6ce1ee2fc44d40ac9ed6d3a59c361c80a75a7aa8e75bb9baed31cf2ea"
|
||||
|
||||
[[package]]
|
||||
name = "unic-langid"
|
||||
version = "0.9.5"
|
||||
@ -7559,6 +7641,7 @@ dependencies = [
|
||||
"enum-map",
|
||||
"enumset",
|
||||
"fallible-iterator",
|
||||
"fixed",
|
||||
"flate2",
|
||||
"fxhash",
|
||||
"hashbrown 0.13.2",
|
||||
@ -8536,6 +8619,15 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.51.1"
|
||||
|
@ -65,7 +65,7 @@ csv = { version = "1.1.3", optional = true }
|
||||
# graphviz exporters
|
||||
petgraph = { version = "0.6", optional = true }
|
||||
# K-d trees used for RRT pathfinding
|
||||
kiddo = { version = "0.2", optional = true }
|
||||
kiddo = { version = "4.2.0", optional = true }
|
||||
clap = { workspace = true, optional = true }
|
||||
|
||||
# Data structures
|
||||
|
@ -8,9 +8,12 @@ use hashbrown::hash_map::DefaultHashBuilder;
|
||||
#[cfg(feature = "rrt_pathfinding")]
|
||||
use hashbrown::HashMap;
|
||||
#[cfg(feature = "rrt_pathfinding")]
|
||||
use kiddo::{distance::squared_euclidean, KdTree}; // For RRT paths (disabled for now)
|
||||
use kiddo::{float::kdtree::KdTree, nearest_neighbour::NearestNeighbour, SquaredEuclidean}; /* For RRT paths (disabled for now) */
|
||||
#[cfg(feature = "rrt_pathfinding")]
|
||||
use rand::distributions::Uniform;
|
||||
use rand::{
|
||||
distributions::{Distribution, Uniform},
|
||||
prelude::IteratorRandom,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
#[cfg(feature = "rrt_pathfinding")]
|
||||
use std::f32::consts::PI;
|
||||
@ -442,7 +445,7 @@ impl Chaser {
|
||||
self.last_search_tgt = Some(tgt);
|
||||
|
||||
// NOTE: Enable air paths when air braking has been figured out
|
||||
let (path, complete) = /*if cfg!(rrt_pathfinding) && traversal_cfg.can_fly {
|
||||
let (path, complete) = /*if cfg!(feature = "rrt_pathfinding") && traversal_cfg.can_fly {
|
||||
find_air_path(vol, pos, tgt, &traversal_cfg)
|
||||
} else */{
|
||||
find_path(&mut self.astar, vol, pos, tgt, &traversal_cfg)
|
||||
@ -718,7 +721,6 @@ where
|
||||
V: BaseVol<Vox = Block> + ReadVol,
|
||||
{
|
||||
let radius = traversal_cfg.node_tolerance;
|
||||
let mut connect = false;
|
||||
let total_dist_sqrd = startf.distance_squared(endf);
|
||||
// First check if a straight line path works
|
||||
if vol
|
||||
@ -731,7 +733,7 @@ where
|
||||
{
|
||||
let mut path = Vec::new();
|
||||
path.push(endf.map(|e| e.floor() as i32));
|
||||
connect = true;
|
||||
let connect = true;
|
||||
(Some(path.into_iter().collect()), connect)
|
||||
// Else use RRTs
|
||||
} else {
|
||||
@ -745,7 +747,7 @@ where
|
||||
//vol.get(*pos).ok().copied().unwrap_or_else(Block::empty).
|
||||
// is_fluid();
|
||||
};
|
||||
informed_rrt_connect(start, end, is_traversable)
|
||||
informed_rrt_connect(vol, startf, endf, is_traversable, radius)
|
||||
}
|
||||
}
|
||||
|
||||
@ -760,11 +762,17 @@ where
|
||||
/// with narrow gaps, such as navigating a maze.
|
||||
/// Returns a path and whether that path is complete or not.
|
||||
#[cfg(feature = "rrt_pathfinding")]
|
||||
fn informed_rrt_connect(
|
||||
start: Vec3<f32>,
|
||||
end: Vec3<f32>,
|
||||
fn informed_rrt_connect<V>(
|
||||
vol: &V,
|
||||
startf: Vec3<f32>,
|
||||
endf: Vec3<f32>,
|
||||
is_valid_edge: impl Fn(&Vec3<f32>, &Vec3<f32>) -> bool,
|
||||
) -> (Option<Path<Vec3<i32>>>, bool) {
|
||||
radius: f32,
|
||||
) -> (Option<Path<Vec3<i32>>>, bool)
|
||||
where
|
||||
V: BaseVol<Vox = Block> + ReadVol,
|
||||
{
|
||||
const MAX_POINTS: usize = 7000;
|
||||
let mut path = Vec::new();
|
||||
|
||||
// Each tree has a vector of nodes
|
||||
@ -784,20 +792,16 @@ fn informed_rrt_connect(
|
||||
let mut path2 = Vec::new();
|
||||
|
||||
// K-d trees are used to find the closest nodes rapidly
|
||||
let mut kdtree1 = KdTree::new();
|
||||
let mut kdtree2 = KdTree::new();
|
||||
let mut kdtree1: KdTree<f32, usize, 3, 32, u32> = KdTree::with_capacity(MAX_POINTS);
|
||||
let mut kdtree2: KdTree<f32, usize, 3, 32, u32> = KdTree::with_capacity(MAX_POINTS);
|
||||
|
||||
// Add the start as the first node of the first k-d tree
|
||||
kdtree1
|
||||
.add(&[startf.x, startf.y, startf.z], node_index1)
|
||||
.unwrap_or_default();
|
||||
kdtree1.add(&[startf.x, startf.y, startf.z], node_index1);
|
||||
nodes1.push(startf);
|
||||
node_index1 += 1;
|
||||
|
||||
// Add the end as the first node of the second k-d tree
|
||||
kdtree2
|
||||
.add(&[endf.x, endf.y, endf.z], node_index2)
|
||||
.unwrap_or_default();
|
||||
kdtree2.add(&[endf.x, endf.y, endf.z], node_index2);
|
||||
nodes2.push(endf);
|
||||
node_index2 += 1;
|
||||
|
||||
@ -810,8 +814,8 @@ fn informed_rrt_connect(
|
||||
// sample spheroid volume. This increases in value until a path is found.
|
||||
let mut search_parameter = 0.01;
|
||||
|
||||
// Maximum of 7000 iterations
|
||||
for _i in 0..7000 {
|
||||
// Maximum of MAX_POINTS iterations
|
||||
for _i in 0..MAX_POINTS {
|
||||
if connect {
|
||||
break;
|
||||
}
|
||||
@ -824,17 +828,19 @@ fn informed_rrt_connect(
|
||||
|
||||
// Find the nearest nodes to the the sampled point
|
||||
let nearest_index1 = kdtree1
|
||||
.nearest_one(
|
||||
&[sampled_point1.x, sampled_point1.y, sampled_point1.z],
|
||||
&squared_euclidean,
|
||||
)
|
||||
.map_or(0, |n| *n.1);
|
||||
.nearest_one::<SquaredEuclidean>(&[
|
||||
sampled_point1.x,
|
||||
sampled_point1.y,
|
||||
sampled_point1.z,
|
||||
])
|
||||
.item;
|
||||
let nearest_index2 = kdtree2
|
||||
.nearest_one(
|
||||
&[sampled_point2.x, sampled_point2.y, sampled_point2.z],
|
||||
&squared_euclidean,
|
||||
)
|
||||
.map_or(0, |n| *n.1);
|
||||
.nearest_one::<SquaredEuclidean>(&[
|
||||
sampled_point2.x,
|
||||
sampled_point2.y,
|
||||
sampled_point2.z,
|
||||
])
|
||||
.item;
|
||||
let nearest1 = nodes1[nearest_index1];
|
||||
let nearest2 = nodes2[nearest_index2];
|
||||
|
||||
@ -844,49 +850,51 @@ fn informed_rrt_connect(
|
||||
|
||||
// Ensure the new nodes are valid/traversable
|
||||
if is_valid_edge(&nearest1, &new_point1) {
|
||||
kdtree1
|
||||
.add(&[new_point1.x, new_point1.y, new_point1.z], node_index1)
|
||||
.unwrap_or_default();
|
||||
kdtree1.add(&[new_point1.x, new_point1.y, new_point1.z], node_index1);
|
||||
nodes1.push(new_point1);
|
||||
parents1.insert(node_index1, nearest_index1);
|
||||
node_index1 += 1;
|
||||
// Check if the trees connect
|
||||
if let Ok((check, index)) = kdtree2.nearest_one(
|
||||
&[new_point1.x, new_point1.y, new_point1.z],
|
||||
&squared_euclidean,
|
||||
) {
|
||||
if check < radius {
|
||||
let connection = nodes2[*index];
|
||||
connection2_idx = *index;
|
||||
nodes1.push(connection);
|
||||
connection1_idx = nodes1.len() - 1;
|
||||
parents1.insert(node_index1, node_index1 - 1);
|
||||
connect = true;
|
||||
}
|
||||
let NearestNeighbour {
|
||||
distance: check,
|
||||
item: index,
|
||||
} = kdtree2.nearest_one::<SquaredEuclidean>(&[
|
||||
new_point1.x,
|
||||
new_point1.y,
|
||||
new_point1.z,
|
||||
]);
|
||||
if check < radius {
|
||||
let connection = nodes2[index];
|
||||
connection2_idx = index;
|
||||
nodes1.push(connection);
|
||||
connection1_idx = nodes1.len() - 1;
|
||||
parents1.insert(node_index1, node_index1 - 1);
|
||||
connect = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Repeat the validity check for the second tree
|
||||
if is_valid_edge(&nearest2, &new_point2) {
|
||||
kdtree2
|
||||
.add(&[new_point2.x, new_point2.y, new_point1.z], node_index2)
|
||||
.unwrap_or_default();
|
||||
kdtree2.add(&[new_point2.x, new_point2.y, new_point1.z], node_index2);
|
||||
nodes2.push(new_point2);
|
||||
parents2.insert(node_index2, nearest_index2);
|
||||
node_index2 += 1;
|
||||
// Again check for a connection
|
||||
if let Ok((check, index)) = kdtree1.nearest_one(
|
||||
&[new_point2.x, new_point2.y, new_point1.z],
|
||||
&squared_euclidean,
|
||||
) {
|
||||
if check < radius {
|
||||
let connection = nodes1[*index];
|
||||
connection1_idx = *index;
|
||||
nodes2.push(connection);
|
||||
connection2_idx = nodes2.len() - 1;
|
||||
parents2.insert(node_index2, node_index2 - 1);
|
||||
connect = true;
|
||||
}
|
||||
let NearestNeighbour {
|
||||
distance: check,
|
||||
item: index,
|
||||
} = kdtree1.nearest_one::<SquaredEuclidean>(&[
|
||||
new_point2.x,
|
||||
new_point2.y,
|
||||
new_point1.z,
|
||||
]);
|
||||
if check < radius {
|
||||
let connection = nodes1[index];
|
||||
connection1_idx = index;
|
||||
nodes2.push(connection);
|
||||
connection2_idx = nodes2.len() - 1;
|
||||
parents2.insert(node_index2, node_index2 - 1);
|
||||
connect = true;
|
||||
}
|
||||
}
|
||||
// Increase the search parameter to widen the sample volume
|
||||
@ -915,14 +923,14 @@ fn informed_rrt_connect(
|
||||
// If the trees did not connect, construct a path from the start to
|
||||
// the closest node to the end
|
||||
let mut current_node_index1 = kdtree1
|
||||
.nearest_one(&[endf.x, endf.y, endf.z], &squared_euclidean)
|
||||
.map_or(0, |c| *c.1);
|
||||
.nearest_one::<SquaredEuclidean>(&[endf.x, endf.y, endf.z])
|
||||
.item;
|
||||
// Attempt to pick a node other than the start node
|
||||
for _i in 0..3 {
|
||||
if current_node_index1 == 0
|
||||
|| nodes1[current_node_index1].distance_squared(startf) < 4.0
|
||||
{
|
||||
if let Some(index) = parents1.values().choose(&mut thread_rng()) {
|
||||
if let Some(index) = parents1.values().into_iter().choose(&mut thread_rng()) {
|
||||
current_node_index1 = *index;
|
||||
} else {
|
||||
break;
|
||||
@ -973,6 +981,7 @@ fn informed_rrt_connect(
|
||||
node = path[node_idx];
|
||||
}
|
||||
path = new_path;
|
||||
(Some(path.into_iter().collect()), connect)
|
||||
}
|
||||
|
||||
/// Returns a random point within a radially symmetrical ellipsoid with given
|
||||
|
@ -49,7 +49,8 @@ rayon = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
ron = { workspace = true }
|
||||
# inline_tweak = { workspace = true, features = ["derive"] }
|
||||
kiddo = "0.2"
|
||||
kiddo = "4.2.0"
|
||||
fixed = "1"
|
||||
strum = { workspace = true }
|
||||
|
||||
# compression benchmarks
|
||||
|
@ -493,7 +493,8 @@ impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
||||
}
|
||||
}
|
||||
|
||||
use kiddo::KdTree;
|
||||
use fixed::types::U32F0;
|
||||
use kiddo::fixed::{distance::SquaredEuclidean, kdtree::KdTree};
|
||||
use rstar::{PointDistance, RTree, RTreeObject, RTreeParams};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -548,17 +549,16 @@ lazy_static::lazy_static! {
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
pub static ref PALETTE_KDTREE: HashMap<BlockKind, KdTree<f32, u8, 3>> = {
|
||||
pub static ref PALETTE_KDTREE: HashMap<BlockKind, KdTree<U32F0, u16, 3, 32, u32>> = {
|
||||
let ron_bytes = include_bytes!("palettes.ron");
|
||||
let palettes: HashMap<BlockKind, Vec<Rgb<u8>>> =
|
||||
ron::de::from_bytes(ron_bytes).expect("palette should parse");
|
||||
palettes
|
||||
.into_iter()
|
||||
.map(|(k, v)| {
|
||||
let mut tree = KdTree::new();
|
||||
let mut tree: KdTree<U32F0, u16, 3, 32, u32> = KdTree::new();
|
||||
for (i, rgb) in v.into_iter().enumerate() {
|
||||
tree.add(&[rgb.r as f32, rgb.g as f32, rgb.b as f32], i as u8)
|
||||
.expect("kdtree insert should succeed");
|
||||
tree.add(&[U32F0::from(rgb.r), U32F0::from(rgb.g), U32F0::from(rgb.b)], i as u16);
|
||||
}
|
||||
(k, tree)
|
||||
})
|
||||
@ -570,14 +570,18 @@ pub trait NearestNeighbor {
|
||||
fn nearest_neighbor(&self, x: &Rgb<u8>) -> Option<u8>;
|
||||
}
|
||||
|
||||
impl NearestNeighbor for KdTree<f32, u8, 3> {
|
||||
impl NearestNeighbor for KdTree<U32F0, u16, 3, 32, u32> {
|
||||
fn nearest_neighbor(&self, x: &Rgb<u8>) -> Option<u8> {
|
||||
self.nearest_one(
|
||||
&[x.r as f32, x.g as f32, x.b as f32],
|
||||
&kiddo::distance::squared_euclidean,
|
||||
Some(
|
||||
self.nearest_one::<SquaredEuclidean>(&[
|
||||
U32F0::from(x.r),
|
||||
U32F0::from(x.g),
|
||||
U32F0::from(x.b),
|
||||
])
|
||||
.item
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
)
|
||||
.map(|(_, i)| *i)
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,17 +4,28 @@ use common::{
|
||||
vol::{IntoVolIterator, RectVolSize},
|
||||
};
|
||||
use fallible_iterator::FallibleIterator;
|
||||
use kiddo::{distance::squared_euclidean, KdTree};
|
||||
use fixed::{
|
||||
types::{extra::U0, U32F0, U8F0},
|
||||
FixedU8,
|
||||
};
|
||||
use kiddo::{
|
||||
fixed::{distance::SquaredEuclidean, kdtree::KdTree},
|
||||
nearest_neighbour::NearestNeighbour,
|
||||
};
|
||||
use num_traits::identities::{One, Zero};
|
||||
use rayon::{
|
||||
iter::{IntoParallelIterator, ParallelIterator},
|
||||
ThreadPoolBuilder,
|
||||
};
|
||||
use rusqlite::{Connection, ToSql, Transaction, TransactionBehavior};
|
||||
//use serde::{Serialize, Deserialize};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{HashMap, HashSet},
|
||||
error::Error,
|
||||
fs::File,
|
||||
io::Write,
|
||||
ops::{Add, Mul, SubAssign},
|
||||
str::FromStr,
|
||||
sync::mpsc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
@ -25,6 +36,75 @@ use veloren_world::{
|
||||
World,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Hash, Eq, PartialEq /* , Serialize, Deserialize */)]
|
||||
struct KiddoRgb(Rgb<U8F0>);
|
||||
|
||||
impl PartialOrd for KiddoRgb {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
|
||||
}
|
||||
|
||||
impl Ord for KiddoRgb {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
(self.0.r, self.0.g, self.0.b).cmp(&(other.0.r, other.0.g, other.0.b))
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero for KiddoRgb {
|
||||
fn zero() -> Self { KiddoRgb(Rgb::zero()) }
|
||||
|
||||
fn is_zero(&self) -> bool { self == &Self::zero() }
|
||||
}
|
||||
|
||||
impl One for KiddoRgb {
|
||||
fn one() -> Self { KiddoRgb(Rgb::one()) }
|
||||
|
||||
fn is_one(&self) -> bool { self == &Self::one() }
|
||||
}
|
||||
|
||||
impl SubAssign for KiddoRgb {
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
*self = Self(Rgb {
|
||||
r: self.0.r - other.0.r,
|
||||
g: self.0.g - other.0.g,
|
||||
b: self.0.b - other.0.b,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for KiddoRgb {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, other: Self) -> Self {
|
||||
Self(Rgb {
|
||||
r: self.0.r + other.0.r,
|
||||
g: self.0.g + other.0.g,
|
||||
b: self.0.b + other.0.b,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul for KiddoRgb {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
Self(Rgb {
|
||||
r: self.0.r * rhs.0.r,
|
||||
g: self.0.g * rhs.0.g,
|
||||
b: self.0.b * rhs.0.b,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rgb<u8>> for KiddoRgb {
|
||||
fn from(value: Rgb<u8>) -> Self {
|
||||
Self(Rgb {
|
||||
r: FixedU8::<U0>::from_num(value.r),
|
||||
g: FixedU8::<U0>::from_num(value.g),
|
||||
b: FixedU8::<U0>::from_num(value.b),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn block_statistics_db(db_path: &str) -> Result<Connection, Box<dyn Error>> {
|
||||
let conn = Connection::open(db_path)?;
|
||||
#[rustfmt::skip]
|
||||
@ -95,31 +175,40 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
|
||||
if existing_chunks.contains(&(x, y)) {
|
||||
return;
|
||||
}
|
||||
println!("Generating chunk at ({}, {})", x, y);
|
||||
let start_time = SystemTime::now();
|
||||
if let Ok((chunk, _supplement)) =
|
||||
world.generate_chunk(index.as_index_ref(), Vec2::new(x, y), None, || false, None)
|
||||
{
|
||||
let end_time = SystemTime::now();
|
||||
// TODO: can kiddo be made to work without the `Float` bound, so we can use
|
||||
// `KdTree<u8, (), 3>` (currently it uses 15 bytes per point instead of 3)?
|
||||
let mut block_colors = KdTree::<f32, Rgb<u8>, 3>::new();
|
||||
// TODO: The KiddoRgb wrapper type is necessary to satisfy trait bounds.
|
||||
// We store the colors twice currently, once as coordinates and another time
|
||||
// as Content. Kiddo version 5.x is supposed to add the ability to have
|
||||
// Content be (), which would be useful here. Once that's added, do that.
|
||||
// TODO: dist_sq is the same type as the coordinates, and since squared
|
||||
// euclidean distances between colors go way higher than 255,
|
||||
// we're using a U32F0 here instead of the optimal U8F0 (A U16F0
|
||||
// works too, but it could theoretically still overflow so U32F0
|
||||
// is used to be safe). If this ever changes, replace U32F0 with
|
||||
// U8F0.
|
||||
let mut block_colors: KdTree<U32F0, KiddoRgb, 3, 32, u32> = KdTree::new();
|
||||
let mut block_counts = HashMap::new();
|
||||
let mut sprite_counts = HashMap::new();
|
||||
let lo = Vec3::new(0, 0, chunk.get_min_z());
|
||||
let hi = TerrainChunkSize::RECT_SIZE.as_().with_z(chunk.get_max_z());
|
||||
let height = chunk.get_max_z() - chunk.get_min_z();
|
||||
for (_, block) in chunk.vol_iter(lo, hi) {
|
||||
let mut rgb = block.get_color().unwrap_or_else(|| Rgb::new(0, 0, 0));
|
||||
let color: [f32; 3] = [rgb.r as _, rgb.g as _, rgb.b as _];
|
||||
if let Ok((dist, nearest)) =
|
||||
block_colors.nearest_one(&color, &squared_euclidean)
|
||||
{
|
||||
if dist < (5.0f32).powf(2.0) {
|
||||
rgb = *nearest;
|
||||
}
|
||||
let mut rgb =
|
||||
KiddoRgb::from(block.get_color().unwrap_or_else(|| Rgb::new(0, 0, 0)));
|
||||
let color: [U32F0; 3] = [rgb.0.r.into(), rgb.0.g.into(), rgb.0.b.into()];
|
||||
let NearestNeighbour {
|
||||
distance: dist_sq,
|
||||
item: nearest,
|
||||
} = block_colors.nearest_one::<SquaredEuclidean>(&color);
|
||||
if dist_sq < 5_u32.pow(2) {
|
||||
rgb = nearest;
|
||||
} else {
|
||||
block_colors.add(&color, rgb);
|
||||
}
|
||||
let _ = block_colors.add(&color, rgb);
|
||||
*block_counts.entry((block.kind(), rgb)).or_insert(0) += 1;
|
||||
if let Some(sprite) = block.get_sprite() {
|
||||
*sprite_counts.entry(sprite).or_insert(0) += 1;
|
||||
@ -139,6 +228,7 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
|
||||
});
|
||||
let mut tx = Transaction::new_unchecked(&conn, TransactionBehavior::Deferred)?;
|
||||
let mut i = 0;
|
||||
let mut j = 0;
|
||||
while let Ok((x, y, height, start_time, end_time, block_counts, sprite_counts)) = rx.recv() {
|
||||
#[rustfmt::skip]
|
||||
let mut insert_block = tx.prepare_cached("
|
||||
@ -155,15 +245,14 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
|
||||
REPLACE INTO chunk (xcoord, ycoord, height, start_time, end_time)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||
")?;
|
||||
println!("Inserting results for chunk at ({}, {}): {}", x, y, i);
|
||||
for ((kind, color), count) in block_counts.iter() {
|
||||
insert_block.execute([
|
||||
&x as &dyn ToSql,
|
||||
&y,
|
||||
&format!("{:?}", kind),
|
||||
&color.r,
|
||||
&color.g,
|
||||
&color.b,
|
||||
&color.0.r.to_num::<u8>(),
|
||||
&color.0.g.to_num::<u8>(),
|
||||
&color.0.b.to_num::<u8>(),
|
||||
&count,
|
||||
])?;
|
||||
}
|
||||
@ -174,12 +263,13 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
|
||||
let end_time = end_time.duration_since(UNIX_EPOCH)?.as_secs_f64();
|
||||
insert_chunk.execute([&x as &dyn ToSql, &y, &height, &start_time, &end_time])?;
|
||||
if i % 32 == 0 {
|
||||
println!("Committing last 32 chunks");
|
||||
println!("Committing hunk of 32 chunks: {}", j);
|
||||
drop(insert_block);
|
||||
drop(insert_sprite);
|
||||
drop(insert_chunk);
|
||||
tx.commit()?;
|
||||
tx = Transaction::new_unchecked(&conn, TransactionBehavior::Deferred)?;
|
||||
j += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
@ -189,12 +279,12 @@ fn generate(db_path: &str, ymin: Option<i32>, ymax: Option<i32>) -> Result<(), B
|
||||
fn palette(conn: Connection) -> Result<(), Box<dyn Error>> {
|
||||
let mut stmt =
|
||||
conn.prepare("SELECT kind, r, g, b, SUM(quantity) FROM block GROUP BY kind, r, g, b")?;
|
||||
let mut block_colors: HashMap<BlockKind, Vec<(Rgb<u8>, i64)>> = HashMap::new();
|
||||
let mut block_colors: HashMap<BlockKind, Vec<(KiddoRgb, i64)>> = HashMap::new();
|
||||
|
||||
let mut rows = stmt.query([])?;
|
||||
while let Some(row) = rows.next()? {
|
||||
let kind = BlockKind::from_str(&row.get::<_, String>(0)?)?;
|
||||
let rgb: Rgb<u8> = Rgb::new(row.get(1)?, row.get(2)?, row.get(3)?);
|
||||
let rgb: KiddoRgb = KiddoRgb::from(Rgb::new(row.get(1)?, row.get(2)?, row.get(3)?));
|
||||
let count: i64 = row.get(4)?;
|
||||
block_colors.entry(kind).or_default().push((rgb, count));
|
||||
}
|
||||
@ -202,7 +292,7 @@ fn palette(conn: Connection) -> Result<(), Box<dyn Error>> {
|
||||
v.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
}
|
||||
|
||||
let mut palettes: HashMap<BlockKind, Vec<Rgb<u8>>> = HashMap::new();
|
||||
let mut palettes: HashMap<BlockKind, Vec<KiddoRgb>> = HashMap::new();
|
||||
for (kind, colors) in block_colors.iter() {
|
||||
let palette = palettes.entry(*kind).or_default();
|
||||
if colors.len() <= 256 {
|
||||
@ -213,24 +303,43 @@ fn palette(conn: Connection) -> Result<(), Box<dyn Error>> {
|
||||
continue;
|
||||
}
|
||||
let mut radius = 1024.0;
|
||||
let mut tree = KdTree::<f32, Rgb<u8>, 3>::new();
|
||||
let mut tree: KdTree<U32F0, KiddoRgb, 3, 256, u32> = KdTree::new();
|
||||
while palette.len() < 256 {
|
||||
if let Some((color, _)) = colors.iter().find(|(color, _)| {
|
||||
tree.nearest_one(
|
||||
&[color.r as f32, color.g as f32, color.b as f32],
|
||||
&squared_euclidean,
|
||||
)
|
||||
.map(|(dist, _)| dist > radius)
|
||||
.unwrap_or(true)
|
||||
tree.nearest_one::<SquaredEuclidean>(&[
|
||||
color.0.r.into(),
|
||||
color.0.g.into(),
|
||||
color.0.b.into(),
|
||||
])
|
||||
.distance
|
||||
> radius
|
||||
}) {
|
||||
palette.push(*color);
|
||||
tree.add(&[color.r as f32, color.g as f32, color.b as f32], *color)?;
|
||||
tree.add(
|
||||
&[color.0.r.into(), color.0.g.into(), color.0.b.into()],
|
||||
*color,
|
||||
);
|
||||
println!("{:?}, {:?}: {:?}", kind, radius, *color);
|
||||
} else {
|
||||
radius -= 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
let palettes: HashMap<BlockKind, Vec<Rgb<u8>>> = palettes
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
*k,
|
||||
v.iter()
|
||||
.map(|c| Rgb {
|
||||
r: c.0.r.to_num::<u8>(),
|
||||
g: c.0.g.to_num::<u8>(),
|
||||
b: c.0.b.to_num::<u8>(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let mut f = File::create("palettes.ron")?;
|
||||
let pretty = ron::ser::PrettyConfig::default().depth_limit(2);
|
||||
write!(f, "{}", ron::ser::to_string_pretty(&palettes, pretty)?)?;
|
||||
|
@ -9,7 +9,7 @@ use common::{
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
terrain::{Structure as PrefabStructure, StructuresGroup},
|
||||
};
|
||||
use kiddo::{distance::squared_euclidean, KdTree};
|
||||
use kiddo::{float::kdtree::KdTree, SquaredEuclidean};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
@ -1974,13 +1974,14 @@ impl Tunnels {
|
||||
where
|
||||
F: Fn(Vec3<i32>, Vec3<i32>) -> bool,
|
||||
{
|
||||
const MAX_POINTS: usize = 7000;
|
||||
let mut nodes = Vec::new();
|
||||
let mut node_index: usize = 0;
|
||||
|
||||
// HashMap<ChildNode, ParentNode>
|
||||
let mut parents = HashMap::new();
|
||||
|
||||
let mut kdtree = KdTree::new();
|
||||
let mut kdtree: KdTree<f32, usize, 3, 32, u32> = KdTree::with_capacity(MAX_POINTS);
|
||||
let startf = start.map(|a| (a + 1) as f32);
|
||||
let endf = end.map(|a| (a + 1) as f32);
|
||||
|
||||
@ -1995,14 +1996,12 @@ impl Tunnels {
|
||||
startf.z.max(endf.z),
|
||||
);
|
||||
|
||||
kdtree
|
||||
.add(&[startf.x, startf.y, startf.z], node_index)
|
||||
.ok()?;
|
||||
kdtree.add(&[startf.x, startf.y, startf.z], node_index);
|
||||
nodes.push(startf);
|
||||
node_index += 1;
|
||||
let mut connect = false;
|
||||
|
||||
for _i in 0..7000 {
|
||||
for _i in 0..MAX_POINTS {
|
||||
let radius: f32 = rng.gen_range(radius_range.0..radius_range.1);
|
||||
let radius_sqrd = radius.powi(2);
|
||||
if connect {
|
||||
@ -2013,13 +2012,13 @@ impl Tunnels {
|
||||
rng.gen_range(min.y - 20.0..max.y + 20.0),
|
||||
rng.gen_range(min.z - 20.0..max.z - 7.0),
|
||||
);
|
||||
let nearest_index = *kdtree
|
||||
.nearest_one(
|
||||
&[sampled_point.x, sampled_point.y, sampled_point.z],
|
||||
&squared_euclidean,
|
||||
)
|
||||
.ok()?
|
||||
.1;
|
||||
let nearest_index = kdtree
|
||||
.nearest_one::<SquaredEuclidean>(&[
|
||||
sampled_point.x,
|
||||
sampled_point.y,
|
||||
sampled_point.z,
|
||||
])
|
||||
.item;
|
||||
let nearest = nodes[nearest_index];
|
||||
let dist_sqrd = sampled_point.distance_squared(nearest);
|
||||
let new_point = if dist_sqrd > radius_sqrd {
|
||||
@ -2031,24 +2030,21 @@ impl Tunnels {
|
||||
nearest.map(|e| e.floor() as i32),
|
||||
new_point.map(|e| e.floor() as i32),
|
||||
) {
|
||||
kdtree
|
||||
.add(&[new_point.x, new_point.y, new_point.z], node_index)
|
||||
.ok()?;
|
||||
kdtree.add(&[new_point.x, new_point.y, new_point.z], node_index);
|
||||
nodes.push(new_point);
|
||||
parents.insert(node_index, nearest_index);
|
||||
node_index += 1;
|
||||
}
|
||||
if new_point.distance_squared(endf) < radius.powi(2) {
|
||||
if new_point.distance_squared(endf) < radius_sqrd {
|
||||
connect = true;
|
||||
}
|
||||
}
|
||||
|
||||
let mut path = Vec::new();
|
||||
let nearest_index = *kdtree
|
||||
.nearest_one(&[endf.x, endf.y, endf.z], &squared_euclidean)
|
||||
.ok()?
|
||||
.1;
|
||||
kdtree.add(&[endf.x, endf.y, endf.z], node_index).ok()?;
|
||||
let nearest_index = kdtree
|
||||
.nearest_one::<SquaredEuclidean>(&[endf.x, endf.y, endf.z])
|
||||
.item;
|
||||
kdtree.add(&[endf.x, endf.y, endf.z], node_index);
|
||||
nodes.push(endf);
|
||||
parents.insert(node_index, nearest_index);
|
||||
path.push(endf);
|
||||
|
Loading…
Reference in New Issue
Block a user