mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Rename pos fields of Cube and Cylinder for enhanced clarity and added a few tests to the find_dist module
This commit is contained in:
parent
7427367d96
commit
ea275e320f
@ -16,7 +16,7 @@ pub trait FindDist<T> {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Cylinder {
|
||||
/// Center of the cylinder
|
||||
pub pos: Vec3<f32>,
|
||||
pub center: Vec3<f32>,
|
||||
/// Radius of the cylinder
|
||||
pub radius: f32,
|
||||
/// Height of the cylinder
|
||||
@ -26,8 +26,8 @@ pub struct Cylinder {
|
||||
impl Cylinder {
|
||||
fn aabb(&self) -> Aabb<f32> {
|
||||
Aabb {
|
||||
min: self.pos - Vec3::new(self.radius, self.radius, self.height) / 2.0,
|
||||
max: self.pos + Vec3::new(self.radius, self.radius, self.height) / 2.0,
|
||||
min: self.center - Vec3::new(self.radius, self.radius, self.height / 2.0),
|
||||
max: self.center + Vec3::new(self.radius, self.radius, self.height / 2.0),
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ impl Cylinder {
|
||||
.unwrap_or((-0.5 * z_limit_modifier, 0.5 * z_limit_modifier));
|
||||
|
||||
Self {
|
||||
pos: pos + Vec3::unit_z() * (z_top + z_bottom) / 2.0,
|
||||
center: pos + Vec3::unit_z() * (z_top + z_bottom) / 2.0,
|
||||
radius,
|
||||
height: z_top - z_bottom,
|
||||
}
|
||||
@ -60,7 +60,7 @@ impl Cylinder {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Cube {
|
||||
/// The position of min corner of the cube
|
||||
pub pos: Vec3<f32>,
|
||||
pub min: Vec3<f32>,
|
||||
/// The side length of the cube
|
||||
pub side_length: f32,
|
||||
}
|
||||
@ -69,8 +69,8 @@ impl FindDist<Cylinder> for Cube {
|
||||
#[inline]
|
||||
fn approx_in_range(self, other: Cylinder, range: f32) -> bool {
|
||||
let cube_plus_range_aabb = Aabb {
|
||||
min: self.pos - Vec3::from(range),
|
||||
max: self.pos + Vec3::from(range),
|
||||
min: self.min - range,
|
||||
max: self.min + self.side_length + range,
|
||||
};
|
||||
let cylinder_aabb = other.aabb();
|
||||
|
||||
@ -80,15 +80,15 @@ impl FindDist<Cylinder> for Cube {
|
||||
#[inline]
|
||||
fn min_distance(self, other: Cylinder) -> f32 {
|
||||
// Distance between centers along the z-axis
|
||||
let z_center_dist = (self.pos.z + self.side_length / 2.0 - other.pos.z).abs();
|
||||
let z_center_dist = (self.min.z + self.side_length / 2.0 - other.center.z).abs();
|
||||
// Distance between surfaces projected onto the z-axis
|
||||
let z_dist = (z_center_dist - (self.side_length + other.height) / 2.0).max(0.0);
|
||||
// Distance between shapes projected onto the xy plane as a square/circle
|
||||
let square_aabr = Aabr {
|
||||
min: self.pos.xy(),
|
||||
max: self.pos.xy() + self.side_length,
|
||||
min: self.min.xy(),
|
||||
max: self.min.xy() + self.side_length,
|
||||
};
|
||||
let xy_dist = (square_aabr.distance_to_point(other.pos.xy()) - other.radius).max(0.0);
|
||||
let xy_dist = (square_aabr.distance_to_point(other.center.xy()) - other.radius).max(0.0);
|
||||
// Overall distance by pythagoras
|
||||
(z_dist.powi(2) + xy_dist.powi(2)).sqrt()
|
||||
}
|
||||
@ -115,12 +115,12 @@ impl FindDist<Cylinder> for Cylinder {
|
||||
#[inline]
|
||||
fn min_distance(self, other: Cylinder) -> f32 {
|
||||
// Distance between centers along the z-axis
|
||||
let z_center_dist = (self.pos.z - other.pos.z).abs();
|
||||
let z_center_dist = (self.center.z - other.center.z).abs();
|
||||
// Distance between surfaces projected onto the z-axis
|
||||
let z_dist = (z_center_dist - (self.height + other.height) / 2.0).max(0.0);
|
||||
// Distance between shapes projected onto the xy plane as a circles
|
||||
let xy_dist =
|
||||
(self.pos.xy().distance(other.pos.xy()) - self.radius - other.radius).max(0.0);
|
||||
(self.center.xy().distance(other.center.xy()) - self.radius - other.radius).max(0.0);
|
||||
// Overall distance by pythagoras
|
||||
(z_dist.powi(2) + xy_dist.powi(2)).sqrt()
|
||||
}
|
||||
@ -139,12 +139,80 @@ impl FindDist<Vec3<f32>> for Cylinder {
|
||||
#[inline]
|
||||
fn min_distance(self, other: Vec3<f32>) -> f32 {
|
||||
// Distance between center and point along the z-axis
|
||||
let z_center_dist = (self.pos.z - other.z).abs();
|
||||
let z_center_dist = (self.center.z - other.z).abs();
|
||||
// Distance between surface and point projected onto the z-axis
|
||||
let z_dist = (z_center_dist - self.height / 2.0).max(0.0);
|
||||
// Distance between shapes projected onto the xy plane
|
||||
let xy_dist = (self.pos.xy().distance(other.xy()) - self.radius).max(0.0);
|
||||
let xy_dist = (self.center.xy().distance(other.xy()) - self.radius).max(0.0);
|
||||
// Overall distance by pythagoras
|
||||
(z_dist.powi(2) + xy_dist.powi(2)).sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn cylinder_vs_cube() {
|
||||
//let offset = Vec3::new(1213.323, 5424.0, -231.0);
|
||||
let offset = Vec3::zero();
|
||||
let cylinder = Cylinder {
|
||||
center: Vec3::new(0.0, 0.0, 0.0) + offset,
|
||||
radius: 2.0,
|
||||
height: 4.0,
|
||||
};
|
||||
|
||||
let cube = Cube {
|
||||
min: Vec3::new(-0.5, -0.5, -0.5) + offset,
|
||||
side_length: 1.0,
|
||||
};
|
||||
|
||||
assert!(cube.approx_in_range(cylinder, 0.0));
|
||||
assert!(cube.min_distance(cylinder).abs() < f32::EPSILON);
|
||||
assert!((cube.min_distance(cylinder) - cylinder.min_distance(cube)).abs() < 0.001);
|
||||
|
||||
let cube = Cube {
|
||||
min: cube.min + Vec3::unit_x() * 50.0,
|
||||
side_length: 1.0,
|
||||
};
|
||||
|
||||
assert!(!cube.approx_in_range(cylinder, 5.0)); // Note: technically it is not breaking any promises if this returns true but this will be useful as a warning if the filtering is not tight as we were expecting
|
||||
assert!(cube.approx_in_range(cylinder, 47.51));
|
||||
assert!((cube.min_distance(cylinder) - 47.5).abs() < 0.001);
|
||||
assert!((cube.min_distance(cylinder) - cylinder.min_distance(cube)).abs() < 0.001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_size_cylinder() {
|
||||
let cylinder = Cylinder {
|
||||
center: Vec3::new(1.0, 2.0, 3.0),
|
||||
radius: 0.0,
|
||||
height: 0.0,
|
||||
};
|
||||
|
||||
let point = Vec3::new(1.0, 2.5, 3.5);
|
||||
|
||||
assert!(cylinder.approx_in_range(point, 0.71));
|
||||
assert!(cylinder.min_distance(point) < 0.71);
|
||||
assert!(cylinder.min_distance(point) > 0.70);
|
||||
|
||||
let cube = Cube {
|
||||
min: Vec3::new(0.5, 1.9, 2.1),
|
||||
side_length: 1.0,
|
||||
};
|
||||
|
||||
assert!(cylinder.approx_in_range(cube, 0.0));
|
||||
assert!(cylinder.min_distance(cube) < f32::EPSILON);
|
||||
|
||||
let cube = Cube {
|
||||
min: Vec3::new(1.0, 2.0, 4.5),
|
||||
side_length: 1.0,
|
||||
};
|
||||
|
||||
assert!(cylinder.approx_in_range(cube, 1.51));
|
||||
assert!(cylinder.approx_in_range(cube, 100.51));
|
||||
assert!(cylinder.min_distance(cube) < 1.501);
|
||||
assert!(cylinder.min_distance(cube) > 1.499);
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,23 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
let mut dropped_items = Vec::new();
|
||||
let mut thrown_items = Vec::new();
|
||||
|
||||
let get_cylinder = |state: &common::state::State, entity| {
|
||||
let ecs = state.ecs();
|
||||
let positions = ecs.read_storage::<comp::Pos>();
|
||||
let scales = ecs.read_storage::<comp::Scale>();
|
||||
let colliders = ecs.read_storage::<comp::Collider>();
|
||||
let char_states = ecs.read_storage::<comp::CharacterState>();
|
||||
|
||||
positions.get(entity).map(|p| {
|
||||
find_dist::Cylinder::from_components(
|
||||
p.0,
|
||||
scales.get(entity).copied(),
|
||||
colliders.get(entity).copied(),
|
||||
char_states.get(entity),
|
||||
)
|
||||
})
|
||||
};
|
||||
|
||||
match manip {
|
||||
comp::InventoryManip::Pickup(uid) => {
|
||||
let picked_up_item: Option<comp::Item>;
|
||||
@ -60,32 +77,14 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
) {
|
||||
picked_up_item = Some(item.clone());
|
||||
|
||||
{
|
||||
let ecs = state.ecs();
|
||||
let positions = ecs.read_storage::<comp::Pos>();
|
||||
let scales = ecs.read_storage::<comp::Scale>();
|
||||
let colliders = ecs.read_storage::<comp::Collider>();
|
||||
let char_states = ecs.read_storage::<comp::CharacterState>();
|
||||
|
||||
let cylinder = |entity| {
|
||||
positions.get(entity).map(|p| {
|
||||
find_dist::Cylinder::from_components(
|
||||
p.0,
|
||||
scales.get(entity).copied(),
|
||||
colliders.get(entity).copied(),
|
||||
char_states.get(entity),
|
||||
)
|
||||
})
|
||||
};
|
||||
let entity_cylinder = cylinder(entity);
|
||||
if !within_pickup_range(entity_cylinder, || cylinder(item_entity)) {
|
||||
debug!(
|
||||
?entity_cylinder,
|
||||
"Failed to pick up item as not within range, Uid: {}", uid
|
||||
);
|
||||
return;
|
||||
};
|
||||
}
|
||||
let entity_cylinder = get_cylinder(state, entity);
|
||||
if !within_pickup_range(entity_cylinder, || get_cylinder(state, item_entity)) {
|
||||
debug!(
|
||||
?entity_cylinder,
|
||||
"Failed to pick up item as not within range, Uid: {}", uid
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
// Grab the health from the entity and check if the entity is dead.
|
||||
let healths = state.ecs().read_storage::<comp::Health>();
|
||||
@ -131,34 +130,19 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
if let Some(block) = block {
|
||||
if block.is_collectible() && state.can_set_block(pos) {
|
||||
// Check if the block is within pickup range
|
||||
{
|
||||
let ecs = state.ecs();
|
||||
let positions = ecs.read_storage::<comp::Pos>();
|
||||
let scales = ecs.read_storage::<comp::Scale>();
|
||||
let colliders = ecs.read_storage::<comp::Collider>();
|
||||
let char_states = ecs.read_storage::<comp::CharacterState>();
|
||||
|
||||
let entity_cylinder = positions.get(entity).map(|p| {
|
||||
find_dist::Cylinder::from_components(
|
||||
p.0,
|
||||
scales.get(entity).copied(),
|
||||
colliders.get(entity).copied(),
|
||||
char_states.get(entity),
|
||||
)
|
||||
});
|
||||
if !within_pickup_range(entity_cylinder, || {
|
||||
Some(find_dist::Cube {
|
||||
pos: pos.as_(),
|
||||
side_length: 1.0,
|
||||
})
|
||||
}) {
|
||||
debug!(
|
||||
?entity_cylinder,
|
||||
"Failed to pick up block as not within range, block pos: {}", pos
|
||||
);
|
||||
return;
|
||||
};
|
||||
}
|
||||
let entity_cylinder = get_cylinder(state, entity);
|
||||
if !within_pickup_range(entity_cylinder, || {
|
||||
Some(find_dist::Cube {
|
||||
min: pos.as_(),
|
||||
side_length: 1.0,
|
||||
})
|
||||
}) {
|
||||
debug!(
|
||||
?entity_cylinder,
|
||||
"Failed to pick up block as not within range, block pos: {}", pos
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(item) = comp::Item::try_reclaim_from_block(block) {
|
||||
let (event, item_was_added) = if let Some(inv) = state
|
||||
|
@ -1383,7 +1383,7 @@ fn select_interactable(
|
||||
|
||||
// Pick closer one if they exist
|
||||
closest_interactable_block_pos
|
||||
.filter(|block_pos| player_cylinder.min_distance(Cube { pos: block_pos.as_(), side_length: 1.0}) < search_dist)
|
||||
.filter(|block_pos| player_cylinder.min_distance(Cube { min: block_pos.as_(), side_length: 1.0}) < search_dist)
|
||||
.and_then(|block_pos|
|
||||
client.state().terrain().get(block_pos).ok().copied()
|
||||
.map(|b| Interactable::Block(b, block_pos))
|
||||
|
Loading…
Reference in New Issue
Block a user