~starkingdoms/starkingdoms

ref: b5f0ffc992d78c30e2dfa85bd2b22cf609135a98 starkingdoms/crates/unified/src/server/damping.rs -rw-r--r-- 3.0 KiB
b5f0ffc9ghostly_zsh fix: forgot to make hearty thrusters normal again 17 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use avian2d::{dynamics::{joints::EntityConstraint, solver::{joint_damping, schedule::SubstepSolverSystems, solver_body::{SolverBody, SolverBodyInertia}}}, math::RecipOrZero};
use bevy::math::DVec2;

use crate::prelude::*;

pub fn damping_plugin(app: &mut App) {
    let substeps = app.get_schedule_mut(SubstepSchedule).expect("Add SubstepSchedule first");
    substeps.add_systems(
        module_joint_damping::<FixedJoint>
        .after(joint_damping::<DistanceJoint>) // DistanceJoint is the last damping in vanilla avian
        .in_set(SubstepSolverSystems::Damping)
    );
}

#[derive(Component, Debug, Clone, Copy)]
pub struct ModuleJointDamping {
    pub distance: f64,
    pub angular: f64,
}
fn module_joint_damping<T: Component + EntityConstraint<2>>(
    bodies: Query<(&mut SolverBody, &SolverBodyInertia, &Transform)>,
    joints: Query<(&T, &ModuleJointDamping)>,
    time: Res<Time>,
) {
    let delta_secs = time.delta_secs();

    let mut dummy_body1 = SolverBody::DUMMY;
    let mut dummy_body2 = SolverBody::DUMMY;

    for (joint, damping) in &joints {
        let entities = joint.entities();

        let (mut body1, mut inertia1, mut transform1) = (&mut dummy_body1, &SolverBodyInertia::DUMMY, Transform::default());
        let (mut body2, mut inertia2, mut transform2) = (&mut dummy_body2, &SolverBodyInertia::DUMMY, Transform::default());

        // Get the solver bodies for the two jointed entities.
        if let Ok((body, inertia, transform)) = unsafe { bodies.get_unchecked(entities[0]) } {
            body1 = body.into_inner();
            inertia1 = inertia;
            transform1 = *transform;
        }
        if let Ok((body, inertia, transform)) = unsafe { bodies.get_unchecked(entities[1]) } {
            body2 = body.into_inner();
            inertia2 = inertia;
            transform2 = *transform;
        }

        let delta_omega = (body2.angular_velocity - body1.angular_velocity)
            * (damping.angular * delta_secs as f64).min(1.0);

        if !body1.flags.is_kinematic() {
            body1.angular_velocity += delta_omega;
        }
        if !body2.flags.is_kinematic() {
            body2.angular_velocity -= delta_omega;
        }

        let position1 = transform1.translation.truncate().as_dvec2() + body1.delta_position;
        let position2 = transform2.translation.truncate().as_dvec2() + body2.delta_position;

        let relative_position: DVec2 = position1 - position2;

        let parallel_velocity1 = body1.linear_velocity.project_onto(relative_position.normalize());
        let parallel_velocity2 = body2.linear_velocity.project_onto(relative_position.normalize());
        
        let delta_v = (parallel_velocity2 - parallel_velocity1)
            * (damping.distance * delta_secs as f64).min(1.0);

        let w1 = inertia1.effective_inv_mass();
        let w2 = inertia2.effective_inv_mass();

        let p = delta_v * (w1 + w2).recip_or_zero();

        body1.linear_velocity += p * w1;
        body2.linear_velocity -= p * w2;
    }
}