1use crate::body::{body_flags, BodyState, IDENTITY_BODY_STATE};
14use crate::core::NULL_INDEX;
15use crate::id::JointId;
16use crate::joint::{get_joint_sim_check_type, get_joint_sim_check_type_ref, JointSim, JointType};
17use crate::math_functions::WorldTransform;
18use crate::math_functions::{
19 add, cross, cross_sv, inv_mul_rot, is_valid_float, is_valid_vec2, mul_add, mul_rot, mul_sub,
20 mul_sv, rot_get_angle, rotate_vector, solve_22, sub, sub_pos, Vec2, MAT22_ZERO, VEC2_ZERO,
21};
22use crate::solver::{make_soft, StepContext};
23use crate::solver_set::AWAKE_SET;
24use crate::world::World;
25
26pub fn weld_joint_set_linear_hertz(world: &mut World, joint_id: JointId, hertz: f32) {
28 crate::recording::record_op(world, |rec, _| {
29 crate::recording::write_joint_f32(
30 rec,
31 crate::recording::OP_WELD_SET_LINEAR_HERTZ,
32 joint_id,
33 hertz,
34 )
35 });
36 debug_assert!(is_valid_float(hertz) && hertz >= 0.0);
37 let joint = get_joint_sim_check_type(world, joint_id, JointType::Weld);
38 joint.weld_mut().linear_hertz = hertz;
39}
40
41pub fn weld_joint_get_linear_hertz(world: &World, joint_id: JointId) -> f32 {
43 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Weld);
44 joint.weld().linear_hertz
45}
46
47pub fn weld_joint_set_linear_damping_ratio(
49 world: &mut World,
50 joint_id: JointId,
51 damping_ratio: f32,
52) {
53 crate::recording::record_op(world, |rec, _| {
54 crate::recording::write_joint_f32(
55 rec,
56 crate::recording::OP_WELD_SET_LINEAR_DAMPING_RATIO,
57 joint_id,
58 damping_ratio,
59 )
60 });
61 debug_assert!(is_valid_float(damping_ratio) && damping_ratio >= 0.0);
62 let joint = get_joint_sim_check_type(world, joint_id, JointType::Weld);
63 joint.weld_mut().linear_damping_ratio = damping_ratio;
64}
65
66pub fn weld_joint_get_linear_damping_ratio(world: &World, joint_id: JointId) -> f32 {
68 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Weld);
69 joint.weld().linear_damping_ratio
70}
71
72pub fn weld_joint_set_angular_hertz(world: &mut World, joint_id: JointId, hertz: f32) {
74 crate::recording::record_op(world, |rec, _| {
75 crate::recording::write_joint_f32(
76 rec,
77 crate::recording::OP_WELD_SET_ANGULAR_HERTZ,
78 joint_id,
79 hertz,
80 )
81 });
82 debug_assert!(is_valid_float(hertz) && hertz >= 0.0);
83 let joint = get_joint_sim_check_type(world, joint_id, JointType::Weld);
84 joint.weld_mut().angular_hertz = hertz;
85}
86
87pub fn weld_joint_get_angular_hertz(world: &World, joint_id: JointId) -> f32 {
89 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Weld);
90 joint.weld().angular_hertz
91}
92
93pub fn weld_joint_set_angular_damping_ratio(
95 world: &mut World,
96 joint_id: JointId,
97 damping_ratio: f32,
98) {
99 crate::recording::record_op(world, |rec, _| {
100 crate::recording::write_joint_f32(
101 rec,
102 crate::recording::OP_WELD_SET_ANGULAR_DAMPING_RATIO,
103 joint_id,
104 damping_ratio,
105 )
106 });
107 debug_assert!(is_valid_float(damping_ratio) && damping_ratio >= 0.0);
108 let joint = get_joint_sim_check_type(world, joint_id, JointType::Weld);
109 joint.weld_mut().angular_damping_ratio = damping_ratio;
110}
111
112pub fn weld_joint_get_angular_damping_ratio(world: &World, joint_id: JointId) -> f32 {
114 let joint = get_joint_sim_check_type_ref(world, joint_id, JointType::Weld);
115 joint.weld().angular_damping_ratio
116}
117
118pub fn get_weld_joint_force(world: &World, base: &JointSim) -> Vec2 {
120 mul_sv(world.inv_h, base.weld().linear_impulse)
121}
122
123pub fn get_weld_joint_torque(world: &World, base: &JointSim) -> f32 {
125 world.inv_h * base.weld().angular_impulse
126}
127
128pub fn prepare_weld_joint(world: &World, base: &mut JointSim, context: &StepContext) {
144 debug_assert!(base.joint_type() == JointType::Weld);
145
146 let id_a = base.body_id_a;
148 let id_b = base.body_id_b;
149
150 let body_a = &world.bodies[id_a as usize];
151 let body_b = &world.bodies[id_b as usize];
152
153 debug_assert!(body_a.set_index == AWAKE_SET || body_b.set_index == AWAKE_SET);
154
155 let body_sim_a =
156 &world.solver_sets[body_a.set_index as usize].body_sims[body_a.local_index as usize];
157 let body_sim_b =
158 &world.solver_sets[body_b.set_index as usize].body_sims[body_b.local_index as usize];
159
160 let m_a = body_sim_a.inv_mass;
161 let i_a = body_sim_a.inv_inertia;
162 let m_b = body_sim_b.inv_mass;
163 let i_b = body_sim_b.inv_inertia;
164
165 base.inv_mass_a = m_a;
166 base.inv_mass_b = m_b;
167 base.inv_i_a = i_a;
168 base.inv_i_b = i_b;
169
170 let local_frame_a = base.local_frame_a;
171 let local_frame_b = base.local_frame_b;
172 let constraint_softness = base.constraint_softness;
173
174 let index_a = if body_a.set_index == AWAKE_SET {
175 body_a.local_index
176 } else {
177 NULL_INDEX
178 };
179 let index_b = if body_b.set_index == AWAKE_SET {
180 body_b.local_index
181 } else {
182 NULL_INDEX
183 };
184
185 let joint = base.weld_mut();
186 joint.index_a = index_a;
187 joint.index_b = index_b;
188
189 joint.frame_a.q = mul_rot(body_sim_a.transform.q, local_frame_a.q);
192 joint.frame_a.p = rotate_vector(
193 body_sim_a.transform.q,
194 sub(local_frame_a.p, body_sim_a.local_center),
195 );
196 joint.frame_b.q = mul_rot(body_sim_b.transform.q, local_frame_b.q);
197 joint.frame_b.p = rotate_vector(
198 body_sim_b.transform.q,
199 sub(local_frame_b.p, body_sim_b.local_center),
200 );
201
202 joint.delta_center = sub_pos(body_sim_b.center, body_sim_a.center);
205
206 let ka = i_a + i_b;
207 joint.axial_mass = if ka > 0.0 { 1.0 / ka } else { 0.0 };
208
209 if joint.linear_hertz == 0.0 {
210 joint.linear_spring = constraint_softness;
211 } else {
212 joint.linear_spring = make_soft(joint.linear_hertz, joint.linear_damping_ratio, context.h);
213 }
214
215 if joint.angular_hertz == 0.0 {
216 joint.angular_spring = constraint_softness;
217 } else {
218 joint.angular_spring =
219 make_soft(joint.angular_hertz, joint.angular_damping_ratio, context.h);
220 }
221
222 if !context.enable_warm_starting {
223 joint.linear_impulse = VEC2_ZERO;
224 joint.angular_impulse = 0.0;
225 }
226}
227
228pub fn warm_start_weld_joint(base: &mut JointSim, states: &mut [BodyState]) {
230 let m_a = base.inv_mass_a;
231 let m_b = base.inv_mass_b;
232 let i_a = base.inv_i_a;
233 let i_b = base.inv_i_b;
234
235 let joint = base.weld_mut();
236
237 let mut state_a = if joint.index_a == NULL_INDEX {
239 IDENTITY_BODY_STATE
240 } else {
241 states[joint.index_a as usize]
242 };
243 let mut state_b = if joint.index_b == NULL_INDEX {
244 IDENTITY_BODY_STATE
245 } else {
246 states[joint.index_b as usize]
247 };
248
249 let r_a = rotate_vector(state_a.delta_rotation, joint.frame_a.p);
250 let r_b = rotate_vector(state_b.delta_rotation, joint.frame_b.p);
251
252 if state_a.flags & body_flags::DYNAMIC_FLAG != 0 {
253 state_a.linear_velocity = mul_sub(state_a.linear_velocity, m_a, joint.linear_impulse);
254 state_a.angular_velocity -=
255 i_a * (cross(r_a, joint.linear_impulse) + joint.angular_impulse);
256 states[joint.index_a as usize] = state_a;
257 }
258
259 if state_b.flags & body_flags::DYNAMIC_FLAG != 0 {
260 state_b.linear_velocity = mul_add(state_b.linear_velocity, m_b, joint.linear_impulse);
261 state_b.angular_velocity +=
262 i_b * (cross(r_b, joint.linear_impulse) + joint.angular_impulse);
263 states[joint.index_b as usize] = state_b;
264 }
265}
266
267pub fn solve_weld_joint(
269 base: &mut JointSim,
270 _context: &StepContext,
271 states: &mut [BodyState],
272 use_bias: bool,
273) {
274 debug_assert!(base.joint_type() == JointType::Weld);
275
276 let m_a = base.inv_mass_a;
277 let m_b = base.inv_mass_b;
278 let i_a = base.inv_i_a;
279 let i_b = base.inv_i_b;
280
281 let joint = base.weld_mut();
282
283 let mut state_a = if joint.index_a == NULL_INDEX {
285 IDENTITY_BODY_STATE
286 } else {
287 states[joint.index_a as usize]
288 };
289 let mut state_b = if joint.index_b == NULL_INDEX {
290 IDENTITY_BODY_STATE
291 } else {
292 states[joint.index_b as usize]
293 };
294
295 let mut v_a = state_a.linear_velocity;
296 let mut w_a = state_a.angular_velocity;
297 let mut v_b = state_b.linear_velocity;
298 let mut w_b = state_b.angular_velocity;
299
300 {
302 let q_a = mul_rot(state_a.delta_rotation, joint.frame_a.q);
303 let q_b = mul_rot(state_b.delta_rotation, joint.frame_b.q);
304 let rel_q = inv_mul_rot(q_a, q_b);
305 let joint_angle = rot_get_angle(rel_q);
306
307 let mut bias = 0.0;
308 let mut mass_scale = 1.0;
309 let mut impulse_scale = 0.0;
310 if use_bias || joint.angular_hertz > 0.0 {
311 let c = joint_angle;
312 bias = joint.angular_spring.bias_rate * c;
313 mass_scale = joint.angular_spring.mass_scale;
314 impulse_scale = joint.angular_spring.impulse_scale;
315 }
316
317 let c_dot = w_b - w_a;
318 let impulse =
319 -mass_scale * joint.axial_mass * (c_dot + bias) - impulse_scale * joint.angular_impulse;
320 joint.angular_impulse += impulse;
321
322 w_a -= i_a * impulse;
323 w_b += i_b * impulse;
324 }
325
326 {
328 let r_a = rotate_vector(state_a.delta_rotation, joint.frame_a.p);
329 let r_b = rotate_vector(state_b.delta_rotation, joint.frame_b.p);
330
331 let mut bias = VEC2_ZERO;
332 let mut mass_scale = 1.0;
333 let mut impulse_scale = 0.0;
334 if use_bias || joint.linear_hertz > 0.0 {
335 let dc_a = state_a.delta_position;
336 let dc_b = state_b.delta_position;
337 let c = add(add(sub(dc_b, dc_a), sub(r_b, r_a)), joint.delta_center);
338
339 bias = mul_sv(joint.linear_spring.bias_rate, c);
340 mass_scale = joint.linear_spring.mass_scale;
341 impulse_scale = joint.linear_spring.impulse_scale;
342 }
343
344 let c_dot = sub(add(v_b, cross_sv(w_b, r_b)), add(v_a, cross_sv(w_a, r_a)));
345
346 let mut k = MAT22_ZERO;
347 k.cx.x = m_a + m_b + r_a.y * r_a.y * i_a + r_b.y * r_b.y * i_b;
348 k.cy.x = -r_a.y * r_a.x * i_a - r_b.y * r_b.x * i_b;
349 k.cx.y = k.cy.x;
350 k.cy.y = m_a + m_b + r_a.x * r_a.x * i_a + r_b.x * r_b.x * i_b;
351 let b = solve_22(k, add(c_dot, bias));
352
353 let impulse = Vec2 {
354 x: -mass_scale * b.x - impulse_scale * joint.linear_impulse.x,
355 y: -mass_scale * b.y - impulse_scale * joint.linear_impulse.y,
356 };
357
358 joint.linear_impulse = add(joint.linear_impulse, impulse);
359
360 v_a = mul_sub(v_a, m_a, impulse);
361 w_a -= i_a * cross(r_a, impulse);
362 v_b = mul_add(v_b, m_b, impulse);
363 w_b += i_b * cross(r_b, impulse);
364 }
365
366 debug_assert!(is_valid_vec2(v_a));
367 debug_assert!(is_valid_float(w_a));
368 debug_assert!(is_valid_vec2(v_b));
369 debug_assert!(is_valid_float(w_b));
370
371 if state_a.flags & body_flags::DYNAMIC_FLAG != 0 {
372 state_a.linear_velocity = v_a;
373 state_a.angular_velocity = w_a;
374 states[joint.index_a as usize] = state_a;
375 }
376
377 if state_b.flags & body_flags::DYNAMIC_FLAG != 0 {
378 state_b.linear_velocity = v_b;
379 state_b.angular_velocity = w_b;
380 states[joint.index_b as usize] = state_b;
381 }
382}
383
384pub fn draw_weld_joint(
386 draw: &mut dyn crate::debug_draw::DebugDraw,
387 base: &JointSim,
388 transform_a: WorldTransform,
389 transform_b: WorldTransform,
390 draw_scale: f32,
391) {
392 use crate::debug_draw::HexColor;
393 use crate::geometry::make_box;
394 use crate::math_functions::offset_world_transform;
395
396 debug_assert!(base.joint_type() == JointType::Weld);
397
398 let frame_a = offset_world_transform(transform_a, base.local_frame_a);
399 let frame_b = offset_world_transform(transform_b, base.local_frame_b);
400
401 let box_ = make_box(0.25 * draw_scale, 0.125 * draw_scale);
402 draw.draw_polygon(frame_a, &box_.vertices[..4], HexColor::DARK_ORANGE);
403 draw.draw_polygon(frame_b, &box_.vertices[..4], HexColor::DARK_CYAN);
404}