1use super::functions::*;
6pub struct IntegratePositionKernel;
8pub struct ConstraintSolverKernel;
13impl ConstraintSolverKernel {
14 pub fn solve_distance_constraints(
19 positions: &mut [[f64; 3]],
20 inv_masses: &[f64],
21 constraints: &[DistanceConstraint],
22 dt: f64,
23 ) {
24 let alpha = 1.0 / (dt * dt);
25 for c in constraints {
26 let a = c.body_a;
27 let b = c.body_b;
28 let w_a = inv_masses[a];
29 let w_b = inv_masses[b];
30 let w_total = w_a + w_b;
31 if w_total < 1e-30 {
32 continue;
33 }
34 let dx = [
35 positions[b][0] - positions[a][0],
36 positions[b][1] - positions[a][1],
37 positions[b][2] - positions[a][2],
38 ];
39 let dist = (dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]).sqrt();
40 if dist < 1e-30 {
41 continue;
42 }
43 let err = dist - c.rest_length;
44 let tilde_compliance = c.compliance * alpha;
45 let lambda = -err / (w_total + tilde_compliance);
46 let correction = lambda / dist;
47 for k in 0..3 {
48 positions[a][k] -= w_a * correction * dx[k];
49 positions[b][k] += w_b * correction * dx[k];
50 }
51 }
52 }
53}
54#[derive(Debug, Clone, Copy)]
56pub struct SleepParams {
57 pub linear_threshold: f64,
59 pub angular_threshold: f64,
61 pub sleep_frames: u32,
63}
64#[derive(Debug, Clone, Copy, PartialEq)]
68pub struct RigidBodyState {
69 pub position: [f64; 3],
71 pub velocity: [f64; 3],
73 pub orientation: [f64; 4],
75 pub angular_velocity: [f64; 3],
77 pub inverse_mass: f64,
79}
80impl RigidBodyState {
81 pub fn at_rest(position: [f64; 3], inverse_mass: f64) -> Self {
83 Self {
84 position,
85 velocity: [0.0; 3],
86 orientation: [0.0, 0.0, 0.0, 1.0],
87 angular_velocity: [0.0; 3],
88 inverse_mass,
89 }
90 }
91}
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum SleepState {
95 Awake,
97 Sleeping,
99}
100pub struct ContactBatchProcessor {
106 pub restitution: f64,
108 pub baumgarte: f64,
110}
111impl ContactBatchProcessor {
112 pub fn new(restitution: f64, baumgarte: f64) -> Self {
114 Self {
115 restitution,
116 baumgarte,
117 }
118 }
119 pub fn process_contacts(
124 &self,
125 contacts: &[ContactPoint],
126 positions: &[[f64; 3]],
127 velocities: &[[f64; 3]],
128 inv_masses: &[f64],
129 n_bodies: usize,
130 dt: f64,
131 ) -> Vec<AccumulatedImpulse> {
132 let mut impulses = vec![AccumulatedImpulse::default(); n_bodies];
133 for c in contacts {
134 let a = c.body_a;
135 let b = c.body_b;
136 let w_a = inv_masses[a];
137 let w_b = inv_masses[b];
138 let w_total = w_a + w_b;
139 if w_total < 1e-30 {
140 continue;
141 }
142 let corr = self.baumgarte * c.depth / (w_total * dt);
143 let rel_vel: f64 = (0..3)
144 .map(|k| (velocities[b][k] - velocities[a][k]) * c.normal[k])
145 .sum();
146 let j = if rel_vel < 0.0 {
147 -(1.0 + self.restitution) * rel_vel / w_total
148 } else {
149 0.0
150 };
151 let impulse_a = [
152 -w_a * (j + corr) * c.normal[0],
153 -w_a * (j + corr) * c.normal[1],
154 -w_a * (j + corr) * c.normal[2],
155 ];
156 let impulse_b = [
157 w_b * (j + corr) * c.normal[0],
158 w_b * (j + corr) * c.normal[1],
159 w_b * (j + corr) * c.normal[2],
160 ];
161 for k in 0..3 {
162 impulses[a].linear[k] += impulse_a[k];
163 impulses[b].linear[k] += impulse_b[k];
164 }
165 let ra = [
166 c.position[0] - positions[a][0],
167 c.position[1] - positions[a][1],
168 c.position[2] - positions[a][2],
169 ];
170 let rb = [
171 c.position[0] - positions[b][0],
172 c.position[1] - positions[b][1],
173 c.position[2] - positions[b][2],
174 ];
175 let ang_a = [
176 ra[1] * impulse_a[2] - ra[2] * impulse_a[1],
177 ra[2] * impulse_a[0] - ra[0] * impulse_a[2],
178 ra[0] * impulse_a[1] - ra[1] * impulse_a[0],
179 ];
180 let ang_b = [
181 rb[1] * impulse_b[2] - rb[2] * impulse_b[1],
182 rb[2] * impulse_b[0] - rb[0] * impulse_b[2],
183 rb[0] * impulse_b[1] - rb[1] * impulse_b[0],
184 ];
185 impulses[a].add_angular(ang_a);
186 impulses[b].add_angular(ang_b);
187 }
188 impulses
189 }
190}
191pub struct IslandSolver {
196 pub island_ids: Vec<usize>,
198 pub num_islands: usize,
200}
201impl IslandSolver {
202 pub fn build(num_bodies: usize, constraints: &[(usize, usize)]) -> Self {
204 let mut parent: Vec<usize> = (0..num_bodies).collect();
205 let mut rank = vec![0usize; num_bodies];
206 let find = |parent: &mut Vec<usize>, mut x: usize| -> usize {
207 while parent[x] != x {
208 parent[x] = parent[parent[x]];
209 x = parent[x];
210 }
211 x
212 };
213 for &(a, b) in constraints {
214 let ra = find(&mut parent, a);
215 let rb = find(&mut parent, b);
216 if ra != rb {
217 if rank[ra] < rank[rb] {
218 parent[ra] = rb;
219 } else if rank[ra] > rank[rb] {
220 parent[rb] = ra;
221 } else {
222 parent[rb] = ra;
223 rank[ra] += 1;
224 }
225 }
226 }
227 let mut island_map = std::collections::HashMap::new();
228 let mut island_ids = vec![0usize; num_bodies];
229 let mut next_id = 0usize;
230 for (i, island_id) in island_ids.iter_mut().enumerate() {
231 let root = find(&mut parent, i);
232 let id = *island_map.entry(root).or_insert_with(|| {
233 let id = next_id;
234 next_id += 1;
235 id
236 });
237 *island_id = id;
238 }
239 Self {
240 island_ids,
241 num_islands: next_id,
242 }
243 }
244 pub fn bodies_in_island(&self, island_id: usize) -> Vec<usize> {
246 self.island_ids
247 .iter()
248 .enumerate()
249 .filter(|&(_, id)| *id == island_id)
250 .map(|(i, _)| i)
251 .collect()
252 }
253}
254pub struct SleepTest {
256 pub dormant_frames: Vec<u32>,
258 pub sleep_states: Vec<SleepState>,
260}
261impl SleepTest {
262 pub fn new(n: usize) -> Self {
264 Self {
265 dormant_frames: vec![0; n],
266 sleep_states: vec![SleepState::Awake; n],
267 }
268 }
269 pub fn update(&mut self, states: &[RigidBodyState], params: &SleepParams) -> usize {
273 let mut newly_sleeping = 0;
274 for (i, s) in states.iter().enumerate() {
275 if s.inverse_mass < 1e-30 {
276 self.sleep_states[i] = SleepState::Sleeping;
277 continue;
278 }
279 let lin_sq: f64 = s.velocity.iter().map(|v| v * v).sum();
280 let ang_sq: f64 = s.angular_velocity.iter().map(|w| w * w).sum();
281 let dormant = lin_sq < params.linear_threshold * params.linear_threshold
282 && ang_sq < params.angular_threshold * params.angular_threshold;
283 if dormant {
284 self.dormant_frames[i] += 1;
285 if self.dormant_frames[i] >= params.sleep_frames
286 && self.sleep_states[i] == SleepState::Awake
287 {
288 self.sleep_states[i] = SleepState::Sleeping;
289 newly_sleeping += 1;
290 }
291 } else {
292 self.dormant_frames[i] = 0;
293 self.sleep_states[i] = SleepState::Awake;
294 }
295 }
296 newly_sleeping
297 }
298 pub fn wake_all(&mut self) {
300 for s in &mut self.sleep_states {
301 *s = SleepState::Awake;
302 }
303 for f in &mut self.dormant_frames {
304 *f = 0;
305 }
306 }
307 pub fn sleeping_count(&self) -> usize {
309 self.sleep_states
310 .iter()
311 .filter(|&&s| s == SleepState::Sleeping)
312 .count()
313 }
314}
315#[derive(Debug, Clone, Copy, Default)]
317pub struct AccumulatedImpulse {
318 pub linear: [f64; 3],
320 pub angular: [f64; 3],
322}
323impl AccumulatedImpulse {
324 pub fn add_linear(&mut self, impulse: [f64; 3]) {
326 for (l, &imp) in self.linear.iter_mut().zip(impulse.iter()) {
327 *l += imp;
328 }
329 }
330 pub fn add_angular(&mut self, impulse: [f64; 3]) {
332 for (a, &imp) in self.angular.iter_mut().zip(impulse.iter()) {
333 *a += imp;
334 }
335 }
336 pub fn apply(&self, state: &mut RigidBodyState) {
338 let im = state.inverse_mass;
339 for k in 0..3 {
340 state.velocity[k] += self.linear[k] * im;
341 state.angular_velocity[k] += self.angular[k] * im;
342 }
343 }
344 pub fn linear_magnitude(&self) -> f64 {
346 self.linear.iter().map(|v| v * v).sum::<f64>().sqrt()
347 }
348 pub fn angular_magnitude(&self) -> f64 {
350 self.angular.iter().map(|v| v * v).sum::<f64>().sqrt()
351 }
352}
353pub struct IntegrateVelocityKernel;
355#[derive(Debug, Clone, Copy)]
357pub struct DistanceConstraint {
358 pub body_a: usize,
360 pub body_b: usize,
362 pub rest_length: f64,
364 pub compliance: f64,
366}
367pub struct SoaRigidBody {
373 pub count: usize,
375 pub pos_x: Vec<f64>,
376 pub pos_y: Vec<f64>,
377 pub pos_z: Vec<f64>,
378 pub vel_x: Vec<f64>,
379 pub vel_y: Vec<f64>,
380 pub vel_z: Vec<f64>,
381 pub quat_x: Vec<f64>,
382 pub quat_y: Vec<f64>,
383 pub quat_z: Vec<f64>,
384 pub quat_w: Vec<f64>,
385 pub omega_x: Vec<f64>,
386 pub omega_y: Vec<f64>,
387 pub omega_z: Vec<f64>,
388 pub inv_mass: Vec<f64>,
389}
390impl SoaRigidBody {
391 pub fn from_slice(states: &[RigidBodyState]) -> Self {
393 let n = states.len();
394 let mut s = Self {
395 count: n,
396 pos_x: Vec::with_capacity(n),
397 pos_y: Vec::with_capacity(n),
398 pos_z: Vec::with_capacity(n),
399 vel_x: Vec::with_capacity(n),
400 vel_y: Vec::with_capacity(n),
401 vel_z: Vec::with_capacity(n),
402 quat_x: Vec::with_capacity(n),
403 quat_y: Vec::with_capacity(n),
404 quat_z: Vec::with_capacity(n),
405 quat_w: Vec::with_capacity(n),
406 omega_x: Vec::with_capacity(n),
407 omega_y: Vec::with_capacity(n),
408 omega_z: Vec::with_capacity(n),
409 inv_mass: Vec::with_capacity(n),
410 };
411 for rb in states {
412 s.pos_x.push(rb.position[0]);
413 s.pos_y.push(rb.position[1]);
414 s.pos_z.push(rb.position[2]);
415 s.vel_x.push(rb.velocity[0]);
416 s.vel_y.push(rb.velocity[1]);
417 s.vel_z.push(rb.velocity[2]);
418 s.quat_x.push(rb.orientation[0]);
419 s.quat_y.push(rb.orientation[1]);
420 s.quat_z.push(rb.orientation[2]);
421 s.quat_w.push(rb.orientation[3]);
422 s.omega_x.push(rb.angular_velocity[0]);
423 s.omega_y.push(rb.angular_velocity[1]);
424 s.omega_z.push(rb.angular_velocity[2]);
425 s.inv_mass.push(rb.inverse_mass);
426 }
427 s
428 }
429 pub fn to_vec(&self) -> Vec<RigidBodyState> {
431 (0..self.count)
432 .map(|i| RigidBodyState {
433 position: [self.pos_x[i], self.pos_y[i], self.pos_z[i]],
434 velocity: [self.vel_x[i], self.vel_y[i], self.vel_z[i]],
435 orientation: [
436 self.quat_x[i],
437 self.quat_y[i],
438 self.quat_z[i],
439 self.quat_w[i],
440 ],
441 angular_velocity: [self.omega_x[i], self.omega_y[i], self.omega_z[i]],
442 inverse_mass: self.inv_mass[i],
443 })
444 .collect()
445 }
446 pub fn integrate_euler(&mut self, forces: &[[f64; 3]], torques: &[[f64; 3]], dt: f64) {
451 for i in 0..self.count {
452 let im = self.inv_mass[i];
453 let ax = forces[i][0] * im;
454 let ay = forces[i][1] * im;
455 let az = forces[i][2] * im;
456 self.vel_x[i] += ax * dt;
457 self.vel_y[i] += ay * dt;
458 self.vel_z[i] += az * dt;
459 self.pos_x[i] += self.vel_x[i] * dt;
460 self.pos_y[i] += self.vel_y[i] * dt;
461 self.pos_z[i] += self.vel_z[i] * dt;
462 let alpha_x = torques[i][0] * im;
463 let alpha_y = torques[i][1] * im;
464 let alpha_z = torques[i][2] * im;
465 self.omega_x[i] += alpha_x * dt;
466 self.omega_y[i] += alpha_y * dt;
467 self.omega_z[i] += alpha_z * dt;
468 let q = [
469 self.quat_x[i],
470 self.quat_y[i],
471 self.quat_z[i],
472 self.quat_w[i],
473 ];
474 let omega = [self.omega_x[i], self.omega_y[i], self.omega_z[i]];
475 let dq = quat_derivative(q, omega);
476 self.quat_x[i] += dq[0] * dt;
477 self.quat_y[i] += dq[1] * dt;
478 self.quat_z[i] += dq[2] * dt;
479 self.quat_w[i] += dq[3] * dt;
480 let qn = quat_normalise([
481 self.quat_x[i],
482 self.quat_y[i],
483 self.quat_z[i],
484 self.quat_w[i],
485 ]);
486 self.quat_x[i] = qn[0];
487 self.quat_y[i] = qn[1];
488 self.quat_z[i] = qn[2];
489 self.quat_w[i] = qn[3];
490 }
491 }
492}
493#[derive(Debug, Clone, Copy)]
495pub struct Aabb {
496 pub min: [f64; 3],
497 pub max: [f64; 3],
498}
499impl Aabb {
500 pub fn from_center(position: [f64; 3], half_extents: [f64; 3]) -> Self {
502 Self {
503 min: [
504 position[0] - half_extents[0],
505 position[1] - half_extents[1],
506 position[2] - half_extents[2],
507 ],
508 max: [
509 position[0] + half_extents[0],
510 position[1] + half_extents[1],
511 position[2] + half_extents[2],
512 ],
513 }
514 }
515 pub fn overlaps(&self, other: &Aabb) -> bool {
517 self.min[0] <= other.max[0]
518 && self.max[0] >= other.min[0]
519 && self.min[1] <= other.max[1]
520 && self.max[1] >= other.min[1]
521 && self.min[2] <= other.max[2]
522 && self.max[2] >= other.min[2]
523 }
524 pub fn expand(&mut self, margin: f64) {
526 for k in 0..3 {
527 self.min[k] -= margin;
528 self.max[k] += margin;
529 }
530 }
531 pub fn volume(&self) -> f64 {
533 (self.max[0] - self.min[0]) * (self.max[1] - self.min[1]) * (self.max[2] - self.min[2])
534 }
535}
536pub struct BroadphaseUpdateKernel;
539impl BroadphaseUpdateKernel {
540 pub fn find_overlapping_pairs(
545 positions: &[[f64; 3]],
546 half_extents: &[[f64; 3]],
547 margin: f64,
548 ) -> Vec<(usize, usize)> {
549 let n = positions.len();
550 let mut aabbs: Vec<Aabb> = Vec::with_capacity(n);
551 for i in 0..n {
552 let mut aabb = Aabb::from_center(positions[i], half_extents[i]);
553 aabb.expand(margin);
554 aabbs.push(aabb);
555 }
556 let mut pairs = Vec::new();
557 for i in 0..n {
558 for j in (i + 1)..n {
559 if aabbs[i].overlaps(&aabbs[j]) {
560 pairs.push((i, j));
561 }
562 }
563 }
564 pairs
565 }
566}
567#[derive(Debug, Clone, Copy)]
569pub struct ContactPoint {
570 pub position: [f64; 3],
572 pub normal: [f64; 3],
574 pub depth: f64,
576 pub body_a: usize,
578 pub body_b: usize,
580}
581pub struct SemiImplicitEulerKernel;
586pub struct QuaternionNormKernel;
591impl QuaternionNormKernel {
592 pub fn normalize_batch(quats: &[[f64; 4]]) -> Vec<[f64; 4]> {
594 quats.iter().map(|&q| quat_normalise(q)).collect()
595 }
596 pub fn normalize_in_place(quats: &mut [[f64; 4]]) {
598 for q in quats.iter_mut() {
599 *q = quat_normalise(*q);
600 }
601 }
602 pub fn count_denormalized(quats: &[[f64; 4]], tol: f64) -> usize {
604 quats
605 .iter()
606 .filter(|&&q| {
607 let len = (q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]).sqrt();
608 (len - 1.0).abs() > tol
609 })
610 .count()
611 }
612}
613pub struct ContactGenerationKernel;
616impl ContactGenerationKernel {
617 pub fn generate_sphere_contacts(
623 positions: &[[f64; 3]],
624 radii: &[f64],
625 pairs: &[(usize, usize)],
626 ) -> Vec<ContactPoint> {
627 let mut contacts = Vec::new();
628 for &(a, b) in pairs {
629 let dx = [
630 positions[b][0] - positions[a][0],
631 positions[b][1] - positions[a][1],
632 positions[b][2] - positions[a][2],
633 ];
634 let dist_sq = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
635 let sum_r = radii[a] + radii[b];
636 if dist_sq < sum_r * sum_r {
637 let dist = dist_sq.sqrt();
638 let depth = sum_r - dist;
639 let normal = if dist > 1e-14 {
640 [dx[0] / dist, dx[1] / dist, dx[2] / dist]
641 } else {
642 [0.0, 1.0, 0.0]
643 };
644 let contact_pos = [
645 positions[a][0] + normal[0] * radii[a],
646 positions[a][1] + normal[1] * radii[a],
647 positions[a][2] + normal[2] * radii[a],
648 ];
649 contacts.push(ContactPoint {
650 position: contact_pos,
651 normal,
652 depth,
653 body_a: a,
654 body_b: b,
655 });
656 }
657 }
658 contacts
659 }
660 pub fn resolve_contacts(
664 positions: &mut [[f64; 3]],
665 velocities: &mut [[f64; 3]],
666 inv_masses: &[f64],
667 contacts: &[ContactPoint],
668 restitution: f64,
669 ) {
670 for c in contacts {
671 let a = c.body_a;
672 let b = c.body_b;
673 let w_a = inv_masses[a];
674 let w_b = inv_masses[b];
675 let w_total = w_a + w_b;
676 if w_total < 1e-30 {
677 continue;
678 }
679 let correction = c.depth / w_total;
680 for (k, &n_k) in c.normal.iter().enumerate() {
681 positions[a][k] -= w_a * correction * n_k;
682 positions[b][k] += w_b * correction * n_k;
683 }
684 let rel_vel = [
685 velocities[b][0] - velocities[a][0],
686 velocities[b][1] - velocities[a][1],
687 velocities[b][2] - velocities[a][2],
688 ];
689 let vel_along_normal =
690 rel_vel[0] * c.normal[0] + rel_vel[1] * c.normal[1] + rel_vel[2] * c.normal[2];
691 if vel_along_normal < 0.0 {
692 let j = -(1.0 + restitution) * vel_along_normal / w_total;
693 for (k, &n_k) in c.normal.iter().enumerate() {
694 velocities[a][k] -= w_a * j * n_k;
695 velocities[b][k] += w_b * j * n_k;
696 }
697 }
698 }
699 }
700}