Update kiddo from 0.2 to 4.2.0

This commit is contained in:
Youser Nayme 2024-06-03 23:12:53 +00:00 committed by Marcel
parent a168ea8dcd
commit 7edbb4449e
7 changed files with 342 additions and 131 deletions

98
Cargo.lock generated
View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()
}
}

View File

@ -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)?)?;

View File

@ -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);