1use super::collide::collide;
11use super::World;
12use crate::broad_phase::update_broad_phase_pairs;
13use crate::math_functions::{is_valid_float, min_float};
14use crate::sensor::overlap_sensors;
15use crate::solver::{make_soft, solve, StepContext};
16use crate::solver_set::STATIC_SET;
17use crate::timer::{get_milliseconds, get_ticks};
18use crate::types::BodyType;
19
20pub fn world_step(world: &mut World, time_step: f32, sub_step_count: i32) {
23 debug_assert!(is_valid_float(time_step));
24 debug_assert!(0 < sub_step_count);
25
26 debug_assert!(!world.locked);
27 if world.locked {
28 return;
29 }
30
31 if let Some(mut rec) = world.recording.take() {
33 let world_id = crate::id::WorldId {
34 index1: world.world_id + 1,
35 generation: world.generation,
36 };
37 crate::recording::write_step(&mut rec, world_id, time_step, sub_step_count);
38 world.recording = Some(rec);
39 }
40
41 world.body_move_events.clear();
44 world.sensor_begin_events.clear();
45 world.contact_begin_events.clear();
46 world.contact_hit_events.clear();
47 world.joint_events.clear();
48
49 world.profile = super::Profile::default();
50
51 world.locked = true;
52
53 let step_ticks = get_ticks();
54
55 {
56 let static_shape_count = world.broad_phase.trees[BodyType::Static as usize].proxy_count();
57 let dynamic_shape_count = world.broad_phase.trees[BodyType::Dynamic as usize].proxy_count();
58 let static_body_count = world.solver_sets[STATIC_SET as usize].body_sims.len() as i32;
59 let total_body_count = world.body_id_pool.id_count();
61 let total_contact_count = world.contact_id_pool.id_count();
62
63 let c = &mut world.max_capacity;
64 c.static_shape_count =
65 crate::math_functions::max_int(c.static_shape_count, static_shape_count);
66 c.dynamic_shape_count =
67 crate::math_functions::max_int(c.dynamic_shape_count, dynamic_shape_count);
68 c.static_body_count =
69 crate::math_functions::max_int(c.static_body_count, static_body_count);
70 c.dynamic_body_count = crate::math_functions::max_int(
71 c.dynamic_body_count,
72 total_body_count - static_body_count,
73 );
74 c.contact_count = crate::math_functions::max_int(c.contact_count, total_contact_count);
75 }
76
77 {
79 let pair_ticks = get_ticks();
80 update_broad_phase_pairs(world);
81 world.profile.pairs = get_milliseconds(pair_ticks);
82 }
83
84 let sub_step_count = crate::math_functions::max_int(1, sub_step_count);
85 let mut context = StepContext {
86 dt: time_step,
87 sub_step_count,
88 ..StepContext::default()
89 };
90
91 if time_step > 0.0 {
92 context.inv_dt = 1.0 / time_step;
93 context.h = time_step / sub_step_count as f32;
94 context.inv_h = sub_step_count as f32 * context.inv_dt;
95 } else {
96 context.inv_dt = 0.0;
97 context.h = 0.0;
98 context.inv_h = 0.0;
99 }
100
101 world.inv_h = context.inv_h;
102 world.inv_dt = context.inv_dt;
103
104 let contact_hertz = min_float(world.contact_hertz, 0.125 * context.inv_h);
106 context.contact_softness = make_soft(contact_hertz, world.contact_damping_ratio, context.h);
107 context.static_softness =
108 make_soft(2.0 * contact_hertz, world.contact_damping_ratio, context.h);
109
110 context.restitution_threshold = world.restitution_threshold;
111 context.max_linear_velocity = world.max_linear_speed;
112 context.contact_speed = world.contact_speed;
113 context.enable_warm_starting = world.enable_warm_starting;
114
115 {
117 let collide_ticks = get_ticks();
118 collide(world, &context);
119 world.profile.collide = get_milliseconds(collide_ticks);
120 }
121
122 if time_step > 0.0 {
125 let solve_ticks = get_ticks();
126 solve(world, &context);
127 world.profile.solve = get_milliseconds(solve_ticks);
128 }
129
130 {
132 let sensor_ticks = get_ticks();
133 overlap_sensors(world);
134 world.profile.sensors = get_milliseconds(sensor_ticks);
135 }
136
137 world.profile.step = get_milliseconds(step_ticks);
138
139 world.locked = false;
140
141 world.end_event_array_index = 1 - world.end_event_array_index;
143 world.sensor_end_events[world.end_event_array_index as usize].clear();
144 world.contact_end_events[world.end_event_array_index as usize].clear();
145
146 crate::recording::record_step_end(world);
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::body::{create_body, get_body_full_id, get_body_transform};
154 use crate::geometry::make_box;
155 use crate::math_functions::{abs_float, rot_get_angle, to_pos, Vec2};
156 use crate::shape::create_polygon_shape;
157 use crate::types::{default_body_def, default_shape_def, default_world_def};
158
159 #[test]
162 fn hello_world() {
163 let mut world_def = default_world_def();
166 world_def.gravity = Vec2 { x: 0.0, y: -10.0 };
167
168 let mut world = World::new(&world_def);
169
170 let mut ground_body_def = default_body_def();
172 ground_body_def.position = to_pos(Vec2 { x: 0.0, y: -10.0 });
173
174 let ground_id = create_body(&mut world, &ground_body_def);
175
176 let ground_box = make_box(50.0, 10.0);
179
180 let ground_shape_def = default_shape_def();
182 create_polygon_shape(&mut world, ground_id, &ground_shape_def, &ground_box);
183
184 let mut body_def = default_body_def();
187 body_def.type_ = crate::types::BodyType::Dynamic;
188 body_def.position = to_pos(Vec2 { x: 0.0, y: 4.0 });
189
190 let body_id = create_body(&mut world, &body_def);
191 let body_index = get_body_full_id(&world, body_id);
192
193 let dynamic_box = make_box(1.0, 1.0);
195
196 let mut shape_def = default_shape_def();
199 shape_def.density = 1.0;
200 shape_def.material.friction = 0.3;
201
202 create_polygon_shape(&mut world, body_id, &shape_def, &dynamic_box);
203
204 let time_step = 1.0 / 60.0;
208 let sub_step_count = 4;
209
210 for _ in 0..90 {
212 world_step(&mut world, time_step, sub_step_count);
215 }
216
217 let transform = get_body_transform(&world, body_index);
218 let position = transform.p;
219 let rotation = transform.q;
220
221 assert!(abs_float(position.x as f32) < 0.01);
222 assert!(abs_float(position.y as f32 - 1.00) < 0.01);
223 assert!(abs_float(rot_get_angle(rotation)) < 0.01);
224
225 let profile = crate::world::world_get_profile(&world);
227 assert!(profile.step > 0.0);
228 assert!(profile.pairs >= 0.0);
229 assert!(profile.collide >= 0.0);
230 assert!(profile.solve >= 0.0);
231 assert!(profile.sensors >= 0.0);
232 }
233
234 #[test]
236 fn empty_world() {
237 let world_def = default_world_def();
238 let mut world = World::new(&world_def);
239
240 let time_step = 1.0 / 60.0;
241 let sub_step_count = 1;
242
243 for _ in 0..60 {
244 world_step(&mut world, time_step, sub_step_count);
245 }
246
247 assert_eq!(world.step_index, 60);
249 }
250}