Skip to main content

benchmark/human/
mod.rs

1// Port of box2d-cpp-reference/shared/human.c + human.h.
2//
3// Only CreateHuman / DestroyHuman are needed by the Rain benchmark scene, so
4// the mutation helpers (Human_SetScale, Human_ApplyRandomAngularImpulse, ...)
5// are not ported. Several Human/Bone fields exist for struct parity with the C
6// helper but are not read back by the benchmark; the module allows that dead
7// code rather than dropping fields that keep the port readable against the C.
8//
9// The large CreateHuman body lives in the `create` submodule to keep each file
10// within the project's per-file line limit.
11#![allow(dead_code)]
12
13mod create;
14pub use create::create_human;
15
16use box2d_rust::body::{body_get_local_point, destroy_body};
17use box2d_rust::id::{BodyId, JointId};
18use box2d_rust::joint::{create_revolute_joint, destroy_joint};
19use box2d_rust::math_functions::{Pos, Rot};
20use box2d_rust::types::default_revolute_joint_def;
21use box2d_rust::world::World;
22
23// (BoneId)
24pub const BONE_HIP: usize = 0;
25pub const BONE_TORSO: usize = 1;
26pub const BONE_HEAD: usize = 2;
27pub const BONE_UPPER_LEFT_LEG: usize = 3;
28pub const BONE_LOWER_LEFT_LEG: usize = 4;
29pub const BONE_UPPER_RIGHT_LEG: usize = 5;
30pub const BONE_LOWER_RIGHT_LEG: usize = 6;
31pub const BONE_UPPER_LEFT_ARM: usize = 7;
32pub const BONE_LOWER_LEFT_ARM: usize = 8;
33pub const BONE_UPPER_RIGHT_ARM: usize = 9;
34pub const BONE_LOWER_RIGHT_ARM: usize = 10;
35pub const BONE_COUNT: usize = 11;
36
37// (Bone)
38#[derive(Clone, Copy)]
39pub struct Bone {
40    pub body_id: BodyId,
41    pub joint_id: JointId,
42    pub friction_scale: f32,
43    pub parent_index: i32,
44}
45
46impl Default for Bone {
47    fn default() -> Self {
48        Bone {
49            body_id: BodyId::default(),
50            joint_id: JointId::default(),
51            friction_scale: 1.0,
52            parent_index: -1,
53        }
54    }
55}
56
57// (Human)
58#[derive(Clone)]
59pub struct Human {
60    pub bones: [Bone; BONE_COUNT],
61    pub friction_torque: f32,
62    pub original_scale: f32,
63    pub scale: f32,
64    pub is_spawned: bool,
65}
66
67impl Default for Human {
68    fn default() -> Self {
69        Human {
70            bones: [Bone::default(); BONE_COUNT],
71            friction_torque: 0.0,
72            original_scale: 0.0,
73            scale: 0.0,
74            is_spawned: false,
75        }
76    }
77}
78
79/// Shared per-bone revolute joint construction. All bones use the same frame
80/// derivation (local points on both bodies at `pivot`); the two lower arms also
81/// rotate local frame A by a quarter turn.
82#[allow(clippy::too_many_arguments)]
83pub(crate) fn attach_revolute_joint(
84    world: &mut World,
85    parent_body: BodyId,
86    child_body: BodyId,
87    pivot: Pos,
88    lower_angle: f32,
89    upper_angle: f32,
90    enable_limit: bool,
91    enable_motor: bool,
92    max_motor_torque: f32,
93    enable_spring: bool,
94    hertz: f32,
95    damping_ratio: f32,
96    draw_scale: f32,
97    frame_a_rotation: Option<Rot>,
98) -> JointId {
99    let mut joint_def = default_revolute_joint_def();
100    joint_def.base.body_id_a = parent_body;
101    joint_def.base.body_id_b = child_body;
102    joint_def.base.local_frame_a.p = body_get_local_point(world, parent_body, pivot);
103    if let Some(q) = frame_a_rotation {
104        joint_def.base.local_frame_a.q = q;
105    }
106    joint_def.base.local_frame_b.p = body_get_local_point(world, child_body, pivot);
107    joint_def.enable_limit = enable_limit;
108    joint_def.lower_angle = lower_angle;
109    joint_def.upper_angle = upper_angle;
110    joint_def.enable_motor = enable_motor;
111    joint_def.max_motor_torque = max_motor_torque;
112    joint_def.enable_spring = enable_spring;
113    joint_def.hertz = hertz;
114    joint_def.damping_ratio = damping_ratio;
115    joint_def.base.draw_scale = draw_scale;
116    create_revolute_joint(world, &joint_def)
117}
118
119/// (DestroyHuman)
120pub fn destroy_human(world: &mut World, human: &mut Human) {
121    debug_assert!(human.is_spawned);
122
123    for i in 0..BONE_COUNT {
124        if human.bones[i].joint_id.is_null() {
125            continue;
126        }
127
128        destroy_joint(world, human.bones[i].joint_id, false);
129        human.bones[i].joint_id = JointId::default();
130    }
131
132    for i in 0..BONE_COUNT {
133        if human.bones[i].body_id.is_null() {
134            continue;
135        }
136
137        destroy_body(world, human.bones[i].body_id);
138        human.bones[i].body_id = BodyId::default();
139    }
140
141    human.is_spawned = false;
142}