#![allow(
clippy::needless_pass_by_value,
clippy::type_complexity,
clippy::option_if_let_else,
clippy::suboptimal_flops
)]
use crate::components::{cloth::Cloth, cloth_rendering::ClothRendering, collider::ClothCollider};
use bevy::log::{debug, error};
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
fn get_collider(
rendering: &ClothRendering,
collider: &ClothCollider,
matrix: Option<&Mat4>,
) -> Collider {
let (center, extents): (Vec3, Vec3) = rendering.compute_aabb(Some(collider.offset));
Collider::compound(vec![(
matrix.map_or(center, |mat| mat.transform_point3(center)),
Quat::IDENTITY,
Collider::cuboid(extents.x, extents.y, extents.z),
)])
}
pub fn handle_collisions(
mut cloth_query: Query<(
Entity,
&mut Cloth,
&ClothRendering,
&ClothCollider,
&mut Collider,
)>,
rapier_context: Res<RapierContext>,
mut colliders_query: Query<
(&Collider, &GlobalTransform, Option<&mut Velocity>),
Without<Cloth>,
>,
time: Res<Time>,
) {
let delta_time = time.delta_seconds();
for (entity, mut cloth, rendering, collider, mut rapier_collider) in cloth_query.iter_mut() {
for contact_pair in rapier_context.contacts_with(entity) {
let other_entity = if contact_pair.collider1() == entity {
contact_pair.collider2()
} else {
contact_pair.collider1()
};
let Ok((other_collider, other_transform, other_velocity)) = colliders_query.get_mut(other_entity) else {
error!("Couldn't find collider on entity {:?}", entity);
continue;
};
let vel = other_velocity.as_ref().map_or(0.0, |v| {
v.linvel.length_squared() * delta_time * delta_time * collider.velocity_coefficient
});
cloth.solve_collisions(|point| {
let other_transform = other_transform.compute_transform();
let projected_point = other_collider.project_point(
other_transform.translation,
other_transform.rotation,
*point,
false,
);
let normal: Vec3 = (projected_point.point - *point)
.try_normalize()
.unwrap_or(Vec3::Y);
if projected_point.is_inside {
Some(projected_point.point + (normal * collider.offset) + (normal * vel))
} else if point.distance_squared(projected_point.point)
< collider.offset * collider.offset
{
Some(projected_point.point - (normal * collider.offset))
} else {
None
}
});
if let Some((ref mut vel, dampen_coef)) = other_velocity.zip(collider.dampen_others) {
let damp = 1.0 - dampen_coef;
vel.linvel *= damp;
vel.angvel *= damp;
}
}
*rapier_collider = get_collider(rendering, collider, None);
}
}
pub fn init_cloth_collider(
mut commands: Commands,
cloth_query: Query<
(Entity, &GlobalTransform, &ClothRendering, &ClothCollider),
(With<Cloth>, Without<Collider>),
>,
) {
for (entity, transform, rendering, collider) in cloth_query.iter() {
let matrix = transform.compute_matrix();
debug!("Initializing Cloth collisions for {:?}", entity);
commands.entity(entity).insert((
RigidBody::KinematicPositionBased,
get_collider(rendering, collider, Some(&matrix)),
SolverGroups::new(Group::NONE, Group::NONE),
));
}
}