1use super::{get_joint_sim_check_type, get_joint_sim_check_type_ref, JointSim, JointType};
13use crate::body::{body_flags, BodyState, IDENTITY_BODY_STATE};
14use crate::core::NULL_INDEX;
15use crate::id::JointId;
16use crate::math_functions::{
17 add, add_mm, blend2, cross, det, dot, dot_quat, inv_mul_quat, length2, length_squared2, mul_mv,
18 mul_quat, mul_sv, negate_quat, rotate_vector, solve2, sub, sub2, Mat2, Vec2, Vec3, VEC3_AXIS_X,
19 VEC3_AXIS_Y,
20};
21use crate::solver::{make_soft, StepContext};
22use crate::solver_set::AWAKE_SET;
23use crate::world::World;
24
25pub fn parallel_joint_set_spring_hertz(world: &mut World, joint_id: JointId, hertz: f32) {
27 crate::recording::with_recording(world, |rec| {
28 rec.write_parallel_joint_set_spring_hertz(joint_id, hertz);
29 });
30 debug_assert!(hertz >= 0.0);
31 get_joint_sim_check_type(world, joint_id, JointType::Parallel)
32 .parallel_mut()
33 .hertz = hertz;
34}
35
36pub fn parallel_joint_get_spring_hertz(world: &World, joint_id: JointId) -> f32 {
38 get_joint_sim_check_type_ref(world, joint_id, JointType::Parallel)
39 .parallel()
40 .hertz
41}
42
43pub fn parallel_joint_set_spring_damping_ratio(
45 world: &mut World,
46 joint_id: JointId,
47 damping_ratio: f32,
48) {
49 crate::recording::with_recording(world, |rec| {
50 rec.write_parallel_joint_set_spring_damping_ratio(joint_id, damping_ratio);
51 });
52 debug_assert!(damping_ratio >= 0.0);
53 get_joint_sim_check_type(world, joint_id, JointType::Parallel)
54 .parallel_mut()
55 .damping_ratio = damping_ratio;
56}
57
58pub fn parallel_joint_get_spring_damping_ratio(world: &World, joint_id: JointId) -> f32 {
60 get_joint_sim_check_type_ref(world, joint_id, JointType::Parallel)
61 .parallel()
62 .damping_ratio
63}
64
65pub fn parallel_joint_set_max_torque(world: &mut World, joint_id: JointId, max_force: f32) {
67 crate::recording::with_recording(world, |rec| {
68 rec.write_parallel_joint_set_max_torque(joint_id, max_force);
69 });
70 debug_assert!(max_force >= 0.0);
71 get_joint_sim_check_type(world, joint_id, JointType::Parallel)
72 .parallel_mut()
73 .max_torque = max_force;
74}
75
76pub fn parallel_joint_get_max_torque(world: &World, joint_id: JointId) -> f32 {
78 get_joint_sim_check_type_ref(world, joint_id, JointType::Parallel)
79 .parallel()
80 .max_torque
81}
82
83pub fn get_parallel_joint_torque(inv_h: f32, base: &mut JointSim) -> Vec3 {
90 let joint = base.parallel_mut();
91
92 let rel_q = inv_mul_quat(joint.quat_a, joint.quat_b);
93 joint.perp_axis_x = mul_sv(
94 0.5,
95 rotate_vector(
96 joint.quat_a,
97 add(mul_sv(rel_q.s, VEC3_AXIS_X), cross(rel_q.v, VEC3_AXIS_X)),
98 ),
99 );
100 joint.perp_axis_y = mul_sv(
101 0.5,
102 rotate_vector(
103 joint.quat_a,
104 add(mul_sv(rel_q.s, VEC3_AXIS_Y), cross(rel_q.v, VEC3_AXIS_Y)),
105 ),
106 );
107
108 let angular_impulse = blend2(
109 joint.perp_impulse.x,
110 joint.perp_axis_x,
111 joint.perp_impulse.y,
112 joint.perp_axis_y,
113 );
114 mul_sv(inv_h, angular_impulse)
115}
116
117pub fn prepare_parallel_joint(world: &World, base: &mut JointSim, context: &StepContext) {
119 debug_assert!(base.type_ == JointType::Parallel);
120
121 let id_a = base.body_id_a;
122 let id_b = base.body_id_b;
123
124 let body_a = &world.bodies[id_a as usize];
125 let body_b = &world.bodies[id_b as usize];
126
127 debug_assert!(body_a.set_index == AWAKE_SET || body_b.set_index == AWAKE_SET);
128
129 let body_sim_a =
130 &world.solver_sets[body_a.set_index as usize].body_sims[body_a.local_index as usize];
131 let body_sim_b =
132 &world.solver_sets[body_b.set_index as usize].body_sims[body_b.local_index as usize];
133
134 base.inv_mass_a = body_sim_a.inv_mass;
135 base.inv_mass_b = body_sim_b.inv_mass;
136 base.inv_i_a = body_sim_a.inv_inertia_world;
137 base.inv_i_b = body_sim_b.inv_inertia_world;
138
139 let inv_inertia_sum = add_mm(base.inv_i_a, base.inv_i_b);
140 base.fixed_rotation = det(inv_inertia_sum) < 1000.0 * f32::MIN_POSITIVE;
141
142 let local_frame_a_q = base.local_frame_a.q;
143 let local_frame_b_q = base.local_frame_b.q;
144
145 let index_a = if body_a.set_index == AWAKE_SET {
146 body_a.local_index
147 } else {
148 NULL_INDEX
149 };
150 let index_b = if body_b.set_index == AWAKE_SET {
151 body_b.local_index
152 } else {
153 NULL_INDEX
154 };
155
156 let quat_a = mul_quat(body_sim_a.transform.q, local_frame_a_q);
157 let quat_b = mul_quat(body_sim_b.transform.q, local_frame_b_q);
158 let rel_q = inv_mul_quat(quat_a, quat_b);
159
160 let perp_axis_x = mul_sv(
162 0.5,
163 rotate_vector(
164 quat_a,
165 add(mul_sv(rel_q.s, VEC3_AXIS_X), cross(rel_q.v, VEC3_AXIS_X)),
166 ),
167 );
168 let perp_axis_y = mul_sv(
169 0.5,
170 rotate_vector(
171 quat_a,
172 add(mul_sv(rel_q.s, VEC3_AXIS_Y), cross(rel_q.v, VEC3_AXIS_Y)),
173 ),
174 );
175
176 let joint = base.parallel_mut();
177 joint.index_a = index_a;
178 joint.index_b = index_b;
179 joint.quat_a = quat_a;
180 joint.quat_b = quat_b;
181 joint.perp_axis_x = perp_axis_x;
182 joint.perp_axis_y = perp_axis_y;
183
184 joint.softness = make_soft(joint.hertz, joint.damping_ratio, context.h);
185
186 if !context.enable_warm_starting {
187 joint.perp_impulse = Vec2 { x: 0.0, y: 0.0 };
188 }
189}
190
191pub fn warm_start_parallel_joint(base: &mut JointSim, states: &mut [BodyState]) {
193 debug_assert!(base.type_ == JointType::Parallel);
194
195 let i_a = base.inv_i_a;
196 let i_b = base.inv_i_b;
197
198 let joint = base.parallel_mut();
199
200 let mut state_a = if joint.index_a == NULL_INDEX {
201 IDENTITY_BODY_STATE
202 } else {
203 states[joint.index_a as usize]
204 };
205 let mut state_b = if joint.index_b == NULL_INDEX {
206 IDENTITY_BODY_STATE
207 } else {
208 states[joint.index_b as usize]
209 };
210
211 let mut w_a = state_a.angular_velocity;
212 let mut w_b = state_b.angular_velocity;
213
214 let angular_impulse = blend2(
215 joint.perp_impulse.x,
216 joint.perp_axis_x,
217 joint.perp_impulse.y,
218 joint.perp_axis_y,
219 );
220
221 w_a = sub(w_a, mul_mv(i_a, angular_impulse));
222 w_b = add(w_b, mul_mv(i_b, angular_impulse));
223
224 if state_a.flags & body_flags::DYNAMIC_FLAG != 0 {
225 state_a.angular_velocity = w_a;
226 states[joint.index_a as usize] = state_a;
227 }
228
229 if state_b.flags & body_flags::DYNAMIC_FLAG != 0 {
230 state_b.angular_velocity = w_b;
231 states[joint.index_b as usize] = state_b;
232 }
233}
234
235pub fn solve_parallel_joint(base: &mut JointSim, context: &StepContext, states: &mut [BodyState]) {
237 let i_a = base.inv_i_a;
238 let i_b = base.inv_i_b;
239 let fixed_rotation = base.fixed_rotation;
240
241 let joint = base.parallel_mut();
242
243 let mut state_a = if joint.index_a == NULL_INDEX {
244 IDENTITY_BODY_STATE
245 } else {
246 states[joint.index_a as usize]
247 };
248 let mut state_b = if joint.index_b == NULL_INDEX {
249 IDENTITY_BODY_STATE
250 } else {
251 states[joint.index_b as usize]
252 };
253
254 let mut w_a = state_a.angular_velocity;
255 let mut w_b = state_b.angular_velocity;
256
257 let quat_a = mul_quat(state_a.delta_rotation, joint.quat_a);
258 let mut quat_b = mul_quat(state_b.delta_rotation, joint.quat_b);
259
260 if dot_quat(quat_a, quat_b) < 0.0 {
261 quat_b = negate_quat(quat_b);
263 }
264
265 let rel_q = inv_mul_quat(quat_a, quat_b);
266
267 if !fixed_rotation && joint.max_torque > 0.0 {
268 let c = Vec2 {
269 x: rel_q.v.x,
270 y: rel_q.v.y,
271 };
272 let bias = Vec2 {
273 x: joint.softness.bias_rate * c.x,
274 y: joint.softness.bias_rate * c.y,
275 };
276 let mass_scale = joint.softness.mass_scale;
277 let impulse_scale = joint.softness.impulse_scale;
278
279 let perp_axis_x = mul_sv(
281 0.5,
282 rotate_vector(
283 quat_a,
284 add(mul_sv(rel_q.s, VEC3_AXIS_X), cross(rel_q.v, VEC3_AXIS_X)),
285 ),
286 );
287 let perp_axis_y = mul_sv(
288 0.5,
289 rotate_vector(
290 quat_a,
291 add(mul_sv(rel_q.s, VEC3_AXIS_Y), cross(rel_q.v, VEC3_AXIS_Y)),
292 ),
293 );
294 joint.perp_axis_x = perp_axis_x;
295 joint.perp_axis_y = perp_axis_y;
296
297 let inv_inertia_sum = add_mm(i_a, i_b);
298 let kxx = dot(perp_axis_x, mul_mv(inv_inertia_sum, perp_axis_x));
299 let kyy = dot(perp_axis_y, mul_mv(inv_inertia_sum, perp_axis_y));
300 let kxy = dot(perp_axis_x, mul_mv(inv_inertia_sum, perp_axis_y));
301
302 let k = Mat2 {
303 cx: Vec2 { x: kxx, y: kxy },
304 cy: Vec2 { x: kxy, y: kyy },
305 };
306
307 let w_rel = sub(w_b, w_a);
308 let cdot = Vec2 {
309 x: dot(w_rel, perp_axis_x),
310 y: dot(w_rel, perp_axis_y),
311 };
312
313 let max_impulse = context.h * joint.max_torque;
314 let old_impulse = joint.perp_impulse;
315 let cdot_plus_bias = Vec2 {
316 x: cdot.x + bias.x,
317 y: cdot.y + bias.y,
318 };
319 let sol = solve2(k, cdot_plus_bias);
320 let mut delta_impulse = Vec2 {
321 x: -mass_scale * sol.x - impulse_scale * old_impulse.x,
322 y: -mass_scale * sol.y - impulse_scale * old_impulse.y,
323 };
324 joint.perp_impulse = Vec2 {
325 x: old_impulse.x + delta_impulse.x,
326 y: old_impulse.y + delta_impulse.y,
327 };
328 if length_squared2(joint.perp_impulse) > max_impulse * max_impulse {
329 let s = max_impulse / length2(joint.perp_impulse);
330 joint.perp_impulse = Vec2 {
331 x: s * joint.perp_impulse.x,
332 y: s * joint.perp_impulse.y,
333 };
334 }
335
336 delta_impulse = sub2(joint.perp_impulse, old_impulse);
337
338 let angular_impulse = blend2(delta_impulse.x, perp_axis_x, delta_impulse.y, perp_axis_y);
339 w_a = sub(w_a, mul_mv(i_a, angular_impulse));
340 w_b = add(w_b, mul_mv(i_b, angular_impulse));
341 }
342
343 if state_a.flags & body_flags::DYNAMIC_FLAG != 0 {
344 state_a.angular_velocity = w_a;
345 states[joint.index_a as usize] = state_a;
346 }
347
348 if state_b.flags & body_flags::DYNAMIC_FLAG != 0 {
349 state_b.angular_velocity = w_b;
350 states[joint.index_b as usize] = state_b;
351 }
352}