mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add CapsulePrism collider variant
+ Add placeholder physics collision implementation as copy of cylinder Box collider. + Display it with debug hitboxes.
This commit is contained in:
parent
ce29e99403
commit
3b308a3f6f
@ -9,6 +9,7 @@ layout (std140, set = 1, binding = 0)
|
|||||||
uniform u_locals {
|
uniform u_locals {
|
||||||
vec4 w_pos;
|
vec4 w_pos;
|
||||||
vec4 w_color;
|
vec4 w_color;
|
||||||
|
vec4 w_ori;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout (location = 0)
|
layout (location = 0)
|
||||||
@ -16,5 +17,29 @@ out vec4 f_color;
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
f_color = w_color;
|
f_color = w_color;
|
||||||
gl_Position = all_mat * vec4((v_pos + w_pos.xyz) - focus_off.xyz, 1);
|
|
||||||
|
// Build rotation matrix
|
||||||
|
// https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles#Rotation_matrices
|
||||||
|
mat3 rotation_matrix;
|
||||||
|
float q0 = w_ori[3];
|
||||||
|
float q1 = w_ori[0];
|
||||||
|
float q2 = w_ori[1];
|
||||||
|
float q3 = w_ori[2];
|
||||||
|
|
||||||
|
float r00 = 1 - 2 * (pow(q2, 2) + pow(q3, 2));
|
||||||
|
float r01 = 2 * (q1 * q2 - q0 * q3);
|
||||||
|
float r02 = 2 * (q0 * q2 + q1 * q3);
|
||||||
|
rotation_matrix[0] = vec3(r00, r01, r02);
|
||||||
|
|
||||||
|
float r10 = 2 * (q1 * q2 + q0 * q3);
|
||||||
|
float r11 = 1 - 2 * (pow(q1, 2) + pow(q3, 2));
|
||||||
|
float r12 = 2 * (q2 * q3 - q0 * q1);
|
||||||
|
rotation_matrix[1] = vec3(r10, r11, r12);
|
||||||
|
|
||||||
|
float r20 = 2 * (q1 * q3 - q0 * q2);
|
||||||
|
float r21 = 2 * (q0 * q1 + q2 * q3);
|
||||||
|
float r22 = 1 - 2 * (pow(q1, 2) + pow(q2, 2));
|
||||||
|
rotation_matrix[2] = vec3(r20, r21, r22);
|
||||||
|
|
||||||
|
gl_Position = all_mat * vec4((v_pos * rotation_matrix + w_pos.xyz) - focus_off.xyz, 1);
|
||||||
}
|
}
|
||||||
|
@ -147,6 +147,20 @@ impl<
|
|||||||
const EXTENSION: &'static str = "ron";
|
const EXTENSION: &'static str = "ron";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utility enum used to build Stadium points
|
||||||
|
// Read doc for [Body::sausage] for more.
|
||||||
|
//
|
||||||
|
// Actually can be removed I guess?
|
||||||
|
// We can just determine shape form dimensions.
|
||||||
|
//
|
||||||
|
// But I want Dachshund in Veloren at least somewhere XD
|
||||||
|
enum Shape {
|
||||||
|
// Dachshund-like
|
||||||
|
Long,
|
||||||
|
// Cyclops-like
|
||||||
|
Wide,
|
||||||
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub fn is_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
pub fn is_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
||||||
|
|
||||||
@ -292,11 +306,12 @@ impl Body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The width (shoulder to shoulder), length (nose to tail) and height
|
/// The width (shoulder to shoulder), length (nose to tail) and height
|
||||||
/// respectively
|
/// respectively (in metres)
|
||||||
|
// Code reviewers: should we replace metres with 'block height'?
|
||||||
pub fn dimensions(&self) -> Vec3<f32> {
|
pub fn dimensions(&self) -> Vec3<f32> {
|
||||||
match self {
|
match self {
|
||||||
Body::BipedLarge(body) => match body.species {
|
Body::BipedLarge(body) => match body.species {
|
||||||
biped_large::Species::Cyclops => Vec3::new(4.6, 3.0, 6.5),
|
biped_large::Species::Cyclops => Vec3::new(5.6, 3.0, 6.5),
|
||||||
biped_large::Species::Dullahan => Vec3::new(4.6, 3.0, 5.5),
|
biped_large::Species::Dullahan => Vec3::new(4.6, 3.0, 5.5),
|
||||||
biped_large::Species::Mightysaurok => Vec3::new(4.0, 3.0, 3.4),
|
biped_large::Species::Mightysaurok => Vec3::new(4.0, 3.0, 3.4),
|
||||||
biped_large::Species::Mindflayer => Vec3::new(4.4, 3.0, 8.0),
|
biped_large::Species::Mindflayer => Vec3::new(4.4, 3.0, 8.0),
|
||||||
@ -350,6 +365,8 @@ impl Body {
|
|||||||
quadruped_medium::Species::Ngoubou => Vec3::new(2.0, 3.2, 2.4),
|
quadruped_medium::Species::Ngoubou => Vec3::new(2.0, 3.2, 2.4),
|
||||||
quadruped_medium::Species::Llama => Vec3::new(2.0, 2.5, 2.6),
|
quadruped_medium::Species::Llama => Vec3::new(2.0, 2.5, 2.6),
|
||||||
quadruped_medium::Species::Alpaca => Vec3::new(2.0, 2.0, 2.0),
|
quadruped_medium::Species::Alpaca => Vec3::new(2.0, 2.0, 2.0),
|
||||||
|
quadruped_medium::Species::Camel => Vec3::new(2.0, 4.0, 3.5),
|
||||||
|
// FIXME: We really shouldn't be doing wildcards here
|
||||||
_ => Vec3::new(2.0, 3.0, 2.0),
|
_ => Vec3::new(2.0, 3.0, 2.0),
|
||||||
},
|
},
|
||||||
Body::QuadrupedSmall(body) => match body.species {
|
Body::QuadrupedSmall(body) => match body.species {
|
||||||
@ -386,6 +403,26 @@ impl Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn shape(&self) -> Shape {
|
||||||
|
match self {
|
||||||
|
Body::BipedLarge(_)
|
||||||
|
| Body::BipedSmall(_)
|
||||||
|
| Body::Golem(_)
|
||||||
|
| Body::Humanoid(_)
|
||||||
|
| Body::Object(_) => Shape::Wide,
|
||||||
|
Body::BirdLarge(_)
|
||||||
|
| Body::BirdMedium(_)
|
||||||
|
| Body::Dragon(_)
|
||||||
|
| Body::FishMedium(_)
|
||||||
|
| Body::FishSmall(_)
|
||||||
|
| Body::QuadrupedLow(_)
|
||||||
|
| Body::QuadrupedMedium(_)
|
||||||
|
| Body::QuadrupedSmall(_)
|
||||||
|
| Body::Ship(_)
|
||||||
|
| Body::Theropod(_) => Shape::Long,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Note: This is used for collisions, but it's not very accurate for shapes that
|
// Note: This is used for collisions, but it's not very accurate for shapes that
|
||||||
// are very much not cylindrical. Eventually this ought to be replaced by more
|
// are very much not cylindrical. Eventually this ought to be replaced by more
|
||||||
// accurate collision shapes.
|
// accurate collision shapes.
|
||||||
@ -394,6 +431,52 @@ impl Body {
|
|||||||
dim.x.max(dim.y) / 2.0
|
dim.x.max(dim.y) / 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Base of our Capsule Prism used for collisions.
|
||||||
|
/// Returns line segment and radius. See [this wiki page][stadium_wiki].
|
||||||
|
///
|
||||||
|
/// [stadium_wiki]: <https://en.wikipedia.org/wiki/Stadium_(geometry)>
|
||||||
|
pub fn sausage(&self) -> (Vec2<f32>, Vec2<f32>, f32) {
|
||||||
|
// Consider this ascii-art stadium with radius `r` and line segment `a`
|
||||||
|
//
|
||||||
|
// xxxxxxxxxxxxxxxxx
|
||||||
|
//
|
||||||
|
// _ aaaaaaaaa
|
||||||
|
// y -* r * -
|
||||||
|
// y * r *
|
||||||
|
// y * rrr --------- rrr *
|
||||||
|
// y * r *
|
||||||
|
// y * r *
|
||||||
|
// *__aaaaaaaaa_ ^
|
||||||
|
let dim = self.dimensions();
|
||||||
|
// The width (shoulder to shoulder) and length (nose to tail)
|
||||||
|
let (width, length) = (dim.x, dim.y);
|
||||||
|
|
||||||
|
match self.shape() {
|
||||||
|
Shape::Long => {
|
||||||
|
let radius = width / 2.0;
|
||||||
|
|
||||||
|
let a = length - 2.0 * radius;
|
||||||
|
debug_assert!(a > 0.0);
|
||||||
|
|
||||||
|
let p0 = Vec2::new(0.0, -a / 2.0);
|
||||||
|
let p1 = Vec2::new(0.0, a / 2.0);
|
||||||
|
|
||||||
|
(p0, p1, radius)
|
||||||
|
},
|
||||||
|
Shape::Wide => {
|
||||||
|
let radius = length / 2.0;
|
||||||
|
|
||||||
|
let a = width - 2.0 * radius;
|
||||||
|
debug_assert!(a > 0.0);
|
||||||
|
|
||||||
|
let p0 = Vec2::new(-a / 2.0, 0.0);
|
||||||
|
let p1 = Vec2::new(a / 2.0, 0.0);
|
||||||
|
|
||||||
|
(p0, p1, radius)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// How far away other entities should try to be. Will be added uppon the other
|
// How far away other entities should try to be. Will be added uppon the other
|
||||||
// entitys spacing_radius. So an entity with 2.0 and an entity with 3.0 will
|
// entitys spacing_radius. So an entity with 2.0 and an entity with 3.0 will
|
||||||
// lead to that both entities will try to keep 5.0 units away from each
|
// lead to that both entities will try to keep 5.0 units away from each
|
||||||
@ -417,6 +500,7 @@ impl Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Height from the bottom to the top (in metres)
|
||||||
pub fn height(&self) -> f32 { self.dimensions().z }
|
pub fn height(&self) -> f32 { self.dimensions().z }
|
||||||
|
|
||||||
pub fn base_energy(&self) -> u32 {
|
pub fn base_energy(&self) -> u32 {
|
||||||
|
@ -99,10 +99,24 @@ impl Component for Density {
|
|||||||
// Collider
|
// Collider
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Collider {
|
pub enum Collider {
|
||||||
// TODO: pass the map from ids -> voxel data to get_radius and get_z_limits to compute a
|
// TODO: pass the map from ids -> voxel data to get_radius
|
||||||
// bounding cylinder
|
// and get_z_limits to compute a bounding cylinder.
|
||||||
Voxel { id: String },
|
Voxel {
|
||||||
Box { radius: f32, z_min: f32, z_max: f32 },
|
id: String,
|
||||||
|
},
|
||||||
|
Box {
|
||||||
|
radius: f32,
|
||||||
|
z_min: f32,
|
||||||
|
z_max: f32,
|
||||||
|
},
|
||||||
|
/// Capsule prism with line segment from p0 to p1
|
||||||
|
CapsulePrism {
|
||||||
|
p0: Vec2<f32>,
|
||||||
|
p1: Vec2<f32>,
|
||||||
|
radius: f32,
|
||||||
|
z_min: f32,
|
||||||
|
z_max: f32,
|
||||||
|
},
|
||||||
Point,
|
Point,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +125,11 @@ 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,
|
||||||
|
// because it's not a circle.
|
||||||
|
//
|
||||||
|
// CodeReviewers, please welp!
|
||||||
|
Collider::CapsulePrism { radius, .. } => *radius,
|
||||||
Collider::Point => 0.0,
|
Collider::Point => 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,6 +143,7 @@ impl Collider {
|
|||||||
match self {
|
match self {
|
||||||
Collider::Voxel { .. } => (0.0, 1.0),
|
Collider::Voxel { .. } => (0.0, 1.0),
|
||||||
Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
||||||
|
Collider::CapsulePrism { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
||||||
Collider::Point => (0.0, 0.0),
|
Collider::Point => (0.0, 0.0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,8 +290,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
!&read.mountings,
|
!&read.mountings,
|
||||||
read.stickies.maybe(),
|
read.stickies.maybe(),
|
||||||
&mut write.physics_states,
|
&mut write.physics_states,
|
||||||
// TODO: if we need to avoid collisions for other things consider moving whether it
|
// TODO: if we need to avoid collisions for other things consider
|
||||||
// should interact into the collider component or into a separate component
|
// moving whether it should interact into the collider component
|
||||||
|
// or into a separate component.
|
||||||
read.projectiles.maybe(),
|
read.projectiles.maybe(),
|
||||||
read.char_states.maybe(),
|
read.char_states.maybe(),
|
||||||
)
|
)
|
||||||
@ -322,9 +323,10 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let mut entity_entity_collision_checks = 0;
|
let mut entity_entity_collision_checks = 0;
|
||||||
let mut entity_entity_collisions = 0;
|
let mut entity_entity_collisions = 0;
|
||||||
|
|
||||||
// TODO: quick fix for bad performance at extrememly high velocities
|
// TODO: quick fix for bad performance. At extrememly high
|
||||||
// use oriented rectangles at some threshold of displacement/radius
|
// velocities use oriented rectangles at some threshold of
|
||||||
// to query the spatial grid and limit max displacement per tick somehow
|
// displacement/radius to query the spatial grid and limit
|
||||||
|
// max displacement per tick somehow.
|
||||||
if previous_cache.collision_boundary > 128.0 {
|
if previous_cache.collision_boundary > 128.0 {
|
||||||
return PhysicsMetrics {
|
return PhysicsMetrics {
|
||||||
entity_entity_collision_checks,
|
entity_entity_collision_checks,
|
||||||
@ -419,9 +421,14 @@ impl<'a> PhysicsData<'a> {
|
|||||||
<= pos_other.z
|
<= pos_other.z
|
||||||
+ z_limits_other.1 * previous_cache_other.scale
|
+ z_limits_other.1 * previous_cache_other.scale
|
||||||
{
|
{
|
||||||
// If entities have not yet collided this tick (but just
|
// If entities have not yet collided
|
||||||
// did) and if entity is either in mid air or is not sticky,
|
// this tick (but just did) and if entity
|
||||||
// then mark them as colliding with the other entity
|
//
|
||||||
|
// is either in mid air
|
||||||
|
// or is not sticky,
|
||||||
|
//
|
||||||
|
// then mark them as colliding with the
|
||||||
|
// other entity.
|
||||||
if !collision_registered && (is_mid_air || !is_sticky) {
|
if !collision_registered && (is_mid_air || !is_sticky) {
|
||||||
physics.touch_entities.insert(*other);
|
physics.touch_entities.insert(*other);
|
||||||
entity_entity_collisions += 1;
|
entity_entity_collisions += 1;
|
||||||
@ -627,8 +634,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
// And not already stuck on a block (e.g., for arrows)
|
// And not already stuck on a block (e.g., for arrows)
|
||||||
&& !(physics_state.on_surface().is_some() && sticky.is_some())
|
&& !(physics_state.on_surface().is_some() && sticky.is_some())
|
||||||
{
|
{
|
||||||
// Clamp dt to an effective 10 TPS, to prevent gravity from slamming the
|
// Clamp dt to an effective 10 TPS, to prevent gravity
|
||||||
// players into the floor when stationary if other systems cause the server
|
// from slamming the players into the floor when
|
||||||
|
// stationary if other systems cause the server
|
||||||
// to lag (as observed in the 0.9 release party).
|
// to lag (as observed in the 0.9 release party).
|
||||||
let dt = DeltaTime(read.dt.0.min(0.1));
|
let dt = DeltaTime(read.dt.0.min(0.1));
|
||||||
|
|
||||||
@ -750,8 +758,8 @@ impl<'a> PhysicsData<'a> {
|
|||||||
)| {
|
)| {
|
||||||
let mut land_on_ground = None;
|
let mut land_on_ground = None;
|
||||||
let mut outcomes = Vec::new();
|
let mut outcomes = Vec::new();
|
||||||
// Defer the writes of positions, velocities and orientations to allow an inner
|
// Defer the writes of positions, velocities and orientations
|
||||||
// loop over terrain-like entities
|
// to allow an inner loop over terrain-like entities.
|
||||||
let old_vel = *vel;
|
let old_vel = *vel;
|
||||||
let mut vel = *vel;
|
let mut vel = *vel;
|
||||||
let old_ori = *ori;
|
let old_ori = *ori;
|
||||||
@ -777,17 +785,23 @@ impl<'a> PhysicsData<'a> {
|
|||||||
Vec3::zero()
|
Vec3::zero()
|
||||||
};
|
};
|
||||||
|
|
||||||
// What's going on here? Because collisions need to be resolved against multiple
|
// What's going on here?
|
||||||
|
// Because collisions need to be resolved against multiple
|
||||||
// colliders, this code takes the current position and
|
// colliders, this code takes the current position and
|
||||||
// propagates it forward according to velocity to find a
|
// propagates it forward according to velocity to find a
|
||||||
// 'target' position. This is where we'd ideally end up at the end of the tick,
|
// 'target' position.
|
||||||
|
//
|
||||||
|
// This is where we'd ideally end up at the end of the tick,
|
||||||
// assuming no collisions. Then, we refine this target by
|
// assuming no collisions. Then, we refine this target by
|
||||||
// stepping from the original position to the target for
|
// stepping from the original position to the target for
|
||||||
// every obstacle, refining the target position as we go. It's not perfect, but
|
// every obstacle, refining the target position as we go.
|
||||||
// it works pretty well in practice. Oddities can occur on
|
//
|
||||||
// the intersection between multiple colliders, but it's not
|
// It's not perfect, but it works pretty well in practice.
|
||||||
// like any game physics system resolves these sort of things well anyway. At
|
// Oddities can occur on the intersection between multiple
|
||||||
// the very least, we don't do things that result in glitchy
|
// colliders, but it's not like any game physics system
|
||||||
|
// resolves these sort of things well anyway.
|
||||||
|
//
|
||||||
|
// At the very least, we don't do things that result in glitchy
|
||||||
// velocities or entirely broken position snapping.
|
// velocities or entirely broken position snapping.
|
||||||
let mut tgt_pos = pos.0 + pos_delta;
|
let mut tgt_pos = pos.0 + pos_delta;
|
||||||
|
|
||||||
@ -799,11 +813,12 @@ impl<'a> PhysicsData<'a> {
|
|||||||
|
|
||||||
match &collider {
|
match &collider {
|
||||||
Collider::Voxel { .. } => {
|
Collider::Voxel { .. } => {
|
||||||
// for now, treat entities with voxel colliders as their bounding
|
// For now, treat entities with voxel colliders
|
||||||
// cylinders for the purposes of colliding them with terrain
|
// as their bounding cylinders for the purposes of
|
||||||
|
// colliding them with terrain.
|
||||||
// Additionally, multiply radius by 0.1 to make the cylinder smaller to
|
//
|
||||||
// avoid lag
|
// Additionally, multiply radius by 0.1 to make
|
||||||
|
// the cylinder smaller to avoid lag.
|
||||||
let radius = collider.get_radius() * scale * 0.1;
|
let radius = collider.get_radius() * scale * 0.1;
|
||||||
let (z_min, z_max) = collider.get_z_limits(scale);
|
let (z_min, z_max) = collider.get_z_limits(scale);
|
||||||
|
|
||||||
@ -832,6 +847,50 @@ impl<'a> PhysicsData<'a> {
|
|||||||
z_min,
|
z_min,
|
||||||
z_max,
|
z_max,
|
||||||
} => {
|
} => {
|
||||||
|
// FIXME:
|
||||||
|
// !! DEPRECATED !!
|
||||||
|
//
|
||||||
|
// Scale collider
|
||||||
|
let radius = radius.min(0.45) * scale;
|
||||||
|
let z_min = *z_min * scale;
|
||||||
|
let z_max = z_max.clamped(1.2, 1.95) * scale;
|
||||||
|
|
||||||
|
let cylinder = (radius, z_min, z_max);
|
||||||
|
let mut cpos = *pos;
|
||||||
|
box_voxel_collision(
|
||||||
|
cylinder,
|
||||||
|
&*read.terrain,
|
||||||
|
entity,
|
||||||
|
&mut cpos,
|
||||||
|
tgt_pos,
|
||||||
|
&mut vel,
|
||||||
|
&mut physics_state,
|
||||||
|
Vec3::zero(),
|
||||||
|
&read.dt,
|
||||||
|
was_on_ground,
|
||||||
|
block_snap,
|
||||||
|
climbing,
|
||||||
|
|entity, vel| land_on_ground = Some((entity, vel)),
|
||||||
|
read,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sticky things shouldn't move when on a surface
|
||||||
|
if physics_state.on_surface().is_some() && sticky.is_some() {
|
||||||
|
vel.0 = physics_state.ground_vel;
|
||||||
|
}
|
||||||
|
|
||||||
|
tgt_pos = cpos.0;
|
||||||
|
},
|
||||||
|
Collider::CapsulePrism {
|
||||||
|
radius,
|
||||||
|
z_min,
|
||||||
|
z_max,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// 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 = radius.min(0.45) * scale;
|
||||||
let z_min = *z_min * scale;
|
let z_min = *z_min * scale;
|
||||||
@ -866,8 +925,10 @@ impl<'a> PhysicsData<'a> {
|
|||||||
Collider::Point => {
|
Collider::Point => {
|
||||||
let mut pos = *pos;
|
let mut pos = *pos;
|
||||||
|
|
||||||
// If the velocity is exactly 0, a raycast may not pick up the current
|
// TODO: If the velocity is exactly 0,
|
||||||
// block. Handle this.
|
// a raycast may not pick up the current block.
|
||||||
|
//
|
||||||
|
// Handle this.
|
||||||
let (dist, block) = if let Some(block) = read
|
let (dist, block) = if let Some(block) = read
|
||||||
.terrain
|
.terrain
|
||||||
.get(pos.0.map(|e| e.floor() as i32))
|
.get(pos.0.map(|e| e.floor() as i32))
|
||||||
@ -882,7 +943,8 @@ impl<'a> PhysicsData<'a> {
|
|||||||
.until(|block: &Block| block.is_solid())
|
.until(|block: &Block| block.is_solid())
|
||||||
.ignore_error()
|
.ignore_error()
|
||||||
.cast();
|
.cast();
|
||||||
(dist, block.unwrap()) // Can't fail since we do ignore_error above
|
// Can't fail since we do ignore_error above
|
||||||
|
(dist, block.unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
pos.0 += pos_delta.try_normalized().unwrap_or_else(Vec3::zero) * dist;
|
pos.0 += pos_delta.try_normalized().unwrap_or_else(Vec3::zero) * dist;
|
||||||
@ -987,6 +1049,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
// Collide with terrain-like entities
|
// Collide with terrain-like entities
|
||||||
let query_center = path_sphere.center.xy();
|
let query_center = path_sphere.center.xy();
|
||||||
let query_radius = path_sphere.radius;
|
let query_radius = path_sphere.radius;
|
||||||
|
|
||||||
voxel_collider_spatial_grid
|
voxel_collider_spatial_grid
|
||||||
.in_circle_aabr(query_center, query_radius)
|
.in_circle_aabr(query_center, query_radius)
|
||||||
.filter_map(|entity| {
|
.filter_map(|entity| {
|
||||||
@ -1280,6 +1343,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[warn(clippy::pedantic)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||||
cylinder: (f32, f32, f32), // effective collision cylinder
|
cylinder: (f32, f32, f32), // effective collision cylinder
|
||||||
@ -1297,22 +1361,6 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
mut land_on_ground: impl FnMut(Entity, Vel),
|
mut land_on_ground: impl FnMut(Entity, Vel),
|
||||||
read: &PhysicsRead,
|
read: &PhysicsRead,
|
||||||
) {
|
) {
|
||||||
let (radius, z_min, z_max) = cylinder;
|
|
||||||
|
|
||||||
// Probe distances
|
|
||||||
let hdist = radius.ceil() as i32;
|
|
||||||
// Neighbouring blocks iterator
|
|
||||||
let near_iter = (-hdist..hdist + 1)
|
|
||||||
.map(move |i| {
|
|
||||||
(-hdist..hdist + 1).map(move |j| {
|
|
||||||
(1 - Block::MAX_HEIGHT.ceil() as i32 + z_min.floor() as i32
|
|
||||||
..z_max.ceil() as i32 + 1)
|
|
||||||
.map(move |k| (i, j, k))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
// Function for iterating over the blocks the player at a specific position
|
// Function for iterating over the blocks the player at a specific position
|
||||||
// collides with
|
// collides with
|
||||||
fn collision_iter<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
fn collision_iter<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||||
@ -1357,7 +1405,6 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let z_range = z_min..z_max;
|
|
||||||
// Function for determining whether the player at a specific position collides
|
// Function for determining whether the player at a specific position collides
|
||||||
// with blocks with the given criteria
|
// with blocks with the given criteria
|
||||||
fn collision_with<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
fn collision_with<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||||
@ -1381,6 +1428,25 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
.is_some()
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn always_hits(_: &Block) -> bool { true }
|
||||||
|
let (radius, z_min, z_max) = cylinder;
|
||||||
|
|
||||||
|
// Probe distances
|
||||||
|
let hdist = radius.ceil() as i32;
|
||||||
|
// Neighbouring blocks iterator
|
||||||
|
let near_iter = (-hdist..hdist + 1)
|
||||||
|
.map(move |i| {
|
||||||
|
(-hdist..hdist + 1).map(move |j| {
|
||||||
|
(1 - Block::MAX_HEIGHT.ceil() as i32 + z_min.floor() as i32
|
||||||
|
..z_max.ceil() as i32 + 1)
|
||||||
|
.map(move |k| (i, j, k))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let z_range = z_min..z_max;
|
||||||
|
|
||||||
physics_state.on_ground = None;
|
physics_state.on_ground = None;
|
||||||
physics_state.on_ceiling = false;
|
physics_state.on_ceiling = false;
|
||||||
|
|
||||||
@ -1395,7 +1461,6 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
.ceil()
|
.ceil()
|
||||||
.max(1.0);
|
.max(1.0);
|
||||||
let old_pos = pos.0;
|
let old_pos = pos.0;
|
||||||
fn block_true(_: &Block) -> bool { true }
|
|
||||||
for _ in 0..increments as usize {
|
for _ in 0..increments as usize {
|
||||||
pos.0 += pos_delta / increments;
|
pos.0 += pos_delta / increments;
|
||||||
|
|
||||||
@ -1485,7 +1550,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
// ...and the vertical resolution direction is sufficiently great...
|
// ...and the vertical resolution direction is sufficiently great...
|
||||||
&& dir.z < -0.1
|
&& dir.z < -0.1
|
||||||
// ...and the space above is free...
|
// ...and the space above is free...
|
||||||
&& !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), &terrain, block_true, near_iter.clone(), radius, z_range.clone())
|
&& !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), &terrain, always_hits, near_iter.clone(), radius, z_range.clone())
|
||||||
// ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)...
|
// ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)...
|
||||||
// && terrain
|
// && terrain
|
||||||
// .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
|
// .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
|
||||||
@ -1495,7 +1560,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
&& collision_with(
|
&& collision_with(
|
||||||
pos.0 + resolve_dir - Vec3::unit_z() * 1.25,
|
pos.0 + resolve_dir - Vec3::unit_z() * 1.25,
|
||||||
&terrain,
|
&terrain,
|
||||||
block_true,
|
always_hits,
|
||||||
near_iter.clone(),
|
near_iter.clone(),
|
||||||
radius,
|
radius,
|
||||||
z_range.clone(),
|
z_range.clone(),
|
||||||
@ -1545,7 +1610,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
} else if collision_with(
|
} else if collision_with(
|
||||||
pos.0 - Vec3::unit_z() * 1.1,
|
pos.0 - Vec3::unit_z() * 1.1,
|
||||||
&terrain,
|
&terrain,
|
||||||
block_true,
|
always_hits,
|
||||||
near_iter.clone(),
|
near_iter.clone(),
|
||||||
radius,
|
radius,
|
||||||
z_range.clone(),
|
z_range.clone(),
|
||||||
|
@ -185,6 +185,7 @@ impl StateExt for State {
|
|||||||
inventory: comp::Inventory,
|
inventory: comp::Inventory,
|
||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
) -> EcsEntityBuilder {
|
) -> EcsEntityBuilder {
|
||||||
|
use comp::body::{biped_large, quadruped_medium};
|
||||||
self.ecs_mut()
|
self.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(pos)
|
.with(pos)
|
||||||
@ -203,6 +204,31 @@ impl StateExt for State {
|
|||||||
comp::Body::Ship(ship) => comp::Collider::Voxel {
|
comp::Body::Ship(ship) => comp::Collider::Voxel {
|
||||||
id: ship.manifest_entry().to_string(),
|
id: ship.manifest_entry().to_string(),
|
||||||
},
|
},
|
||||||
|
body
|
||||||
|
@
|
||||||
|
(comp::Body::QuadrupedMedium(quadruped_medium::Body {
|
||||||
|
species: quadruped_medium::Species::Camel,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| comp::Body::BipedLarge(biped_large::Body {
|
||||||
|
species: biped_large::Species::Cyclops,
|
||||||
|
..
|
||||||
|
})) => {
|
||||||
|
// no Camel was hurt while doing this sausage
|
||||||
|
// can't say the same about Cyclops
|
||||||
|
let (p0, p1, radius) = body.sausage();
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// It would be cool not have z_min as hardcoded 0.0
|
||||||
|
// but it needs to work nicer with terrain collisions.
|
||||||
|
comp::Collider::CapsulePrism {
|
||||||
|
p0,
|
||||||
|
p1,
|
||||||
|
radius,
|
||||||
|
z_min: 0.0,
|
||||||
|
z_max: body.height(),
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => comp::Collider::Box {
|
_ => comp::Collider::Box {
|
||||||
radius: body.radius(),
|
radius: body.radius(),
|
||||||
z_min: 0.0,
|
z_min: 0.0,
|
||||||
|
@ -36,6 +36,8 @@ pub struct Locals {
|
|||||||
/// by the shader
|
/// by the shader
|
||||||
pub pos: [f32; 4],
|
pub pos: [f32; 4],
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
|
/// quaternion as [x, y, z, w]
|
||||||
|
pub ori: [f32; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type BoundLocals = Bound<Consts<Locals>>;
|
pub type BoundLocals = Bound<Consts<Locals>>;
|
||||||
|
@ -9,7 +9,16 @@ use vek::*;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DebugShape {
|
pub enum DebugShape {
|
||||||
Line([Vec3<f32>; 2]),
|
Line([Vec3<f32>; 2]),
|
||||||
Cylinder { radius: f32, height: f32 },
|
Cylinder {
|
||||||
|
radius: f32,
|
||||||
|
height: f32,
|
||||||
|
},
|
||||||
|
CapsulePrism {
|
||||||
|
p0: Vec2<f32>,
|
||||||
|
p1: Vec2<f32>,
|
||||||
|
radius: f32,
|
||||||
|
height: f32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugShape {
|
impl DebugShape {
|
||||||
@ -22,28 +31,117 @@ impl DebugShape {
|
|||||||
let quad = |x: Vec3<f32>, y: Vec3<f32>, z: Vec3<f32>, w: Vec3<f32>| {
|
let quad = |x: Vec3<f32>, y: Vec3<f32>, z: Vec3<f32>, w: Vec3<f32>| {
|
||||||
Quad::<DebugVertex>::new(x.into(), y.into(), z.into(), w.into())
|
Quad::<DebugVertex>::new(x.into(), y.into(), z.into(), w.into())
|
||||||
};
|
};
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
DebugShape::Line([a, b]) => {
|
DebugShape::Line([a, b]) => {
|
||||||
let h = Vec3::new(0.0, 1.0, 0.0);
|
let h = Vec3::new(0.0, 1.0, 0.0);
|
||||||
mesh.push_quad(quad(*a, a + h, b + h, *b));
|
mesh.push_quad(quad(*a, a + h, b + h, *b));
|
||||||
},
|
},
|
||||||
DebugShape::Cylinder { radius, height } => {
|
DebugShape::Cylinder { radius, height } => {
|
||||||
const SUBDIVISIONS: usize = 16;
|
const SUBDIVISIONS: u8 = 16;
|
||||||
for i in 0..SUBDIVISIONS {
|
for i in 0..SUBDIVISIONS {
|
||||||
let angle = |j: usize| (j as f32 / SUBDIVISIONS as f32) * 2.0 * PI;
|
// dot on circle edge
|
||||||
let a = Vec3::zero();
|
let to = |n: u8| {
|
||||||
let b = Vec3::new(radius * angle(i).cos(), radius * angle(i).sin(), 0.0);
|
const FULL: f32 = 2.0 * PI;
|
||||||
let c = Vec3::new(
|
let angle = FULL * f32::from(n) / f32::from(SUBDIVISIONS);
|
||||||
radius * angle(i + 1).cos(),
|
|
||||||
radius * angle(i + 1).sin(),
|
Vec3::new(radius * angle.cos(), radius * angle.sin(), 0.0)
|
||||||
0.0,
|
};
|
||||||
);
|
|
||||||
|
let origin = Vec3::zero();
|
||||||
|
let r0 = to(i);
|
||||||
|
let r1 = to(i + 1);
|
||||||
|
|
||||||
let h = Vec3::new(0.0, 0.0, *height);
|
let h = Vec3::new(0.0, 0.0, *height);
|
||||||
mesh.push_tri(tri(c, b, a));
|
|
||||||
mesh.push_quad(quad(b, c, c + h, b + h));
|
// Draw bottom sector
|
||||||
mesh.push_tri(tri(a + h, b + h, c + h));
|
mesh.push_tri(tri(r1, r0, origin));
|
||||||
|
// Draw face
|
||||||
|
mesh.push_quad(quad(r0, r1, r1 + h, r0 + h));
|
||||||
|
// Draw top sector
|
||||||
|
mesh.push_tri(tri(origin + h, r0 + h, r1 + h));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
DebugShape::CapsulePrism {
|
||||||
|
p0,
|
||||||
|
p1,
|
||||||
|
radius,
|
||||||
|
height,
|
||||||
|
} => {
|
||||||
|
// We split circle in two parts
|
||||||
|
const HALF_SECTORS: u8 = 8;
|
||||||
|
const TOTAL: u8 = HALF_SECTORS * 2;
|
||||||
|
|
||||||
|
let offset = (p0 - p1).angle_between(Vec2::new(0.0, 1.0));
|
||||||
|
let h = Vec3::new(0.0, 0.0, *height);
|
||||||
|
|
||||||
|
let draw_cylinder_sector =
|
||||||
|
|mesh: &mut Mesh<DebugVertex>, origin: Vec3<f32>, from: u8, to: u8| {
|
||||||
|
for i in from..to {
|
||||||
|
// dot on circle edge
|
||||||
|
let to = |n: u8| {
|
||||||
|
const FULL: f32 = 2.0 * PI;
|
||||||
|
let angle = offset + FULL * f32::from(n) / f32::from(TOTAL);
|
||||||
|
let (x, y) = (radius * angle.cos(), radius * angle.sin());
|
||||||
|
let to_edge = Vec3::new(x, y, 0.0);
|
||||||
|
|
||||||
|
origin + to_edge
|
||||||
|
};
|
||||||
|
|
||||||
|
let r0 = to(i);
|
||||||
|
let r1 = to(i + 1);
|
||||||
|
|
||||||
|
// Draw bottom sector
|
||||||
|
mesh.push_tri(tri(r1, r0, origin));
|
||||||
|
// Draw face
|
||||||
|
mesh.push_quad(quad(r0, r1, r1 + h, r0 + h));
|
||||||
|
// Draw top sector
|
||||||
|
mesh.push_tri(tri(origin + h, r0 + h, r1 + h));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let p0 = Vec3::new(p0.x, p0.y, 0.0);
|
||||||
|
let p1 = Vec3::new(p1.x, p1.y, 0.0);
|
||||||
|
// 1) Draw first half-cylinder
|
||||||
|
draw_cylinder_sector(&mut mesh, p0, 0, HALF_SECTORS);
|
||||||
|
|
||||||
|
// 2) Draw cuboid in-between
|
||||||
|
// get main line segment
|
||||||
|
let a = p1 - p0;
|
||||||
|
// normalize
|
||||||
|
let a = a / a.magnitude();
|
||||||
|
// stretch to radius
|
||||||
|
let a = a * *radius;
|
||||||
|
// rotate to 90 degrees to get needed shift
|
||||||
|
let ortoghonal = Quaternion::rotation_z(PI / 2.0);
|
||||||
|
let shift = ortoghonal * a;
|
||||||
|
|
||||||
|
// bottom points
|
||||||
|
let a0 = p0 + shift;
|
||||||
|
let b0 = p0 - shift;
|
||||||
|
let c0 = p1 - shift;
|
||||||
|
let d0 = p1 + shift;
|
||||||
|
|
||||||
|
// top points
|
||||||
|
let a1 = a0 + h;
|
||||||
|
let b1 = b0 + h;
|
||||||
|
let c1 = c0 + h;
|
||||||
|
let d1 = d0 + h;
|
||||||
|
|
||||||
|
// Bottom
|
||||||
|
mesh.push_quad(quad(d0, c0, b0, a0));
|
||||||
|
|
||||||
|
// Faces
|
||||||
|
// (we need only two of them, because other two are inside)
|
||||||
|
mesh.push_quad(quad(d0, a0, a1, d1));
|
||||||
|
mesh.push_quad(quad(b0, c0, c1, b1));
|
||||||
|
|
||||||
|
// Top
|
||||||
|
mesh.push_quad(quad(a1, b1, c1, d1));
|
||||||
|
|
||||||
|
// 3) Draw second half-cylinder
|
||||||
|
draw_cylinder_sector(&mut mesh, p1, HALF_SECTORS, TOTAL);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
mesh
|
mesh
|
||||||
}
|
}
|
||||||
@ -55,7 +153,7 @@ pub struct DebugShapeId(pub u64);
|
|||||||
pub struct Debug {
|
pub struct Debug {
|
||||||
next_shape_id: DebugShapeId,
|
next_shape_id: DebugShapeId,
|
||||||
pending_shapes: HashMap<DebugShapeId, DebugShape>,
|
pending_shapes: HashMap<DebugShapeId, DebugShape>,
|
||||||
pending_locals: HashMap<DebugShapeId, ([f32; 4], [f32; 4])>,
|
pending_locals: HashMap<DebugShapeId, ([f32; 4], [f32; 4], [f32; 4])>,
|
||||||
pending_deletes: HashSet<DebugShapeId>,
|
pending_deletes: HashSet<DebugShapeId>,
|
||||||
models: HashMap<DebugShapeId, (Model<DebugVertex>, Bound<Consts<DebugLocals>>)>,
|
models: HashMap<DebugShapeId, (Model<DebugVertex>, Bound<Consts<DebugLocals>>)>,
|
||||||
}
|
}
|
||||||
@ -78,8 +176,8 @@ impl Debug {
|
|||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pos_and_color(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4]) {
|
pub fn set_context(&mut self, id: DebugShapeId, pos: [f32; 4], color: [f32; 4], ori: [f32; 4]) {
|
||||||
self.pending_locals.insert(id, (pos, color));
|
self.pending_locals.insert(id, (pos, color, ori));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); }
|
pub fn remove_shape(&mut self, id: DebugShapeId) { self.pending_deletes.insert(id); }
|
||||||
@ -90,6 +188,7 @@ impl Debug {
|
|||||||
let locals = renderer.create_debug_bound_locals(&[DebugLocals {
|
let locals = renderer.create_debug_bound_locals(&[DebugLocals {
|
||||||
pos: [0.0; 4],
|
pos: [0.0; 4],
|
||||||
color: [1.0, 0.0, 0.0, 1.0],
|
color: [1.0, 0.0, 0.0, 1.0],
|
||||||
|
ori: [0.0, 0.0, 0.0, 1.0],
|
||||||
}]);
|
}]);
|
||||||
self.models.insert(id, (model, locals));
|
self.models.insert(id, (model, locals));
|
||||||
} else {
|
} else {
|
||||||
@ -99,12 +198,13 @@ impl Debug {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (id, (pos, color)) in self.pending_locals.drain() {
|
for (id, (pos, color, ori)) in self.pending_locals.drain() {
|
||||||
if let Some((_, locals)) = self.models.get_mut(&id) {
|
if let Some((_, locals)) = self.models.get_mut(&id) {
|
||||||
let lc = srgba_to_linear(color.into());
|
let lc = srgba_to_linear(color.into());
|
||||||
let new_locals = [DebugLocals {
|
let new_locals = [DebugLocals {
|
||||||
pos,
|
pos,
|
||||||
color: [lc.r, lc.g, lc.b, lc.a],
|
color: [lc.r, lc.g, lc.b, lc.a],
|
||||||
|
ori,
|
||||||
}];
|
}];
|
||||||
renderer.update_consts(locals, &new_locals);
|
renderer.update_consts(locals, &new_locals);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1152,32 +1152,73 @@ impl Scene {
|
|||||||
if settings.interface.toggle_hitboxes {
|
if settings.interface.toggle_hitboxes {
|
||||||
let positions = ecs.read_component::<comp::Pos>();
|
let positions = ecs.read_component::<comp::Pos>();
|
||||||
let colliders = ecs.read_component::<comp::Collider>();
|
let colliders = ecs.read_component::<comp::Collider>();
|
||||||
|
let orientations = ecs.read_component::<comp::Ori>();
|
||||||
let groups = ecs.read_component::<comp::Group>();
|
let groups = ecs.read_component::<comp::Group>();
|
||||||
for (entity, pos, collider, group) in
|
for (entity, pos, collider, ori, group) in (
|
||||||
(&ecs.entities(), &positions, &colliders, groups.maybe()).join()
|
&ecs.entities(),
|
||||||
|
&positions,
|
||||||
|
&colliders,
|
||||||
|
&orientations,
|
||||||
|
groups.maybe(),
|
||||||
|
)
|
||||||
|
.join()
|
||||||
{
|
{
|
||||||
if let comp::Collider::Box {
|
match collider {
|
||||||
radius,
|
comp::Collider::Box {
|
||||||
z_min,
|
radius,
|
||||||
z_max,
|
z_min,
|
||||||
} = collider
|
z_max,
|
||||||
{
|
} => {
|
||||||
current_entities.insert(entity);
|
current_entities.insert(entity);
|
||||||
let shape_id = hitboxes.entry(entity).or_insert_with(|| {
|
let shape_id = hitboxes.entry(entity).or_insert_with(|| {
|
||||||
self.debug.add_shape(DebugShape::Cylinder {
|
self.debug.add_shape(DebugShape::Cylinder {
|
||||||
radius: *radius,
|
radius: *radius,
|
||||||
height: *z_max - *z_min,
|
height: *z_max - *z_min,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min, 0.0];
|
let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min, 0.0];
|
||||||
let color = if group == Some(&comp::group::ENEMY) {
|
let color = if group == Some(&comp::group::ENEMY) {
|
||||||
[1.0, 0.0, 0.0, 0.5]
|
[1.0, 0.0, 0.0, 0.5]
|
||||||
} else if group == Some(&comp::group::NPC) {
|
} else if group == Some(&comp::group::NPC) {
|
||||||
[0.0, 0.0, 1.0, 0.5]
|
[0.0, 0.0, 1.0, 0.5]
|
||||||
} else {
|
} else {
|
||||||
[0.0, 1.0, 0.0, 0.5]
|
[0.0, 1.0, 0.0, 0.5]
|
||||||
};
|
};
|
||||||
self.debug.set_pos_and_color(*shape_id, hb_pos, color);
|
// cylinders don't need orientation anyway
|
||||||
|
let ori = [0.0, 0.0, 0.0, 1.0];
|
||||||
|
self.debug.set_context(*shape_id, hb_pos, color, ori);
|
||||||
|
},
|
||||||
|
comp::Collider::CapsulePrism {
|
||||||
|
p0,
|
||||||
|
p1,
|
||||||
|
radius,
|
||||||
|
z_min,
|
||||||
|
z_max,
|
||||||
|
} => {
|
||||||
|
current_entities.insert(entity);
|
||||||
|
let shape_id = hitboxes.entry(entity).or_insert_with(|| {
|
||||||
|
self.debug.add_shape(DebugShape::CapsulePrism {
|
||||||
|
p0: *p0,
|
||||||
|
p1: *p1,
|
||||||
|
radius: *radius,
|
||||||
|
height: *z_max - *z_min,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let hb_pos = [pos.0.x, pos.0.y, pos.0.z + *z_min, 0.0];
|
||||||
|
let color = if group == Some(&comp::group::ENEMY) {
|
||||||
|
[1.0, 0.0, 0.0, 0.5]
|
||||||
|
} else if group == Some(&comp::group::NPC) {
|
||||||
|
[0.0, 0.0, 1.0, 0.5]
|
||||||
|
} else {
|
||||||
|
[0.0, 1.0, 0.0, 0.5]
|
||||||
|
};
|
||||||
|
let ori = ori.to_quat();
|
||||||
|
let hb_ori = [ori.x, ori.y, ori.z, ori.w];
|
||||||
|
self.debug.set_context(*shape_id, hb_pos, color, hb_ori);
|
||||||
|
},
|
||||||
|
comp::Collider::Voxel { .. } | comp::Collider::Point => {
|
||||||
|
// ignore terrain-like or point-hitboxes
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,10 @@ impl EguiState {
|
|||||||
scene.debug.remove_shape(DebugShapeId(*debug_shape_id));
|
scene.debug.remove_shape(DebugShapeId(*debug_shape_id));
|
||||||
},
|
},
|
||||||
DebugShapeAction::SetPosAndColor { id, pos, color } => {
|
DebugShapeAction::SetPosAndColor { id, pos, color } => {
|
||||||
|
let identity_ori = [0.0, 0.0, 0.0, 1.0];
|
||||||
scene
|
scene
|
||||||
.debug
|
.debug
|
||||||
.set_pos_and_color(DebugShapeId(*id), *pos, *color);
|
.set_context(DebugShapeId(*id), *pos, *color, identity_ori);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user