1use crate::components::*;
18use crate::skeletal::{SkinnedModel, Trs};
19use glam::{Mat4, Quat, Vec3};
20use kvas_ecs::{Entity, World};
21use serde::{Deserialize, Serialize};
22
23pub const RAGDOLL_GROUP: u32 = 1 << 29;
25
26#[derive(Clone, Serialize, Deserialize)]
28#[serde(default)]
29pub struct Ragdoll {
30 pub active: bool,
32 pub mass: f32,
34 pub impulse: Vec3,
36 pub damping: f32,
38}
39
40impl Default for Ragdoll {
41 fn default() -> Self {
42 Self { active: false, mass: 65.0, impulse: Vec3::ZERO, damping: 1.5 }
43 }
44}
45
46impl Component for Ragdoll {
47 fn fields(&mut self) -> Vec<crate::reflect::Field<'_>> {
48 use crate::reflect::Field;
49 vec![Field::bool("active", &mut self.active), Field::f32("mass", &mut self.mass), Field::vec3("impulse", &mut self.impulse), Field::f32("damping", &mut self.damping)]
50 }
51}
52
53#[derive(Clone)]
55pub(crate) struct Part {
56 node: usize,
57 entity: Entity,
58 offset: Mat4,
59}
60
61#[derive(Clone, Default)]
63pub(crate) struct RagdollState {
64 pub(crate) parts: Vec<Part>,
65 start: Vec<Trs>,
66}
67
68impl RagdollState {
69 pub(crate) fn is_active(&self) -> bool {
70 !self.parts.is_empty()
71 }
72}
73
74fn body_name(owner: Entity, i: usize) -> String {
75 format!("ragdoll#{}#{i}", owner.to_bits())
76}
77
78pub(crate) fn activate(world_ecs: &mut World, owner: Entity, model: &SkinnedModel, pose: &[Trs], world: Mat4, rd: &Ragdoll, velocity: Vec3) -> RagdollState {
80 let Some(h) = &model.humanoid else { return RagdollState::default() };
81 let at = |n: Option<usize>| n.and_then(|i| pose.get(i)).map(|p| world.transform_point3(p.t));
82 let up = world.transform_vector3(h.up).normalize_or(Vec3::Y);
83 let (Some(hips), Some(head)) = (at(h.hips), at(h.head)) else { return RagdollState::default() };
84 let height = ((head - hips).length() * 2.4).max(0.5);
85 let chest = at(h.chest).or(at(h.spine));
86 let chest_node = h.chest.or(h.spine);
87 let mut spec: Vec<(usize, Vec3, Vec3, f32, f32, Option<usize>)> = Vec::new();
89 let torso_top = chest.unwrap_or(head);
90 spec.push((h.hips.unwrap_or(0), hips, torso_top, 0.075, 0.26, None));
91 let mut upper = 0;
92 if let (Some(c), Some(cn)) = (chest, chest_node) {
93 spec.push((cn, c, at(h.neck).unwrap_or(head), 0.08, 0.24, Some(0)));
94 upper = 1;
95 }
96 let head_part = spec.len();
97 spec.push((h.head.unwrap_or(0), head, head + up * height * 0.13, 0.06, 0.08, Some(upper)));
98 let _ = head_part;
99 for s in 0..2 {
100 if let (Some(ua), Some(fa)) = (at(h.upper_arm[s]), at(h.forearm[s])) {
101 let p = spec.len();
102 spec.push((h.upper_arm[s].unwrap_or(0), ua, fa, 0.03, 0.03, Some(upper)));
103 let hand = at(h.hand[s]).unwrap_or(fa + (fa - ua));
104 spec.push((h.forearm[s].unwrap_or(0), fa, hand, 0.025, 0.02, Some(p)));
105 }
106 if let (Some(th), Some(sh)) = (at(h.thigh[s]), at(h.shin[s])) {
107 let p = spec.len();
108 spec.push((h.thigh[s].unwrap_or(0), th, sh, 0.045, 0.1, Some(0)));
109 let foot = at(h.foot[s]).unwrap_or(sh + (sh - th));
110 spec.push((h.shin[s].unwrap_or(0), sh, foot, 0.035, 0.05, Some(p)));
111 }
112 }
113 let mut parts = Vec::new();
114 for (i, (node, from, to, r, share, parent)) in spec.iter().enumerate() {
115 let seg = *to - *from;
116 let len = seg.length().max(0.02);
117 let radius = (r * height).clamp(0.015, len * 0.5);
118 let center = (*from + *to) * 0.5;
119 let rot = Quat::from_rotation_arc(Vec3::Y, seg / len);
120 let e = world_ecs.spawn();
121 world_ecs.insert(e, Name(body_name(owner, i)));
122 let mut t = Transform { position: center, ..Default::default() };
123 t.set_quat(rot);
124 world_ecs.insert(e, t);
125 world_ecs.insert(e, Transient);
126 world_ecs.insert(
127 e,
128 RigidBody {
129 mass: (rd.mass * share).max(0.2),
130 linear_damping: 0.15,
131 angular_damping: rd.damping,
132 velocity,
133 set_velocity: Some(velocity),
134 impulse: if i == upper { rd.impulse } else { Vec3::ZERO },
135 ..Default::default()
136 },
137 );
138 world_ecs.insert(
139 e,
140 Collider { shape: ShapeKind::Capsule, size: Vec3::new(radius * 2.0, len.max(radius * 2.0 + 0.01), radius * 2.0), friction: 0.8, restitution: 0.0, memberships: RAGDOLL_GROUP, filter: !RAGDOLL_GROUP, ..Default::default() },
141 );
142 if let Some(p) = parent {
143 world_ecs.insert(e, Joint { kind: JointKind::Ball, target: body_name(owner, *p), anchor: Vec3::new(0.0, -len * 0.5, 0.0), ..Default::default() });
144 }
145 let body = Mat4::from_rotation_translation(rot, center);
146 let bone = world * pose[*node].mat();
147 parts.push(Part { node: *node, entity: e, offset: body.inverse() * bone });
148 }
149 RagdollState { parts, start: pose.to_vec() }
150}
151
152pub(crate) fn pose(world_ecs: &World, model: &SkinnedModel, st: &RagdollState, world: Mat4, out: &mut Vec<Trs>) {
155 let inv = world.inverse();
156 let start: Vec<Mat4> = st.start.iter().map(Trs::mat).collect();
157 let mut now: Vec<Option<Mat4>> = vec![None; model.nodes.len()];
158 for p in &st.parts {
159 if let Some(t) = world_ecs.get::<Transform>(p.entity) {
160 now[p.node] = Some(inv * t.matrix() * p.offset);
161 }
162 }
163 let depth = |mut i: usize| {
165 let mut d = 0;
166 while let Some(p) = model.nodes[i].parent {
167 i = p;
168 d += 1;
169 if d > 128 {
170 break;
171 }
172 }
173 d
174 };
175 let mut order: Vec<usize> = (0..model.nodes.len()).collect();
176 order.sort_by_key(|&i| depth(i));
177 for &i in &order {
178 if now[i].is_some() {
179 continue;
180 }
181 now[i] = Some(match model.nodes[i].parent {
182 Some(p) => now[p].unwrap_or(start[p]) * start[p].inverse() * start[i],
183 None => start[i],
184 });
185 }
186 out.clear();
187 out.extend(now.into_iter().map(|m| Trs::from_mat(m.unwrap_or(Mat4::IDENTITY))));
188}
189
190pub(crate) fn deactivate(world_ecs: &mut World, st: &mut RagdollState) {
192 for p in st.parts.drain(..) {
193 world_ecs.despawn(p.entity);
194 }
195 st.start.clear();
196}