use bevy_ecs::entity::Entity;
use bevy_ecs::prelude::{Component, World};
use nalgebra::{Point3, Vector3};
use serde::{Deserialize, Serialize};
use super::cpu_physics::next_spawn_order;
use super::{
sim_math, Collider, ColliderShape, Joint, JointKind, PhysicsBody,
RigidBody, RigidBodyKind, SceneId,
};
use crate::Transform;
pub const MAX_SEGMENTS: u32 = 256;
#[derive(Component, Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct Rope {
pub target: Entity,
pub anchor: [f32; 3],
pub target_anchor: [f32; 3],
pub length: f32,
pub segments: u32,
pub radius: f32,
pub mass: f32,
}
impl Default for Rope {
fn default() -> Self {
Self {
target: Entity::PLACEHOLDER,
anchor: [0.0; 3],
target_anchor: [0.0; 3],
length: 2.0,
segments: 8,
radius: 0.05,
mass: 0.1,
}
}
}
#[derive(Component, Clone, Debug, Default, PartialEq)]
pub struct RopeState {
pub beads: Vec<Entity>,
}
#[derive(Component, Clone, Copy, Debug, PartialEq, Eq)]
pub struct RopeBead {
pub rope: Entity,
}
fn world_point(world: &World, entity: Entity, local: [f32; 3]) -> Point3<f32> {
match world.get::<Transform>(entity) {
Some(transform) if entity != Entity::PLACEHOLDER => {
let [roll, pitch, yaw] = transform.rotation;
let turn = sim_math::rotation_from_euler(roll, pitch, yaw);
Point3::from(transform.position) + turn * Vector3::from(local)
}
_ => local.into(),
}
}
pub(super) fn build_ropes(world: &mut World) {
let mut ropes: Vec<_> = world
.query::<(Entity, &Rope, Option<&SceneId>, Option<&RopeState>)>()
.iter(world)
.filter(|(.., state)| state.is_none())
.map(|(entity, rope, id, _)| {
(id.map(|id| id.0.as_u128()), entity, *rope)
})
.collect();
ropes.sort_unstable_by_key(|(id, entity, _)| (*id, entity.to_bits()));
for (_, owner, rope) in ropes {
let count = rope.segments.clamp(1, MAX_SEGMENTS);
let link = rope.length / (count + 1) as f32;
let from = world_point(world, rope.target, rope.target_anchor);
let to = world_point(world, owner, rope.anchor);
let mut beads = Vec::with_capacity(count as usize);
let mut previous = (rope.target, rope.target_anchor);
for i in 1..=count {
let at = from + (to - from) * (i as f32 / (count + 1) as f32);
let order = next_spawn_order(world);
let bead = world
.spawn((
Transform::new(at.into()),
Collider {
shape: ColliderShape::Sphere {
radius: rope.radius,
},
..Collider::default()
},
PhysicsBody::default(),
RigidBody {
kind: RigidBodyKind::Dynamic,
mass: rope.mass,
..RigidBody::default()
},
rope_joint(previous, [0.0; 3], link),
RopeBead { rope: owner },
order,
))
.id();
beads.push(bead);
previous = (bead, [0.0; 3]);
}
let mut entity = world.entity_mut(owner);
if !entity.contains::<Joint>() {
entity.insert(rope_joint(previous, rope.anchor, link));
}
entity.insert(RopeState { beads });
}
}
fn rope_joint(
(target, target_anchor): (Entity, [f32; 3]),
anchor: [f32; 3],
link: f32,
) -> Joint {
Joint {
target,
kind: JointKind::Distance {
min: 0.0,
max: link,
},
anchor,
target_anchor,
collide_connected: false,
..Joint::default()
}
}