box3d_rust/
determinism.rs1#![allow(clippy::unnecessary_cast)] use crate::body::{body_get_transform, create_body};
17use crate::core::{hash, HASH_INIT};
18use crate::human::{create_human, Human, BONE_COUNT};
19use crate::math_functions::{Pos, WorldTransform, VEC3_ONE};
20use crate::mesh::{create_grid_mesh, create_torus_mesh, destroy_mesh, MeshData};
21use crate::shape::create_mesh_shape;
22use crate::types::{default_body_def, default_shape_def};
23use crate::world::{world_get_awake_body_count, world_get_body_events, World};
24
25pub const RAGDOLL_GROUP_SIZE: usize = 2;
27
28pub const RAGDOLL_GRID_COUNT: usize = 2;
30
31const GRID_SIZE: f32 = 15.0;
32
33#[derive(Debug, Clone, Default)]
35pub struct RagdollGroup {
36 pub humans: [Human; RAGDOLL_GROUP_SIZE],
37}
38
39#[derive(Debug)]
41pub struct FallingRagdollData {
42 pub groups: [RagdollGroup; RAGDOLL_GRID_COUNT * RAGDOLL_GRID_COUNT],
43 pub grid_mesh: Option<MeshData>,
44 pub torus_mesh: Option<MeshData>,
45 pub column_count: i32,
46 pub column_index: i32,
47 pub step_count: i32,
48 pub sleep_step: i32,
49 pub hash: u32,
50}
51
52impl Default for FallingRagdollData {
53 fn default() -> Self {
54 FallingRagdollData {
55 groups: std::array::from_fn(|_| RagdollGroup::default()),
56 grid_mesh: None,
57 torus_mesh: None,
58 column_count: 0,
59 column_index: 0,
60 step_count: 0,
61 sleep_step: 0,
62 hash: 0,
63 }
64 }
65}
66
67pub fn hash_world_transform(h: u32, xf: &WorldTransform) -> u32 {
71 let mut bytes = Vec::with_capacity(40);
72 bytes.extend_from_slice(&xf.p.x.to_le_bytes());
73 bytes.extend_from_slice(&xf.p.y.to_le_bytes());
74 bytes.extend_from_slice(&xf.p.z.to_le_bytes());
75 bytes.extend_from_slice(&xf.q.v.x.to_le_bytes());
76 bytes.extend_from_slice(&xf.q.v.y.to_le_bytes());
77 bytes.extend_from_slice(&xf.q.v.z.to_le_bytes());
78 bytes.extend_from_slice(&xf.q.s.to_le_bytes());
79 hash(h, &bytes)
80}
81
82fn create_group(
83 data: &mut FallingRagdollData,
84 world: &mut World,
85 row_index: usize,
86 column_index: usize,
87) {
88 debug_assert!(row_index < RAGDOLL_GRID_COUNT && column_index < RAGDOLL_GRID_COUNT);
89
90 let group_index = row_index * RAGDOLL_GRID_COUNT + column_index;
91
92 let span = RAGDOLL_GRID_COUNT as f32 * GRID_SIZE;
93 let group_distance = 1.0 * span / RAGDOLL_GRID_COUNT as f32;
94
95 let mut position = Pos {
96 x: (-0.5 * span + group_distance * (column_index as f32 + 0.5)) as _,
97 y: 15.0 as _,
98 z: (-0.5 * span + group_distance * (row_index as f32 + 0.5)) as _,
99 };
100
101 let friction_torque = 5.0;
102 let hertz = 1.0;
103 let damping_ratio = 0.7;
104 let colorize = false;
105
106 for i in 0..RAGDOLL_GROUP_SIZE {
107 let human = &mut data.groups[group_index].humans[i];
108 create_human(
109 human,
110 world,
111 position,
112 friction_torque,
113 hertz,
114 damping_ratio,
115 group_index as i32,
116 0,
117 colorize,
118 );
119 position.x = (position.x as f32 + 0.75) as _;
120 }
121}
122
123pub fn create_falling_ragdolls(world: &mut World) -> FallingRagdollData {
125 let mut data = FallingRagdollData::default();
126
127 let half_mesh_grid_rows = 4;
128 let mesh_grid_cell_width = GRID_SIZE / (2.0 * half_mesh_grid_rows as f32);
129 data.grid_mesh = create_grid_mesh(
130 2 * half_mesh_grid_rows,
131 2 * half_mesh_grid_rows,
132 mesh_grid_cell_width,
133 0,
134 true,
135 );
136 data.torus_mesh = create_torus_mesh(16, 16, 0.25 * GRID_SIZE, 1.0);
137
138 let span = GRID_SIZE * RAGDOLL_GRID_COUNT as f32;
139 let mut body_def = default_body_def();
140 let shape_def = default_shape_def();
141
142 let grid_mesh = data.grid_mesh.as_ref().expect("grid mesh").clone();
145 let torus_mesh = data.torus_mesh.as_ref().expect("torus mesh").clone();
146
147 body_def.position.x = (-0.5 * span + 0.5 * GRID_SIZE) as _;
148 for i in 0..RAGDOLL_GRID_COUNT {
149 body_def.position.z = (-0.5 * span + 0.5 * GRID_SIZE) as _;
150 for j in 0..RAGDOLL_GRID_COUNT {
151 let body = create_body(world, &body_def);
152 create_mesh_shape(world, body, &shape_def, &grid_mesh, VEC3_ONE);
153 create_mesh_shape(world, body, &shape_def, &torus_mesh, VEC3_ONE);
154
155 create_group(&mut data, world, i, j);
156
157 body_def.position.z = (body_def.position.z as f32 + GRID_SIZE) as _;
158 }
159
160 body_def.position.x = (body_def.position.x as f32 + GRID_SIZE) as _;
161 }
162
163 data
164}
165
166pub fn update_falling_ragdolls(world: &World, data: &mut FallingRagdollData) -> bool {
169 if data.hash == 0 {
170 let body_events = world_get_body_events(world);
171
172 if body_events.is_empty() {
173 let awake_count = world_get_awake_body_count(world);
174 debug_assert!(awake_count == 0);
175
176 data.hash = HASH_INIT;
177 for i in 0..RAGDOLL_GRID_COUNT {
178 for j in 0..RAGDOLL_GRID_COUNT {
179 for k in 0..RAGDOLL_GROUP_SIZE {
180 let group_index = i * RAGDOLL_GRID_COUNT + j;
181 let human = &data.groups[group_index].humans[k];
182
183 for b in 0..BONE_COUNT {
184 let body_id = human.bones[b].body_id;
185 let xf = body_get_transform(world, body_id);
186 data.hash = hash_world_transform(data.hash, &xf);
187 }
188 }
189 }
190 }
191
192 data.sleep_step = data.step_count;
193 }
194 }
195
196 data.step_count += 1;
197
198 data.hash != 0
199}
200
201pub fn destroy_falling_ragdolls(data: &mut FallingRagdollData) {
203 if let Some(mesh) = data.grid_mesh.take() {
204 destroy_mesh(mesh);
205 }
206 if let Some(mesh) = data.torus_mesh.take() {
207 destroy_mesh(mesh);
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214 use crate::types::default_world_def;
215
216 #[test]
217 fn create_and_destroy_falling_ragdolls() {
218 let mut world = World::new(&default_world_def());
219 let mut data = create_falling_ragdolls(&mut world);
220 assert!(data.grid_mesh.is_some());
221 assert!(data.torus_mesh.is_some());
222 assert!(!data.groups[0].humans[0].bones[0].body_id.is_null());
223 destroy_falling_ragdolls(&mut data);
224 assert!(data.grid_mesh.is_none());
225 }
226
227 #[test]
228 fn hash_world_transform_is_stable() {
229 let xf = WorldTransform {
230 p: Pos {
231 x: 1.0 as _,
232 y: 2.0 as _,
233 z: 3.0 as _,
234 },
235 q: crate::math_functions::QUAT_IDENTITY,
236 };
237 let h1 = hash_world_transform(HASH_INIT, &xf);
238 let h2 = hash_world_transform(HASH_INIT, &xf);
239 assert_eq!(h1, h2);
240 assert_ne!(h1, HASH_INIT);
241 }
242}