Skip to main content

box3d_rust/human/
create.rs

1//! Create a human ragdoll. Port of `CreateHuman` from `shared/human.c`.
2//!
3//! SPDX-FileCopyrightText: 2026 Erin Catto
4//! SPDX-License-Identifier: MIT
5
6use super::types::{
7    BoneId, Human, BONE_COUNT, COLOR_DODGER_BLUE, COLOR_LIGHT_YELLOW, COLOR_MEDIUM_TURQUOISE,
8    COLOR_NAVAJO_WHITE, COLOR_PERU, COLOR_TAN, FILTER_JOINT_COUNT,
9};
10use crate::body::create_body;
11use crate::geometry::Capsule;
12use crate::id::{NULL_BODY_ID, NULL_JOINT_ID};
13use crate::joint::{create_filter_joint, create_revolute_joint, create_spherical_joint, JointType};
14use crate::math_functions::{
15    normalize_quat, offset_pos, Pos, Quat, Transform, Vec2, Vec3, DEG_TO_RAD,
16};
17use crate::shape::create_capsule_shape;
18use crate::types::{
19    default_body_def, default_filter_joint_def, default_revolute_joint_def, default_shape_def,
20    default_spherical_joint_def, BodyType,
21};
22use crate::world::World;
23
24/// Build a `Transform` from C aggregate initializer order `{p, {q.v, q.s}}`.
25#[inline]
26fn xf(px: f32, py: f32, pz: f32, qx: f32, qy: f32, qz: f32, qs: f32) -> Transform {
27    Transform {
28        p: Vec3 {
29            x: px,
30            y: py,
31            z: pz,
32        },
33        q: Quat {
34            v: Vec3 {
35                x: qx,
36                y: qy,
37                z: qz,
38            },
39            s: qs,
40        },
41    }
42}
43
44/// Spawn a capsule-bone ragdoll with spherical / revolute joint tree.
45/// (CreateHuman)
46pub fn create_human(
47    human: &mut Human,
48    world: &mut World,
49    position: Pos,
50    friction_torque: f32,
51    hertz: f32,
52    damping_ratio: f32,
53    group_index: i32,
54    user_data: u64,
55    colorize: bool,
56) {
57    debug_assert!(!human.is_spawned);
58
59    for i in 0..BONE_COUNT {
60        human.bones[i].body_id = NULL_BODY_ID;
61        human.bones[i].anchor_id = NULL_BODY_ID;
62        human.bones[i].joint_id = NULL_JOINT_ID;
63        human.bones[i].joint_friction = 1.0;
64        human.bones[i].parent_index = -1;
65    }
66
67    for i in 0..FILTER_JOINT_COUNT {
68        human.filter_joints[i] = NULL_JOINT_ID;
69    }
70
71    human.friction_torque = friction_torque;
72
73    let mut body_def = default_body_def();
74    body_def.type_ = BodyType::Dynamic;
75    body_def.user_data = user_data;
76
77    let mut shape_def = default_shape_def();
78    shape_def.base_material.rolling_resistance = 0.2;
79
80    let shirt_color = COLOR_MEDIUM_TURQUOISE;
81    let pant_color = COLOR_DODGER_BLUE;
82    let skin_colors = [
83        COLOR_NAVAJO_WHITE,
84        COLOR_LIGHT_YELLOW,
85        COLOR_PERU,
86        COLOR_TAN,
87    ];
88    let skin_color = skin_colors[(group_index.rem_euclid(4)) as usize];
89
90    // pelvis
91    {
92        let bone = &mut human.bones[BoneId::Pelvis as usize];
93        bone.parent_index = -1;
94        body_def.name = "pelvis".to_string();
95        bone.reference_frame = xf(0.0, 0.932087, -0.051708, 0.739169, 0.0, 0.0, 0.673520);
96        body_def.rotation = bone.reference_frame.q;
97        body_def.position = offset_pos(position, bone.reference_frame.p);
98        bone.body_id = create_body(world, &body_def);
99
100        let capsule = Capsule {
101            center1: Vec3 {
102                x: 0.07,
103                y: 0.0,
104                z: -0.08,
105            },
106            center2: Vec3 {
107                x: -0.07,
108                y: 0.0,
109                z: -0.08,
110            },
111            radius: 0.13,
112        };
113        shape_def.filter.group_index = 0;
114        shape_def.base_material.custom_color = if colorize { pant_color } else { 0 };
115        create_capsule_shape(world, bone.body_id, &shape_def, &capsule);
116    }
117
118    // spine_01
119    {
120        let bone = &mut human.bones[BoneId::Spine01 as usize];
121        bone.parent_index = BoneId::Pelvis as i32;
122        body_def.name = "spine_01".to_string();
123        bone.reference_frame = xf(0.0, 1.113505, -0.03481, 0.739973, 0.0, 0.0, 0.672637);
124        body_def.rotation = bone.reference_frame.q;
125        body_def.position = offset_pos(position, bone.reference_frame.p);
126        bone.body_id = create_body(world, &body_def);
127        body_def.type_ = BodyType::Dynamic;
128
129        let capsule = Capsule {
130            center1: Vec3 {
131                x: 0.06,
132                y: -0.0,
133                z: -0.052264,
134            },
135            center2: Vec3 {
136                x: -0.06,
137                y: 0.0,
138                z: -0.052264,
139            },
140            radius: 0.12,
141        };
142        shape_def.filter.group_index = -group_index;
143        shape_def.base_material.custom_color = if colorize { shirt_color } else { 0 };
144        create_capsule_shape(world, bone.body_id, &shape_def, &capsule);
145
146        bone.joint_type = JointType::Spherical;
147        bone.local_frame_a = xf(0.0, 0.0, -0.182204, -0.999999, 0.0, -0.0, 0.001194);
148        bone.local_frame_b = xf(0.0, 0.0, -0.007736, -1.0, 0.0, -0.0, 0.0);
149        bone.swing_limit = 25.0 * DEG_TO_RAD;
150        bone.twist_limit = Vec2 {
151            x: -15.0 * DEG_TO_RAD,
152            y: 15.0 * DEG_TO_RAD,
153        };
154    }
155
156    // spine_02
157    {
158        let bone = &mut human.bones[BoneId::Spine02 as usize];
159        bone.parent_index = BoneId::Spine01 as i32;
160        bone.reference_frame = xf(0.0, 1.194336, -0.027087, 0.703611, 0.0, 0.0, 0.710586);
161        body_def.rotation = bone.reference_frame.q;
162        body_def.position = offset_pos(position, bone.reference_frame.p);
163        bone.body_id = create_body(world, &body_def);
164
165        let capsule = Capsule {
166            center1: Vec3 {
167                x: 0.08,
168                y: -0.015133,
169                z: -0.091801,
170            },
171            center2: Vec3 {
172                x: -0.08,
173                y: -0.015133,
174                z: -0.091801,
175            },
176            radius: 0.10,
177        };
178        shape_def.filter.group_index = 0;
179        shape_def.base_material.custom_color = if colorize { shirt_color } else { 0 };
180        create_capsule_shape(world, bone.body_id, &shape_def, &capsule);
181
182        bone.joint_type = JointType::Spherical;
183        bone.local_frame_a = xf(0.0, -0.0, -0.088935, -0.998619, -0.0, 0.0, -0.052540);
184        bone.local_frame_b = xf(-0.0, 0.0, -0.008199, -1.0, 0.0, -0.0, 0.0);
185        bone.swing_limit = 25.0 * DEG_TO_RAD;
186        bone.twist_limit = Vec2 {
187            x: -15.0 * DEG_TO_RAD,
188            y: 15.0 * DEG_TO_RAD,
189        };
190    }
191
192    // spine_03
193    {
194        let bone = &mut human.bones[BoneId::Spine03 as usize];
195        bone.parent_index = BoneId::Spine02 as i32;
196        body_def.name = "spine_03".to_string();
197        bone.reference_frame = xf(
198            -0.0, 1.31043, -0.028232, 0.669856, 0.000001, -0.000001, 0.742491,
199        );
200        body_def.rotation = bone.reference_frame.q;
201        body_def.position = offset_pos(position, bone.reference_frame.p);
202        bone.body_id = create_body(world, &body_def);
203
204        let capsule = Capsule {
205            center1: Vec3 {
206                x: 0.11,
207                y: -0.039753,
208                z: -0.13,
209            },
210            center2: Vec3 {
211                x: -0.11,
212                y: -0.039753,
213                z: -0.13,
214            },
215            radius: 0.145,
216        };
217        shape_def.filter.group_index = 0;
218        shape_def.base_material.custom_color = if colorize { shirt_color } else { 0 };
219        create_capsule_shape(world, bone.body_id, &shape_def, &capsule);
220
221        bone.joint_type = JointType::Spherical;
222        bone.local_frame_a = xf(
223            -0.0, 0.0, -0.124298, -0.998921, 0.000001, -0.000001, -0.046434,
224        );
225        bone.local_frame_b = xf(0.0, 0.0, 0.0, -1.0, 0.0, -0.000001, 0.0);
226        bone.swing_limit = 15.0 * DEG_TO_RAD;
227        bone.twist_limit = Vec2 {
228            x: -10.0 * DEG_TO_RAD,
229            y: 10.0 * DEG_TO_RAD,
230        };
231    }
232
233    // neck
234    {
235        let bone = &mut human.bones[BoneId::Neck as usize];
236        bone.parent_index = BoneId::Spine03 as i32;
237        body_def.name = "neck".to_string();
238        bone.reference_frame = xf(0.0, 1.575582, -0.055837, 0.879922, 0.0, 0.0, 0.475118);
239        body_def.rotation = bone.reference_frame.q;
240        body_def.position = offset_pos(position, bone.reference_frame.p);
241        bone.body_id = create_body(world, &body_def);
242
243        let capsule = Capsule {
244            center1: Vec3 {
245                x: -0.000001,
246                y: -0.0,
247                z: -0.02,
248            },
249            center2: Vec3 {
250                x: 0.0,
251                y: -0.005,
252                z: -0.08,
253            },
254            radius: 0.07,
255        };
256        shape_def.filter.group_index = 0;
257        shape_def.base_material.custom_color = if colorize { skin_color } else { 0 };
258        create_capsule_shape(world, bone.body_id, &shape_def, &capsule);
259
260        bone.joint_type = JointType::Spherical;
261        bone.local_frame_a = xf(
262            0.000001, -0.000259, -0.266585, -0.942192, -0.000001, 0.0, 0.335074,
263        );
264        bone.local_frame_b = xf(0.0, 0.0, 0.0, -1.0, 0.0, -0.000001, 0.0);
265        bone.swing_limit = 45.0 * DEG_TO_RAD;
266        bone.twist_limit = Vec2 {
267            x: -15.0 * DEG_TO_RAD,
268            y: 15.0 * DEG_TO_RAD,
269        };
270        bone.joint_friction = 0.8;
271    }
272
273    // head
274    {
275        let bone = &mut human.bones[BoneId::Head as usize];
276        bone.parent_index = BoneId::Neck as i32;
277        body_def.name = "head".to_string();
278        bone.reference_frame = xf(0.0, 1.653348, -0.003241, 0.750288, 0.0, 0.0, 0.661111);
279        body_def.rotation = bone.reference_frame.q;
280        body_def.position = offset_pos(position, bone.reference_frame.p);
281        bone.body_id = create_body(world, &body_def);
282
283        let capsule = Capsule {
284            center1: Vec3 {
285                x: -0.000001,
286                y: 0.016892,
287                z: -0.05869,
288            },
289            center2: Vec3 {
290                x: 0.0,
291                y: -0.003629,
292                z: -0.115072,
293            },
294            radius: 0.0975,
295        };
296        shape_def.filter.group_index = 0;
297        shape_def.base_material.custom_color = if colorize { skin_color } else { 0 };
298        create_capsule_shape(world, bone.body_id, &shape_def, &capsule);
299
300        bone.joint_type = JointType::Spherical;
301        bone.local_frame_a = xf(0.0, 0.001321, -0.093873, -0.974301, -0.0, -0.0, -0.225251);
302        bone.local_frame_b = xf(0.0, 0.001268, -0.005104, -1.0, 0.0, -0.0, 0.0);
303        bone.swing_limit = 15.0 * DEG_TO_RAD;
304        bone.twist_limit = Vec2 {
305            x: -15.0 * DEG_TO_RAD,
306            y: 15.0 * DEG_TO_RAD,
307        };
308        bone.joint_friction = 0.4;
309    }
310
311    create_legs_and_arms(
312        human,
313        world,
314        &mut body_def,
315        &mut shape_def,
316        position,
317        group_index,
318        colorize,
319        pant_color,
320        shirt_color,
321        skin_color,
322    );
323    create_bone_joints(human, world, friction_torque, hertz, damping_ratio);
324
325    let mut filter_def = default_filter_joint_def();
326    filter_def.base.body_id_a = human.bones[BoneId::ThighL as usize].body_id;
327    filter_def.base.body_id_b = human.bones[BoneId::ThighR as usize].body_id;
328    human.filter_joints[0] = create_filter_joint(world, &filter_def);
329    human.filter_joint_count = 1;
330
331    human.is_spawned = true;
332}
333
334#[allow(clippy::too_many_arguments)]
335fn create_legs_and_arms(
336    human: &mut Human,
337    world: &mut World,
338    body_def: &mut crate::types::BodyDef,
339    shape_def: &mut crate::types::ShapeDef,
340    position: Pos,
341    group_index: i32,
342    colorize: bool,
343    pant_color: u32,
344    shirt_color: u32,
345    skin_color: u32,
346) {
347    // thigh_l
348    {
349        let bone = &mut human.bones[BoneId::ThighL as usize];
350        bone.parent_index = BoneId::Pelvis as i32;
351        body_def.name = "thigh_l".to_string();
352        bone.reference_frame = xf(
353            0.090416, 0.986104, -0.035090, -0.703287, -0.070715, 0.053866, 0.705327,
354        );
355        body_def.rotation = bone.reference_frame.q;
356        body_def.position = offset_pos(position, bone.reference_frame.p);
357        bone.body_id = create_body(world, body_def);
358
359        let capsule = Capsule {
360            center1: Vec3 {
361                x: 0.023719,
362                y: 0.006008,
363                z: -0.039068,
364            },
365            center2: Vec3 {
366                x: -0.064492,
367                y: -0.004664,
368                z: -0.424718,
369            },
370            radius: 0.09,
371        };
372        shape_def.filter.group_index = -group_index;
373        shape_def.base_material.custom_color = if colorize { pant_color } else { 0 };
374        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
375
376        bone.joint_type = JointType::Spherical;
377        bone.local_frame_a = xf(
378            0.05, 0.011537, -0.055325, -0.714896, -0.022305, -0.698361, -0.026790,
379        );
380        bone.local_frame_b = xf(0.0, 0.0, 0.0, -0.002064, 0.758987, 0.017046, 0.650880);
381        bone.swing_limit = 10.0 * DEG_TO_RAD;
382        bone.twist_limit = Vec2 {
383            x: -60.0 * DEG_TO_RAD,
384            y: 40.0 * DEG_TO_RAD,
385        };
386    }
387
388    // calf_l
389    {
390        let bone = &mut human.bones[BoneId::CalfL as usize];
391        bone.parent_index = BoneId::ThighL as i32;
392        body_def.name = "calf_l".to_string();
393        bone.reference_frame = xf(
394            0.101198, 0.527027, -0.037374, -0.653328, -0.066860, 0.058582, 0.751838,
395        );
396        body_def.rotation = bone.reference_frame.q;
397        body_def.position = offset_pos(position, bone.reference_frame.p);
398        bone.body_id = create_body(world, body_def);
399
400        let capsule = Capsule {
401            center1: Vec3 {
402                x: 0.001778,
403                y: 0.0,
404                z: 0.009841,
405            },
406            center2: Vec3 {
407                x: -0.078577,
408                y: 0.014707,
409                z: -0.41816,
410            },
411            radius: 0.075,
412        };
413        shape_def.filter.group_index = 0;
414        shape_def.base_material.custom_color = if colorize { pant_color } else { 0 };
415        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
416
417        bone.joint_type = JointType::Revolute;
418        bone.local_frame_a = xf(
419            -0.069989, 0.000253, -0.453844, -0.000677, 0.760087, 0.105674, 0.641171,
420        );
421        bone.local_frame_b = xf(0.0, 0.0, 0.0, -0.044589, 0.765540, 0.053368, 0.639619);
422        bone.twist_limit = Vec2 {
423            x: -5.0 * DEG_TO_RAD,
424            y: 45.0 * DEG_TO_RAD,
425        };
426    }
427
428    // thigh_r
429    {
430        let bone = &mut human.bones[BoneId::ThighR as usize];
431        bone.parent_index = BoneId::Pelvis as i32;
432        body_def.name = "thigh_r".to_string();
433        bone.reference_frame = xf(
434            -0.090416, 0.986104, -0.03509, -0.703287, 0.070715, -0.053865, 0.705326,
435        );
436        body_def.rotation = bone.reference_frame.q;
437        body_def.position = offset_pos(position, bone.reference_frame.p);
438        bone.body_id = create_body(world, body_def);
439
440        let capsule = Capsule {
441            center1: Vec3 {
442                x: -0.023719,
443                y: 0.006008,
444                z: -0.039068,
445            },
446            center2: Vec3 {
447                x: 0.064492,
448                y: -0.004664,
449                z: -0.424718,
450            },
451            radius: 0.09,
452        };
453        shape_def.filter.group_index = -group_index;
454        shape_def.base_material.custom_color = if colorize { pant_color } else { 0 };
455        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
456
457        bone.joint_type = JointType::Spherical;
458        bone.local_frame_a = xf(
459            -0.05, 0.011537, -0.055326, -0.039089, -0.714094, 0.043177, 0.697623,
460        );
461        bone.local_frame_b = xf(0.0, 0.0, 0.0, 0.758805, -0.019886, -0.651012, -0.001759);
462        bone.swing_limit = 10.0 * DEG_TO_RAD;
463        bone.twist_limit = Vec2 {
464            x: -30.0 * DEG_TO_RAD,
465            y: 60.0 * DEG_TO_RAD,
466        };
467    }
468
469    // calf_r
470    {
471        let bone = &mut human.bones[BoneId::CalfR as usize];
472        bone.parent_index = BoneId::ThighR as i32;
473        body_def.name = "calf_r".to_string();
474        bone.reference_frame = xf(
475            -0.101198, 0.527027, -0.037373, -0.653327, 0.06686, -0.058582, 0.751839,
476        );
477        body_def.rotation = bone.reference_frame.q;
478        body_def.position = offset_pos(position, bone.reference_frame.p);
479        bone.body_id = create_body(world, body_def);
480
481        let capsule = Capsule {
482            center1: Vec3 {
483                x: -0.001820,
484                y: 0.0,
485                z: 0.010071,
486            },
487            center2: Vec3 {
488                x: 0.077883,
489                y: 0.014825,
490                z: -0.418047,
491            },
492            radius: 0.075,
493        };
494        shape_def.filter.group_index = 0;
495        shape_def.base_material.custom_color = if colorize { pant_color } else { 0 };
496        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
497
498        bone.joint_type = JointType::Revolute;
499        bone.local_frame_a = xf(
500            0.069988, 0.000253, -0.453844, 0.760086, -0.000675, -0.641171, -0.105676,
501        );
502        bone.local_frame_b = xf(0.0, 0.0, 0.0, 0.765540, -0.044589, -0.639619, -0.053368);
503        bone.twist_limit = Vec2 {
504            x: -45.0 * DEG_TO_RAD,
505            y: 5.0 * DEG_TO_RAD,
506        };
507    }
508
509    // upper_arm_l
510    {
511        let bone = &mut human.bones[BoneId::UpperArmL as usize];
512        bone.parent_index = BoneId::Spine03 as i32;
513        body_def.name = "upper_arm_l".to_string();
514        bone.reference_frame = xf(
515            0.20378, 1.484275, -0.115897, 0.143082, 0.695980, -0.690130, 0.13733,
516        );
517        body_def.rotation = bone.reference_frame.q;
518        body_def.position = offset_pos(position, bone.reference_frame.p);
519        bone.body_id = create_body(world, body_def);
520
521        let capsule = Capsule {
522            center1: Vec3 {
523                x: 0.0,
524                y: 0.0,
525                z: 0.0,
526            },
527            center2: Vec3 {
528                x: -0.091118,
529                y: 0.037775,
530                z: 0.229719,
531            },
532            radius: 0.075,
533        };
534        shape_def.filter.group_index = 0;
535        shape_def.base_material.custom_color = if colorize { shirt_color } else { 0 };
536        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
537
538        bone.joint_type = JointType::Spherical;
539        bone.local_frame_a = xf(
540            0.203780, -0.069369, -0.181921, -0.278486, 0.445600, -0.097014, 0.845266,
541        );
542        bone.local_frame_b = xf(0.0, 0.0, 0.0, -0.201396, -0.001586, 0.901850, 0.382234);
543        bone.swing_limit = 60.0 * DEG_TO_RAD;
544        bone.twist_limit = Vec2 {
545            x: -5.0 * DEG_TO_RAD,
546            y: 5.0 * DEG_TO_RAD,
547        };
548    }
549
550    // lower_arm_l
551    {
552        let bone = &mut human.bones[BoneId::LowerArmL as usize];
553        bone.parent_index = BoneId::UpperArmL as i32;
554        body_def.name = "lower_arm_l".to_string();
555        bone.reference_frame = xf(
556            0.305614, 1.242908, -0.117599, 0.165048, 0.563437, -0.802002, 0.109959,
557        );
558        body_def.rotation = bone.reference_frame.q;
559        body_def.position = offset_pos(position, bone.reference_frame.p);
560        bone.body_id = create_body(world, body_def);
561
562        let capsule = Capsule {
563            center1: Vec3 {
564                x: 0.0,
565                y: 0.0,
566                z: 0.0,
567            },
568            center2: Vec3 {
569                x: -0.142406,
570                y: 0.039392,
571                z: 0.261092,
572            },
573            radius: 0.05,
574        };
575        shape_def.filter.group_index = 0;
576        shape_def.base_material.custom_color = if colorize { skin_color } else { 0 };
577        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
578
579        bone.joint_type = JointType::Revolute;
580        bone.local_frame_a = xf(
581            -0.095482, 0.039584, 0.240723, 0.512487, -0.180629, 0.839474, 0.003742,
582        );
583        bone.local_frame_b = xf(0.0, 0.0, 0.0, 0.503803, -0.029831, 0.858168, 0.094017);
584        bone.twist_limit = Vec2 {
585            x: -5.0 * DEG_TO_RAD,
586            y: 60.0 * DEG_TO_RAD,
587        };
588    }
589
590    // upper_arm_r
591    {
592        let bone = &mut human.bones[BoneId::UpperArmR as usize];
593        bone.parent_index = BoneId::Spine03 as i32;
594        body_def.name = "upper_arm_r".to_string();
595        bone.reference_frame = xf(
596            -0.20378, 1.484276, -0.115899, 0.143083, -0.695978, 0.690132, 0.137329,
597        );
598        body_def.rotation = bone.reference_frame.q;
599        body_def.position = offset_pos(position, bone.reference_frame.p);
600        bone.body_id = create_body(world, body_def);
601
602        let capsule = Capsule {
603            center1: Vec3 {
604                x: 0.0,
605                y: 0.0,
606                z: 0.0,
607            },
608            center2: Vec3 {
609                x: 0.091118,
610                y: 0.037775,
611                z: 0.229718,
612            },
613            radius: 0.075,
614        };
615        shape_def.filter.group_index = 0;
616        shape_def.base_material.custom_color = if colorize { shirt_color } else { 0 };
617        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
618
619        bone.joint_type = JointType::Spherical;
620        bone.local_frame_a = xf(
621            -0.203779, -0.069371, -0.181922, -0.253621, -0.414842, 0.106962, 0.867261,
622        );
623        bone.local_frame_b = xf(0.0, 0.0, 0.0, -0.201397, 0.001587, -0.901850, 0.382233);
624        bone.swing_limit = 60.0 * DEG_TO_RAD;
625        bone.twist_limit = Vec2 {
626            x: -5.0 * DEG_TO_RAD,
627            y: 5.0 * DEG_TO_RAD,
628        };
629    }
630
631    // lower_arm_r
632    {
633        let bone = &mut human.bones[BoneId::LowerArmR as usize];
634        bone.parent_index = BoneId::UpperArmR as i32;
635        body_def.name = "lower_arm_r".to_string();
636        bone.reference_frame = xf(
637            -0.305614, 1.242907, -0.117599, 0.165048, -0.563437, 0.802002, 0.109959,
638        );
639        body_def.rotation = bone.reference_frame.q;
640        body_def.position = offset_pos(position, bone.reference_frame.p);
641        bone.body_id = create_body(world, body_def);
642
643        let capsule = Capsule {
644            center1: Vec3 {
645                x: 0.0,
646                y: 0.0,
647                z: 0.0,
648            },
649            center2: Vec3 {
650                x: 0.142406,
651                y: 0.039392,
652                z: 0.261092,
653            },
654            radius: 0.05,
655        };
656        shape_def.filter.group_index = 0;
657        shape_def.base_material.custom_color = if colorize { skin_color } else { 0 };
658        create_capsule_shape(world, bone.body_id, shape_def, &capsule);
659
660        bone.joint_type = JointType::Revolute;
661        bone.local_frame_a = xf(
662            0.095484, 0.039585, 0.240723, -0.180627, 0.512487, -0.003744, -0.839474,
663        );
664        bone.local_frame_b = xf(0.0, 0.0, 0.0, -0.029831, 0.503803, -0.094017, -0.858169);
665        bone.twist_limit = Vec2 {
666            x: -60.0 * DEG_TO_RAD,
667            y: 5.0 * DEG_TO_RAD,
668        };
669    }
670}
671
672fn create_bone_joints(
673    human: &mut Human,
674    world: &mut World,
675    friction_torque: f32,
676    hertz: f32,
677    damping_ratio: f32,
678) {
679    for i in 1..BONE_COUNT {
680        let parent_index = human.bones[i].parent_index as usize;
681        let body_id_a = human.bones[parent_index].body_id;
682        let body_id_b = human.bones[i].body_id;
683
684        human.bones[i].local_frame_a.q = normalize_quat(human.bones[i].local_frame_a.q);
685        human.bones[i].local_frame_b.q = normalize_quat(human.bones[i].local_frame_b.q);
686
687        let joint_type = human.bones[i].joint_type;
688        let local_frame_a = human.bones[i].local_frame_a;
689        let local_frame_b = human.bones[i].local_frame_b;
690        let twist_limit = human.bones[i].twist_limit;
691        let swing_limit = human.bones[i].swing_limit;
692        let joint_friction = human.bones[i].joint_friction;
693
694        if joint_type == JointType::Revolute {
695            let mut joint_def = default_revolute_joint_def();
696            joint_def.base.body_id_a = body_id_a;
697            joint_def.base.body_id_b = body_id_b;
698            joint_def.base.local_frame_a = local_frame_a;
699            joint_def.base.local_frame_b = local_frame_b;
700            joint_def.enable_limit = true;
701            joint_def.lower_angle = twist_limit.x;
702            joint_def.upper_angle = twist_limit.y;
703            joint_def.enable_spring = hertz > 0.0;
704            joint_def.hertz = hertz;
705            joint_def.damping_ratio = damping_ratio;
706            joint_def.enable_motor = true;
707            joint_def.max_motor_torque = joint_friction * friction_torque;
708            human.bones[i].joint_id = create_revolute_joint(world, &joint_def);
709        } else if joint_type == JointType::Spherical {
710            let mut joint_def = default_spherical_joint_def();
711            joint_def.base.body_id_a = body_id_a;
712            joint_def.base.body_id_b = body_id_b;
713            joint_def.base.local_frame_a = local_frame_a;
714            joint_def.base.local_frame_b = local_frame_b;
715            joint_def.enable_cone_limit = true;
716            joint_def.cone_angle = swing_limit;
717            joint_def.enable_twist_limit = true;
718            joint_def.lower_twist_angle = twist_limit.x;
719            joint_def.upper_twist_angle = twist_limit.y;
720            joint_def.enable_spring = hertz > 0.0;
721            joint_def.hertz = hertz;
722            joint_def.damping_ratio = damping_ratio;
723            joint_def.enable_motor = true;
724            joint_def.max_motor_torque = joint_friction * friction_torque;
725            human.bones[i].joint_id = create_spherical_joint(world, &joint_def);
726        }
727    }
728}