1use super::{
17 get_joint_full_id, get_joint_sim, make_joint_id, Joint, JointSim, JointType, JointUnion,
18};
19use crate::body::get_body_full_id;
20use crate::constants::linear_slop;
21use crate::core::NULL_INDEX;
22use crate::id::JointId;
23use crate::island::link_joint;
24use crate::math_functions::{
25 clamp_float, is_valid_float, is_valid_transform, max_float, max_int, min_float, PI,
26};
27use crate::solver_set::{
28 merge_solver_sets, wake_solver_set, AWAKE_SET, DISABLED_SET, FIRST_SLEEPING_SET, STATIC_SET,
29};
30use crate::types::{
31 BodyType, DistanceJointDef, FilterJointDef, JointDef, MotorJointDef, ParallelJointDef,
32 PrismaticJointDef, RevoluteJointDef, SphericalJointDef, WeldJointDef, WheelJointDef,
33};
34use crate::world::World;
35
36pub(crate) fn destroy_contacts_between_bodies(world: &mut World, body_id_a: i32, body_id_b: i32) {
38 let (mut contact_key, other_body_id) = {
40 let body_a = &world.bodies[body_id_a as usize];
41 let body_b = &world.bodies[body_id_b as usize];
42 if body_a.contact_count < body_b.contact_count {
43 (body_a.head_contact_key, body_b.id)
44 } else {
45 (body_b.head_contact_key, body_a.id)
46 }
47 };
48
49 let wake_bodies = false;
51
52 while contact_key != NULL_INDEX {
54 let contact_id = contact_key >> 1;
55 let edge_index = contact_key & 1;
56
57 contact_key = world.contacts[contact_id as usize].edges[edge_index as usize].next_key;
58
59 let other_edge_index = edge_index ^ 1;
60 if world.contacts[contact_id as usize].edges[other_edge_index as usize].body_id
61 == other_body_id
62 {
63 crate::contact::destroy_contact(world, contact_id, wake_bodies);
66 }
67 }
68
69 world.validate_solver_sets();
70}
71
72pub(crate) fn create_joint(world: &mut World, def: &JointDef, joint_type: JointType) -> i32 {
75 debug_assert!(is_valid_transform(def.local_frame_a));
76 debug_assert!(is_valid_transform(def.local_frame_b));
77 debug_assert!(def.internal_value == crate::core::SECRET_COOKIE);
78
79 let body_id_a = get_body_full_id(world, def.body_id_a);
80 let body_id_b = get_body_full_id(world, def.body_id_b);
81 let max_set_index = max_int(
82 world.bodies[body_id_a as usize].set_index,
83 world.bodies[body_id_b as usize].set_index,
84 );
85
86 let joint_id = world.joint_id_pool.alloc_id();
88 if joint_id == world.joints.len() as i32 {
89 world.joints.push(Joint::default());
90 }
91
92 {
93 let joint = &mut world.joints[joint_id as usize];
94 joint.joint_id = joint_id;
95 joint.user_data = def.user_data;
96 joint.generation = joint.generation.wrapping_add(1);
97 joint.set_index = NULL_INDEX;
98 joint.color_index = NULL_INDEX;
99 joint.local_index = NULL_INDEX;
100 joint.island_id = NULL_INDEX;
101 joint.island_index = NULL_INDEX;
102 joint.draw_scale = def.draw_scale;
103 joint.type_ = joint_type;
104 joint.collide_connected = def.collide_connected;
105 }
106
107 {
109 let head_joint_key = world.bodies[body_id_a as usize].head_joint_key;
110 {
111 let joint = &mut world.joints[joint_id as usize];
112 joint.edges[0].body_id = body_id_a;
113 joint.edges[0].prev_key = NULL_INDEX;
114 joint.edges[0].next_key = head_joint_key;
115 }
116
117 let key_a = joint_id << 1;
118 if head_joint_key != NULL_INDEX {
119 let head_joint = &mut world.joints[(head_joint_key >> 1) as usize];
120 head_joint.edges[(head_joint_key & 1) as usize].prev_key = key_a;
121 }
122 let body_a = &mut world.bodies[body_id_a as usize];
123 body_a.head_joint_key = key_a;
124 body_a.joint_count += 1;
125 }
126
127 {
129 let head_joint_key = world.bodies[body_id_b as usize].head_joint_key;
130 {
131 let joint = &mut world.joints[joint_id as usize];
132 joint.edges[1].body_id = body_id_b;
133 joint.edges[1].prev_key = NULL_INDEX;
134 joint.edges[1].next_key = head_joint_key;
135 }
136
137 let key_b = (joint_id << 1) | 1;
138 if head_joint_key != NULL_INDEX {
139 let head_joint = &mut world.joints[(head_joint_key >> 1) as usize];
140 head_joint.edges[(head_joint_key & 1) as usize].prev_key = key_b;
141 }
142 let body_b = &mut world.bodies[body_id_b as usize];
143 body_b.head_joint_key = key_b;
144 body_b.joint_count += 1;
145 }
146
147 let set_a = world.bodies[body_id_a as usize].set_index;
148 let set_b = world.bodies[body_id_b as usize].set_index;
149 let type_a = world.bodies[body_id_a as usize].type_;
150 let type_b = world.bodies[body_id_b as usize].type_;
151
152 if set_a == DISABLED_SET || set_b == DISABLED_SET {
153 let local_index = world.solver_sets[DISABLED_SET as usize].joint_sims.len() as i32;
155 {
156 let joint = &mut world.joints[joint_id as usize];
157 joint.set_index = DISABLED_SET;
158 joint.local_index = local_index;
159 }
160
161 let joint_sim = JointSim {
162 joint_id,
163 body_id_a,
164 body_id_b,
165 type_: joint_type,
166 union_: JointUnion::empty(joint_type),
167 ..JointSim::default()
168 };
169 world.solver_sets[DISABLED_SET as usize]
170 .joint_sims
171 .push(joint_sim);
172 } else if type_a != BodyType::Dynamic && type_b != BodyType::Dynamic {
173 let local_index = world.solver_sets[STATIC_SET as usize].joint_sims.len() as i32;
175 {
176 let joint = &mut world.joints[joint_id as usize];
177 joint.set_index = STATIC_SET;
178 joint.local_index = local_index;
179 }
180
181 let joint_sim = JointSim {
182 joint_id,
183 body_id_a,
184 body_id_b,
185 type_: joint_type,
186 union_: JointUnion::empty(joint_type),
187 ..JointSim::default()
188 };
189 world.solver_sets[STATIC_SET as usize]
190 .joint_sims
191 .push(joint_sim);
192 } else if set_a == AWAKE_SET || set_b == AWAKE_SET {
193 if max_set_index >= FIRST_SLEEPING_SET {
195 wake_solver_set(world, max_set_index);
196 }
197
198 world.joints[joint_id as usize].set_index = AWAKE_SET;
199
200 let (color_index, local_index) =
201 crate::constraint_graph::create_joint_in_graph(world, joint_id);
202 {
203 let joint_sim = &mut world.constraint_graph.colors[color_index as usize].joint_sims
204 [local_index as usize];
205 joint_sim.joint_id = joint_id;
206 joint_sim.body_id_a = body_id_a;
207 joint_sim.body_id_b = body_id_b;
208 joint_sim.type_ = joint_type;
209 joint_sim.union_ = JointUnion::empty(joint_type);
210 }
211 } else {
212 debug_assert!(set_a >= FIRST_SLEEPING_SET || set_b >= FIRST_SLEEPING_SET);
214 debug_assert!(set_a != STATIC_SET || set_b != STATIC_SET);
215
216 let set_index = max_set_index;
218
219 let local_index = world.solver_sets[set_index as usize].joint_sims.len() as i32;
220 {
221 let joint = &mut world.joints[joint_id as usize];
222 joint.set_index = set_index;
223 joint.local_index = local_index;
224 }
225
226 let joint_sim = JointSim {
227 joint_id,
228 body_id_a,
229 body_id_b,
230 type_: joint_type,
231 union_: JointUnion::empty(joint_type),
232 ..JointSim::default()
233 };
234 world.solver_sets[set_index as usize]
235 .joint_sims
236 .push(joint_sim);
237
238 if set_a != set_b && set_a >= FIRST_SLEEPING_SET && set_b >= FIRST_SLEEPING_SET {
239 merge_solver_sets(world, set_a, set_b);
242 debug_assert!(
243 world.bodies[body_id_a as usize].set_index
244 == world.bodies[body_id_b as usize].set_index
245 );
246 }
247 }
248
249 debug_assert!(is_valid_float(def.force_threshold) && def.force_threshold >= 0.0);
250 debug_assert!(is_valid_float(def.torque_threshold) && def.torque_threshold >= 0.0);
251
252 {
253 let joint_sim = get_joint_sim(world, joint_id);
254 joint_sim.local_frame_a = def.local_frame_a;
255 joint_sim.local_frame_b = def.local_frame_b;
256 joint_sim.type_ = joint_type;
257 joint_sim.constraint_hertz = def.constraint_hertz;
258 joint_sim.constraint_damping_ratio = def.constraint_damping_ratio;
259 joint_sim.constraint_softness = crate::solver::Softness {
260 bias_rate: 0.0,
261 mass_scale: 1.0,
262 impulse_scale: 0.0,
263 };
264 joint_sim.force_threshold = def.force_threshold;
265 joint_sim.torque_threshold = def.torque_threshold;
266
267 debug_assert!(joint_sim.joint_id == joint_id);
268 debug_assert!(joint_sim.body_id_a == body_id_a);
269 debug_assert!(joint_sim.body_id_b == body_id_b);
270 }
271
272 if world.joints[joint_id as usize].set_index > DISABLED_SET {
273 link_joint(world, joint_id);
275 }
276
277 world.validate_solver_sets();
282
283 joint_id
284}
285
286pub fn create_filter_joint(world: &mut World, def: &FilterJointDef) -> JointId {
288 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
289 debug_assert!(!world.locked);
290 if world.locked {
291 return crate::id::NULL_JOINT_ID;
292 }
293
294 let joint_id = create_joint(world, &def.base, JointType::Filter);
295 let id = make_joint_id(world, joint_id);
296 crate::recording::capture::rec(world, |rec, wid| {
297 rec.write_create_filter_joint(wid, def, id);
298 });
299 id
300}
301
302pub fn create_distance_joint(world: &mut World, def: &DistanceJointDef) -> JointId {
304 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
305 debug_assert!(is_valid_float(def.length) && def.length > 0.0);
306 debug_assert!(def.lower_spring_force <= def.upper_spring_force);
307 debug_assert!(!world.locked);
308 if world.locked {
309 return crate::id::NULL_JOINT_ID;
310 }
311
312 let joint_id = create_joint(world, &def.base, JointType::Distance);
313
314 let joint_sim = get_joint_sim(world, joint_id);
315 let joint = joint_sim.distance_mut();
316 *joint = super::DistanceJoint::default();
317 joint.length = max_float(def.length, linear_slop());
318 joint.hertz = def.hertz;
319 joint.damping_ratio = def.damping_ratio;
320 joint.lower_spring_force = def.lower_spring_force;
321 joint.upper_spring_force = def.upper_spring_force;
322 joint.min_length = max_float(def.min_length, linear_slop());
323 joint.max_length = max_float(def.min_length, def.max_length);
324 joint.max_motor_force = def.max_motor_force;
325 joint.motor_speed = def.motor_speed;
326 joint.enable_spring = def.enable_spring;
327 joint.enable_limit = def.enable_limit;
328 joint.enable_motor = def.enable_motor;
329 joint.impulse = 0.0;
330 joint.lower_impulse = 0.0;
331 joint.upper_impulse = 0.0;
332 joint.motor_impulse = 0.0;
333
334 let id = make_joint_id(world, joint_id);
335 crate::recording::capture::rec(world, |rec, wid| {
336 rec.write_create_distance_joint(wid, def, id);
337 });
338 id
339}
340
341pub fn create_motor_joint(world: &mut World, def: &MotorJointDef) -> JointId {
343 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
344 debug_assert!(!world.locked);
345 if world.locked {
346 return crate::id::NULL_JOINT_ID;
347 }
348
349 let joint_id = create_joint(world, &def.base, JointType::Motor);
350
351 let joint_sim = get_joint_sim(world, joint_id);
352 let joint = joint_sim.motor_mut();
353 *joint = super::MotorJoint::default();
354 joint.linear_velocity = def.linear_velocity;
355 joint.max_velocity_force = def.max_velocity_force;
356 joint.angular_velocity = def.angular_velocity;
357 joint.max_velocity_torque = def.max_velocity_torque;
358 joint.linear_hertz = def.linear_hertz;
359 joint.linear_damping_ratio = def.linear_damping_ratio;
360 joint.max_spring_force = def.max_spring_force;
361 joint.angular_hertz = def.angular_hertz;
362 joint.angular_damping_ratio = def.angular_damping_ratio;
363 joint.max_spring_torque = def.max_spring_torque;
364
365 let id = make_joint_id(world, joint_id);
366 crate::recording::capture::rec(world, |rec, wid| {
367 rec.write_create_motor_joint(wid, def, id);
368 });
369 id
370}
371
372pub fn create_parallel_joint(world: &mut World, def: &ParallelJointDef) -> JointId {
374 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
375 debug_assert!(is_valid_float(def.hertz) && def.hertz >= 0.0);
376 debug_assert!(is_valid_float(def.damping_ratio) && def.damping_ratio >= 0.0);
377 debug_assert!(is_valid_float(def.max_torque) && def.max_torque >= 0.0);
378 debug_assert!(!world.locked);
379 if world.locked {
380 return crate::id::NULL_JOINT_ID;
381 }
382
383 let joint_id = create_joint(world, &def.base, JointType::Parallel);
384
385 let joint_sim = get_joint_sim(world, joint_id);
386 let joint = joint_sim.parallel_mut();
387 *joint = super::ParallelJoint::default();
388 joint.hertz = def.hertz;
389 joint.damping_ratio = def.damping_ratio;
390 joint.max_torque = def.max_torque;
391
392 let id = make_joint_id(world, joint_id);
393 crate::recording::capture::rec(world, |rec, wid| {
394 rec.write_create_parallel_joint(wid, def, id);
395 });
396 id
397}
398
399pub fn create_spherical_joint(world: &mut World, def: &SphericalJointDef) -> JointId {
401 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
402 debug_assert!((0.0..=0.99 * PI).contains(&def.cone_angle));
403 debug_assert!(crate::math_functions::is_valid_quat(def.target_rotation));
404 debug_assert!(!world.locked);
405 if world.locked {
406 return crate::id::NULL_JOINT_ID;
407 }
408
409 let joint_id = create_joint(world, &def.base, JointType::Spherical);
410
411 let joint_sim = get_joint_sim(world, joint_id);
412 let joint = joint_sim.spherical_mut();
413 *joint = super::SphericalJoint::default();
414 joint.hertz = def.hertz;
415 joint.damping_ratio = def.damping_ratio;
416 joint.target_rotation = def.target_rotation;
417 joint.cone_angle = clamp_float(def.cone_angle, 0.0, 0.5 * PI);
418
419 let lower_angle = min_float(def.lower_twist_angle, def.upper_twist_angle);
420 let upper_angle = max_float(def.lower_twist_angle, def.upper_twist_angle);
421 joint.lower_twist_angle = clamp_float(lower_angle, -0.99 * PI, 0.99 * PI);
422 joint.upper_twist_angle = clamp_float(upper_angle, -0.99 * PI, 0.99 * PI);
423
424 joint.max_motor_torque = def.max_motor_torque;
425 joint.motor_velocity = def.motor_velocity;
426 joint.enable_spring = def.enable_spring;
427 joint.enable_cone_limit = def.enable_cone_limit;
428 joint.enable_twist_limit = def.enable_twist_limit;
429 joint.enable_motor = def.enable_motor;
430
431 let id = make_joint_id(world, joint_id);
432 crate::recording::capture::rec(world, |rec, wid| {
433 rec.write_create_spherical_joint(wid, def, id);
434 });
435 id
436}
437
438pub fn create_weld_joint(world: &mut World, def: &WeldJointDef) -> JointId {
440 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
441 debug_assert!(def.angular_hertz >= 0.0);
442 debug_assert!(def.angular_damping_ratio >= 0.0);
443 debug_assert!(def.linear_hertz >= 0.0);
444 debug_assert!(def.linear_damping_ratio >= 0.0);
445 debug_assert!(!world.locked);
446 if world.locked {
447 return crate::id::NULL_JOINT_ID;
448 }
449
450 let joint_id = create_joint(world, &def.base, JointType::Weld);
451
452 let joint_sim = get_joint_sim(world, joint_id);
453 let joint = joint_sim.weld_mut();
454 *joint = super::WeldJoint::default();
455 joint.linear_hertz = def.linear_hertz;
456 joint.linear_damping_ratio = def.linear_damping_ratio;
457 joint.angular_hertz = def.angular_hertz;
458 joint.angular_damping_ratio = def.angular_damping_ratio;
459
460 let id = make_joint_id(world, joint_id);
461 crate::recording::capture::rec(world, |rec, wid| {
462 rec.write_create_weld_joint(wid, def, id);
463 });
464 id
465}
466
467pub fn create_revolute_joint(world: &mut World, def: &RevoluteJointDef) -> JointId {
469 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
470 debug_assert!(!world.locked);
471 if world.locked {
472 return crate::id::NULL_JOINT_ID;
473 }
474
475 let joint_id = create_joint(world, &def.base, JointType::Revolute);
476
477 let joint_sim = get_joint_sim(world, joint_id);
478 let joint = joint_sim.revolute_mut();
479 *joint = super::RevoluteJoint::default();
480 joint.hertz = def.hertz;
481 joint.damping_ratio = def.damping_ratio;
482 joint.target_angle = clamp_float(def.target_angle, -PI, PI);
483
484 let lower_angle = min_float(def.lower_angle, def.upper_angle);
485 let upper_angle = max_float(def.lower_angle, def.upper_angle);
486 joint.lower_angle = clamp_float(lower_angle, -0.99 * PI, 0.99 * PI);
487 joint.upper_angle = clamp_float(upper_angle, -0.99 * PI, 0.99 * PI);
488
489 joint.max_motor_torque = def.max_motor_torque;
490 joint.motor_speed = def.motor_speed;
491 joint.enable_spring = def.enable_spring;
492 joint.enable_limit = def.enable_limit;
493 joint.enable_motor = def.enable_motor;
494
495 let id = make_joint_id(world, joint_id);
496 crate::recording::capture::rec(world, |rec, wid| {
497 rec.write_create_revolute_joint(wid, def, id);
498 });
499 id
500}
501
502pub fn create_prismatic_joint(world: &mut World, def: &PrismaticJointDef) -> JointId {
504 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
505 debug_assert!(def.lower_translation <= def.upper_translation);
506 debug_assert!(!world.locked);
507 if world.locked {
508 return crate::id::NULL_JOINT_ID;
509 }
510
511 let joint_id = create_joint(world, &def.base, JointType::Prismatic);
512
513 let joint_sim = get_joint_sim(world, joint_id);
514 let joint = joint_sim.prismatic_mut();
515 *joint = super::PrismaticJoint::default();
516 joint.hertz = def.hertz;
517 joint.damping_ratio = def.damping_ratio;
518 joint.target_translation = def.target_translation;
519 joint.lower_translation = def.lower_translation;
520 joint.upper_translation = def.upper_translation;
521 joint.max_motor_force = def.max_motor_force;
522 joint.motor_speed = def.motor_speed;
523 joint.enable_spring = def.enable_spring;
524 joint.enable_limit = def.enable_limit;
525 joint.enable_motor = def.enable_motor;
526
527 let id = make_joint_id(world, joint_id);
528 crate::recording::capture::rec(world, |rec, wid| {
529 rec.write_create_prismatic_joint(wid, def, id);
530 });
531 id
532}
533
534pub fn create_wheel_joint(world: &mut World, def: &WheelJointDef) -> JointId {
536 debug_assert!(def.base.internal_value == crate::core::SECRET_COOKIE);
537 debug_assert!(def.lower_suspension_limit <= def.upper_suspension_limit);
538 debug_assert!(!world.locked);
539 if world.locked {
540 return crate::id::NULL_JOINT_ID;
541 }
542
543 let joint_id = create_joint(world, &def.base, JointType::Wheel);
544
545 let joint_sim = get_joint_sim(world, joint_id);
546 let joint = joint_sim.wheel_mut();
547 *joint = super::WheelJoint::default();
548 joint.enable_suspension_spring = def.enable_suspension_spring;
549 joint.suspension_hertz = def.suspension_hertz;
550 joint.suspension_damping_ratio = def.suspension_damping_ratio;
551 joint.enable_suspension_limit = def.enable_suspension_limit;
552 joint.lower_suspension_limit = def.lower_suspension_limit;
553 joint.upper_suspension_limit = def.upper_suspension_limit;
554 joint.enable_spin_motor = def.enable_spin_motor;
555 joint.max_spin_torque = def.max_spin_torque;
556 joint.spin_speed = def.spin_speed;
557 joint.enable_steering = def.enable_steering;
558 joint.steering_hertz = def.steering_hertz;
559 joint.steering_damping_ratio = def.steering_damping_ratio;
560 joint.target_steering_angle = def.target_steering_angle;
561 joint.max_steering_torque = def.max_steering_torque;
562 joint.enable_steering_limit = def.enable_steering_limit;
563 joint.lower_steering_limit = def.lower_steering_limit;
564 joint.upper_steering_limit = def.upper_steering_limit;
565
566 let id = make_joint_id(world, joint_id);
567 crate::recording::capture::rec(world, |rec, wid| {
568 rec.write_create_wheel_joint(wid, def, id);
569 });
570 id
571}
572
573pub fn joint_set_collide_connected(world: &mut World, joint_id: JointId, should_collide: bool) {
575 crate::recording::with_recording(world, |rec| {
576 rec.write_joint_set_collide_connected(joint_id, should_collide);
577 });
578 debug_assert!(!world.locked);
579 if world.locked {
580 return;
581 }
582
583 let id = get_joint_full_id(world, joint_id);
584 if world.joints[id as usize].collide_connected == should_collide {
585 return;
586 }
587
588 world.joints[id as usize].collide_connected = should_collide;
589
590 let body_id_a = world.joints[id as usize].edges[0].body_id;
591 let body_id_b = world.joints[id as usize].edges[1].body_id;
592
593 if should_collide {
594 let (shape_count_a, shape_count_b) = {
597 let body_a = &world.bodies[body_id_a as usize];
598 let body_b = &world.bodies[body_id_b as usize];
599 (body_a.shape_count, body_b.shape_count)
600 };
601
602 let mut shape_id = if shape_count_a < shape_count_b {
603 world.bodies[body_id_a as usize].head_shape_id
604 } else {
605 world.bodies[body_id_b as usize].head_shape_id
606 };
607
608 while shape_id != NULL_INDEX {
609 let proxy_key = world.shapes[shape_id as usize].proxy_key;
610 let next = world.shapes[shape_id as usize].next_shape_id;
611 if proxy_key != NULL_INDEX {
612 world.broad_phase.buffer_move(proxy_key);
613 }
614 shape_id = next;
615 }
616 } else {
617 destroy_contacts_between_bodies(world, body_id_a, body_id_b);
618 }
619}
620
621pub fn joint_get_collide_connected(world: &World, joint_id: JointId) -> bool {
623 let id = get_joint_full_id(world, joint_id);
624 world.joints[id as usize].collide_connected
625}
626
627pub fn joint_get_type(world: &World, joint_id: JointId) -> JointType {
629 let id = get_joint_full_id(world, joint_id);
630 world.joints[id as usize].type_
631}
632
633pub fn joint_get_body_a(world: &World, joint_id: JointId) -> crate::id::BodyId {
635 let id = get_joint_full_id(world, joint_id);
636 let body_index = world.joints[id as usize].edges[0].body_id;
637 crate::body::make_body_id(world, body_index)
638}
639
640pub fn joint_get_body_b(world: &World, joint_id: JointId) -> crate::id::BodyId {
642 let id = get_joint_full_id(world, joint_id);
643 let body_index = world.joints[id as usize].edges[1].body_id;
644 crate::body::make_body_id(world, body_index)
645}
646
647#[cfg(test)]
648#[path = "lifecycle_tests.rs"]
649mod tests;