mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implement CapsulePrism collisions
This commit is contained in:
parent
7712976b86
commit
6c3b61dc25
@ -55,6 +55,9 @@ pub struct PreviousPhysCache {
|
|||||||
pub collision_boundary: f32,
|
pub collision_boundary: f32,
|
||||||
pub scale: f32,
|
pub scale: f32,
|
||||||
pub scaled_radius: f32,
|
pub scaled_radius: f32,
|
||||||
|
pub neighborhood_radius: f32,
|
||||||
|
/// p0 and p1 in case of CapsulePrism collider, Vec2::zero() otherwise.
|
||||||
|
pub origins: (Vec2<f32>, Vec2<f32>),
|
||||||
pub ori: Quaternion<f32>,
|
pub ori: Quaternion<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,11 +128,10 @@ impl Collider {
|
|||||||
match self {
|
match self {
|
||||||
Collider::Voxel { .. } => 1.0,
|
Collider::Voxel { .. } => 1.0,
|
||||||
Collider::Box { radius, .. } => *radius,
|
Collider::Box { radius, .. } => *radius,
|
||||||
// FIXME: I know that this is wrong for sure,
|
Collider::CapsulePrism { radius, p0, p1, .. } => {
|
||||||
// because it's not a circle.
|
let a = p0.distance(*p1);
|
||||||
//
|
a / 2.0 + *radius
|
||||||
// CodeReviewers, please welp!
|
},
|
||||||
Collider::CapsulePrism { radius, .. } => *radius,
|
|
||||||
Collider::Point => 0.0,
|
Collider::Point => 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,15 +192,18 @@ impl<'a> PhysicsData<'a> {
|
|||||||
collision_boundary: 0.0,
|
collision_boundary: 0.0,
|
||||||
scale: 0.0,
|
scale: 0.0,
|
||||||
scaled_radius: 0.0,
|
scaled_radius: 0.0,
|
||||||
|
neighborhood_radius: 0.0,
|
||||||
|
origins: (Vec2::zero(), Vec2::zero()),
|
||||||
ori: Quaternion::identity(),
|
ori: Quaternion::identity(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update PreviousPhysCache
|
// Update PreviousPhysCache
|
||||||
for (_, vel, position, mut phys_cache, collider, scale, cs, _, _, _) in (
|
for (_, vel, position, ori, mut phys_cache, collider, scale, cs, _, _, _) in (
|
||||||
&self.read.entities,
|
&self.read.entities,
|
||||||
&self.write.velocities,
|
&self.write.velocities,
|
||||||
&self.write.positions,
|
&self.write.positions,
|
||||||
|
&self.write.orientations,
|
||||||
&mut self.write.previous_phys_cache,
|
&mut self.write.previous_phys_cache,
|
||||||
self.read.colliders.maybe(),
|
self.read.colliders.maybe(),
|
||||||
self.read.scales.maybe(),
|
self.read.scales.maybe(),
|
||||||
@ -219,6 +222,10 @@ impl<'a> PhysicsData<'a> {
|
|||||||
phys_cache.velocity_dt = vel.0 * self.read.dt.0;
|
phys_cache.velocity_dt = vel.0 * self.read.dt.0;
|
||||||
let entity_center = position.0 + Vec3::new(0.0, z_limits.0 + half_height, 0.0);
|
let entity_center = position.0 + Vec3::new(0.0, z_limits.0 + half_height, 0.0);
|
||||||
let flat_radius = collider.map(|c| c.get_radius()).unwrap_or(0.5) * scale;
|
let flat_radius = collider.map(|c| c.get_radius()).unwrap_or(0.5) * scale;
|
||||||
|
let neighborhood_radius = match collider {
|
||||||
|
Some(Collider::CapsulePrism { radius, .. }) => radius * scale,
|
||||||
|
_ => flat_radius,
|
||||||
|
};
|
||||||
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
let radius = (flat_radius.powi(2) + half_height.powi(2)).sqrt();
|
||||||
|
|
||||||
// Move center to the middle between OLD and OLD+VEL_DT so that we can reduce
|
// Move center to the middle between OLD and OLD+VEL_DT so that we can reduce
|
||||||
@ -226,7 +233,36 @@ impl<'a> PhysicsData<'a> {
|
|||||||
phys_cache.center = entity_center + phys_cache.velocity_dt / 2.0;
|
phys_cache.center = entity_center + phys_cache.velocity_dt / 2.0;
|
||||||
phys_cache.collision_boundary = radius + (phys_cache.velocity_dt / 2.0).magnitude();
|
phys_cache.collision_boundary = radius + (phys_cache.velocity_dt / 2.0).magnitude();
|
||||||
phys_cache.scale = scale;
|
phys_cache.scale = scale;
|
||||||
|
phys_cache.neighborhood_radius = neighborhood_radius;
|
||||||
phys_cache.scaled_radius = flat_radius;
|
phys_cache.scaled_radius = flat_radius;
|
||||||
|
|
||||||
|
let ori = ori.to_quat();
|
||||||
|
let (p0, p1) = match collider {
|
||||||
|
Some(Collider::CapsulePrism { p0, p1, .. }) => {
|
||||||
|
// Build the line between two origins
|
||||||
|
let a = p1 - p0;
|
||||||
|
let len = a.magnitude();
|
||||||
|
// Make it 3d
|
||||||
|
let a = Vec3::new(a.x, a.y, 0.0);
|
||||||
|
// Rotate it
|
||||||
|
let a = ori * a;
|
||||||
|
// Make it 2d again
|
||||||
|
let a = Vec2::new(a.x, a.y);
|
||||||
|
// Make sure we have the same length as before
|
||||||
|
// (and scale it)
|
||||||
|
let a = a * scale * len / a.magnitude();
|
||||||
|
|
||||||
|
// Splite the oriented line into two origins
|
||||||
|
let p0 = -a / 2.0;
|
||||||
|
let p1 = a / 2.0;
|
||||||
|
(p0, p1)
|
||||||
|
},
|
||||||
|
Some(Collider::Box { .. } | Collider::Voxel { .. } | Collider::Point) | None => {
|
||||||
|
(Vec2::zero(), Vec2::zero())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
phys_cache.origins = (p0, p1);
|
||||||
|
phys_cache.ori = ori;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,13 +385,12 @@ impl<'a> PhysicsData<'a> {
|
|||||||
spatial_grid
|
spatial_grid
|
||||||
.in_circle_aabr(query_center, query_radius)
|
.in_circle_aabr(query_center, query_radius)
|
||||||
.filter_map(|entity| {
|
.filter_map(|entity| {
|
||||||
read.uids
|
let uid = read.uids.get(entity)?;
|
||||||
.get(entity)
|
let pos = positions.get(entity)?;
|
||||||
.and_then(|l| positions.get(entity).map(|r| (l, r)))
|
let previous_cache = previous_phys_cache.get(entity)?;
|
||||||
.and_then(|l| previous_phys_cache.get(entity).map(|r| (l, r)))
|
let mass = read.masses.get(entity)?;
|
||||||
.and_then(|l| read.masses.get(entity).map(|r| (l, r)))
|
|
||||||
.map(|(((uid, pos), previous_cache), mass)| {
|
Some((
|
||||||
(
|
|
||||||
entity,
|
entity,
|
||||||
uid,
|
uid,
|
||||||
pos,
|
pos,
|
||||||
@ -363,8 +398,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
mass,
|
mass,
|
||||||
read.colliders.get(entity),
|
read.colliders.get(entity),
|
||||||
read.char_states.get(entity),
|
read.char_states.get(entity),
|
||||||
)
|
))
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.for_each(
|
.for_each(
|
||||||
|(
|
|(
|
||||||
@ -387,8 +421,6 @@ impl<'a> PhysicsData<'a> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let collision_dist = previous_cache.scaled_radius
|
|
||||||
+ previous_cache_other.scaled_radius;
|
|
||||||
let z_limits_other =
|
let z_limits_other =
|
||||||
calc_z_limit(char_state_other_maybe, collider_other);
|
calc_z_limit(char_state_other_maybe, collider_other);
|
||||||
|
|
||||||
@ -403,24 +435,62 @@ impl<'a> PhysicsData<'a> {
|
|||||||
.ceil()
|
.ceil()
|
||||||
as usize;
|
as usize;
|
||||||
let step_delta = 1.0 / increments as f32;
|
let step_delta = 1.0 / increments as f32;
|
||||||
|
|
||||||
let mut collision_registered = false;
|
let mut collision_registered = false;
|
||||||
|
|
||||||
|
let collision_dist = previous_cache.neighborhood_radius
|
||||||
|
+ previous_cache_other.neighborhood_radius;
|
||||||
|
|
||||||
for i in 0..increments {
|
for i in 0..increments {
|
||||||
let factor = i as f32 * step_delta;
|
let factor = i as f32 * step_delta;
|
||||||
|
|
||||||
|
// get positions
|
||||||
let pos = pos.0 + previous_cache.velocity_dt * factor;
|
let pos = pos.0 + previous_cache.velocity_dt * factor;
|
||||||
|
|
||||||
let pos_other =
|
let pos_other =
|
||||||
pos_other.0 + previous_cache_other.velocity_dt * factor;
|
pos_other.0 + previous_cache_other.velocity_dt * factor;
|
||||||
|
|
||||||
let diff = pos.xy() - pos_other.xy();
|
// Compare Z ranges
|
||||||
|
let ceiling = pos.z + z_limits.1 * previous_cache.scale;
|
||||||
|
let floor = pos.z + z_limits.0 * previous_cache.scale;
|
||||||
|
|
||||||
|
let ceiling_other =
|
||||||
|
pos_other.z + z_limits_other.1 * previous_cache_other.scale;
|
||||||
|
let floor_other =
|
||||||
|
pos_other.z + z_limits_other.0 * previous_cache_other.scale;
|
||||||
|
let in_z_range =
|
||||||
|
ceiling >= floor_other && floor <= ceiling_other;
|
||||||
|
|
||||||
|
// check horizontal distance
|
||||||
|
let (p0_offset, p1_offset) = previous_cache.origins;
|
||||||
|
let p0 = pos + p0_offset;
|
||||||
|
let p1 = pos + p1_offset;
|
||||||
|
let segment = LineSegment2 {
|
||||||
|
start: p0.xy(),
|
||||||
|
end: p1.xy(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (p0_offset_other, p1_offset_other) =
|
||||||
|
previous_cache_other.origins;
|
||||||
|
let p0_other = pos_other + p0_offset_other;
|
||||||
|
let p1_other = pos_other + p1_offset_other;
|
||||||
|
|
||||||
|
let segment_other = LineSegment2 {
|
||||||
|
start: p0_other.xy(),
|
||||||
|
end: p1_other.xy(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let diff = projection_between(segment, segment_other);
|
||||||
|
|
||||||
|
let in_collision_range =
|
||||||
|
diff.magnitude_squared() <= collision_dist.powi(2);
|
||||||
|
|
||||||
|
let collides = in_collision_range && in_z_range;
|
||||||
|
|
||||||
|
if !collides {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if diff.magnitude_squared() <= collision_dist.powi(2)
|
|
||||||
&& pos.z + z_limits.1 * previous_cache.scale
|
|
||||||
>= pos_other.z
|
|
||||||
+ z_limits_other.0 * previous_cache_other.scale
|
|
||||||
&& pos.z + z_limits.0 * previous_cache.scale
|
|
||||||
<= pos_other.z
|
|
||||||
+ z_limits_other.1 * previous_cache_other.scale
|
|
||||||
{
|
|
||||||
// If entities have not yet collided
|
// If entities have not yet collided
|
||||||
// this tick (but just did) and if entity
|
// this tick (but just did) and if entity
|
||||||
//
|
//
|
||||||
@ -467,10 +537,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&& (!is_sticky || is_mid_air)
|
&& (!is_sticky || is_mid_air)
|
||||||
&& diff.magnitude_squared() > 0.0
|
&& diff.magnitude_squared() > 0.0
|
||||||
&& !is_projectile
|
&& !is_projectile
|
||||||
&& !matches!(
|
&& !matches!(collider_other, Some(Collider::Voxel { .. }))
|
||||||
collider_other,
|
|
||||||
Some(Collider::Voxel { .. })
|
|
||||||
)
|
|
||||||
&& !matches!(collider, Some(Collider::Voxel { .. }))
|
&& !matches!(collider, Some(Collider::Voxel { .. }))
|
||||||
{
|
{
|
||||||
let force = 400.0
|
let force = 400.0
|
||||||
@ -484,7 +551,6 @@ impl<'a> PhysicsData<'a> {
|
|||||||
|
|
||||||
collision_registered = true;
|
collision_registered = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -848,7 +914,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
z_max,
|
z_max,
|
||||||
} => {
|
} => {
|
||||||
// FIXME:
|
// FIXME:
|
||||||
// !! DEPRECATED !!
|
// Deprecated, should remove?
|
||||||
//
|
//
|
||||||
// Scale collider
|
// Scale collider
|
||||||
let radius = radius.min(0.45) * scale;
|
let radius = radius.min(0.45) * scale;
|
||||||
@ -882,17 +948,14 @@ impl<'a> PhysicsData<'a> {
|
|||||||
tgt_pos = cpos.0;
|
tgt_pos = cpos.0;
|
||||||
},
|
},
|
||||||
Collider::CapsulePrism {
|
Collider::CapsulePrism {
|
||||||
radius,
|
|
||||||
z_min,
|
z_min,
|
||||||
z_max,
|
z_max,
|
||||||
..
|
p0: _,
|
||||||
|
p1: _,
|
||||||
|
radius: _,
|
||||||
} => {
|
} => {
|
||||||
// FIXME: placeholder.
|
|
||||||
// copypasted from Box collider
|
|
||||||
//
|
|
||||||
// This shoudln't pass Code Review.
|
|
||||||
// Scale collider
|
// Scale collider
|
||||||
let radius = radius.min(0.45) * scale;
|
let radius = collider.get_radius().min(0.45) * scale;
|
||||||
let z_min = *z_min * scale;
|
let z_min = *z_min * scale;
|
||||||
let z_max = z_max.clamped(1.2, 1.95) * scale;
|
let z_max = z_max.clamped(1.2, 1.95) * scale;
|
||||||
|
|
||||||
@ -1798,3 +1861,32 @@ fn voxel_collider_bounding_sphere(
|
|||||||
radius,
|
radius,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the shortest line segment between AB and CD, used for pushback
|
||||||
|
/// and collision checks.
|
||||||
|
fn projection_between(ab: LineSegment2<f32>, cd: LineSegment2<f32>) -> Vec2<f32> {
|
||||||
|
// On 2d we can just check projection of A to CD, B to CD, C to AB and D to AB.
|
||||||
|
//
|
||||||
|
// NOTE: We don't check if segments are intersecting, because
|
||||||
|
// even if they do, we still need to return pushback vector.
|
||||||
|
let a = ab.start;
|
||||||
|
let b = ab.end;
|
||||||
|
let c = cd.start;
|
||||||
|
let d = cd.end;
|
||||||
|
|
||||||
|
let projections = [
|
||||||
|
// A to CD
|
||||||
|
a - cd.projected_point(a),
|
||||||
|
// B to CD
|
||||||
|
b - cd.projected_point(b),
|
||||||
|
// C to AB
|
||||||
|
c - ab.projected_point(c),
|
||||||
|
// D to AB
|
||||||
|
d - ab.projected_point(d),
|
||||||
|
];
|
||||||
|
|
||||||
|
// min_by_key returns None only if iterator is empty, so unwrap is fine
|
||||||
|
IntoIterator::into_iter(projections)
|
||||||
|
.min_by_key(|p| ordered_float::OrderedFloat(p.magnitude_squared()))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user