pub struct World {Show 63 fields
pub broad_phase: BroadPhase,
pub constraint_graph: ConstraintGraph,
pub body_id_pool: IdPool,
pub bodies: Vec<Body>,
pub solver_set_id_pool: IdPool,
pub solver_sets: Vec<SolverSet>,
pub joint_id_pool: IdPool,
pub joints: Vec<Joint>,
pub contact_id_pool: IdPool,
pub contacts: Vec<Contact>,
pub island_id_pool: IdPool,
pub islands: Vec<Island>,
pub shape_id_pool: IdPool,
pub chain_id_pool: IdPool,
pub shapes: Vec<Shape>,
pub chain_shapes: Vec<ChainShape>,
pub sensors: Vec<Sensor>,
pub task_contexts: Vec<TaskContext>,
pub sensor_task_contexts: Vec<SensorTaskContext>,
pub body_move_events: Vec<BodyMoveEvent>,
pub sensor_begin_events: Vec<SensorBeginTouchEvent>,
pub contact_begin_events: Vec<ContactBeginTouchEvent>,
pub sensor_end_events: [Vec<SensorEndTouchEvent>; 2],
pub contact_end_events: [Vec<ContactEndTouchEvent>; 2],
pub end_event_array_index: i32,
pub contact_hit_events: Vec<ContactHitEvent>,
pub joint_events: Vec<JointEvent>,
pub debug_body_set: BitSet,
pub debug_joint_set: BitSet,
pub debug_contact_set: BitSet,
pub debug_island_set: BitSet,
pub step_index: u64,
pub split_island_id: i32,
pub gravity: Vec2,
pub hit_event_threshold: f32,
pub restitution_threshold: f32,
pub max_linear_speed: f32,
pub contact_speed: f32,
pub contact_hertz: f32,
pub contact_damping_ratio: f32,
pub contact_recycle_distance: f32,
pub friction_callback: Option<FrictionCallback>,
pub restitution_callback: Option<RestitutionCallback>,
pub generation: u16,
pub profile: Profile,
pub max_capacity: Capacity,
pub pre_solve_fcn: Option<PreSolveFcn>,
pub pre_solve_context: u64,
pub custom_filter_fcn: Option<CustomFilterFcn>,
pub custom_filter_context: u64,
pub worker_count: i32,
pub user_data: u64,
pub inv_h: f32,
pub inv_dt: f32,
pub world_id: u16,
pub enable_sleep: bool,
pub locked: bool,
pub enable_warm_starting: bool,
pub enable_contact_softening: bool,
pub enable_continuous: bool,
pub enable_speculative: bool,
pub in_use: bool,
pub recording: Option<Recording>,
}Expand description
The world struct manages all physics entities, dynamic simulation, and asynchronous queries. (b2World)
Fields§
§broad_phase: BroadPhase§constraint_graph: ConstraintGraph§body_id_pool: IdPoolThe body id pool allocates and recycles body ids. Body ids provide a
stable identifier for users. Aligns with bodies.
bodies: Vec<Body>Sparse array mapping body ids to the body data stored in solver sets.
solver_set_id_pool: IdPoolProvides free list for solver sets.
solver_sets: Vec<SolverSet>Solver sets store sims in contiguous arrays. Set 0 is static, set 1 is disabled, set 2 is awake; the rest are sleeping islands.
joint_id_pool: IdPoolUsed to create stable ids for joints.
joints: Vec<Joint>Sparse array mapping joint ids to joints in the constraint graph or solver sets.
contact_id_pool: IdPoolUsed to create stable ids for contacts.
contacts: Vec<Contact>Sparse array mapping contact ids to contacts in the constraint graph or solver sets.
island_id_pool: IdPoolUsed to create stable ids for islands.
islands: Vec<Island>Persistent islands.
shape_id_pool: IdPool§chain_id_pool: IdPool§shapes: Vec<Shape>Sparse arrays that point into the pools above.
chain_shapes: Vec<ChainShape>§sensors: Vec<Sensor>Dense array of sensor data.
task_contexts: Vec<TaskContext>Per thread storage (one entry in the single-threaded port).
sensor_task_contexts: Vec<SensorTaskContext>§body_move_events: Vec<BodyMoveEvent>§sensor_begin_events: Vec<SensorBeginTouchEvent>§contact_begin_events: Vec<ContactBeginTouchEvent>§sensor_end_events: [Vec<SensorEndTouchEvent>; 2]End events are double buffered so that the user doesn’t need to flush events.
contact_end_events: [Vec<ContactEndTouchEvent>; 2]§end_event_array_index: i32§contact_hit_events: Vec<ContactHitEvent>§joint_events: Vec<JointEvent>§debug_body_set: BitSetUsed to track debug draw.
debug_joint_set: BitSet§debug_contact_set: BitSet§debug_island_set: BitSet§step_index: u64Id that is incremented every time step.
split_island_id: i32Identify islands for splitting.
gravity: Vec2§hit_event_threshold: f32§restitution_threshold: f32§max_linear_speed: f32§contact_speed: f32§contact_hertz: f32§contact_damping_ratio: f32§contact_recycle_distance: f32§friction_callback: Option<FrictionCallback>§restitution_callback: Option<RestitutionCallback>§generation: u16§profile: Profile§max_capacity: Capacity§pre_solve_fcn: Option<PreSolveFcn>§pre_solve_context: u64§custom_filter_fcn: Option<CustomFilterFcn>§custom_filter_context: u64§worker_count: i32§user_data: u64§inv_h: f32Inverse sub-step, remembered for reporting forces and torques.
inv_dt: f32Inverse full-step.
world_id: u16§enable_sleep: bool§locked: bool§enable_warm_starting: bool§enable_contact_softening: bool§enable_continuous: bool§enable_speculative: bool§in_use: bool§recording: Option<Recording>Active recording session; owned by the world between world_start_recording and world_stop_recording. (C: b2Recording*)
Implementations§
Source§impl World
impl World
Sourcepub fn validate_connectivity(&self)
pub fn validate_connectivity(&self)
(b2ValidateConnectivity — C compiles the body under B2_ENABLE_VALIDATION; here the whole check runs in debug builds only)
Source§impl World
impl World
Sourcepub fn validate_contacts(&self)
pub fn validate_contacts(&self)
(b2ValidateContacts — C compiles the body under B2_ENABLE_VALIDATION; here the whole check runs in debug builds only)
Source§impl World
impl World
Sourcepub fn new(def: &WorldDef) -> World
pub fn new(def: &WorldDef) -> World
Create a world. (b2CreateWorld)
Differences from C, all documented in the module header: there is no
global world registry (the returned World is owned; world_id stays 0
unless the embedder assigns one), no arena stack, and the serial task
path is always used (worker_count = 1 with one task context), which is
the C fallback when no task system is supplied.
Examples found in repository?
50fn main() {
51 // === Bodies demo scene ===
52 let mut wd = default_world_def();
53 wd.gravity = m::Vec2 { x: 0.0, y: -10.0 };
54 let mut world = World::new(&wd);
55
56 add_static_box(&mut world, 0.0, -0.5, 13.0, 0.5);
57 add_static_box(&mut world, -12.2, 2.0, 0.3, 2.0);
58 add_static_box(&mut world, 12.2, 2.0, 0.3, 2.0);
59
60 let mut tracked = Vec::new();
61 for i in 0..24usize {
62 let x = -6.0 + (i % 8) as f32 * 1.7 + 0.13 * (i % 3) as f32;
63 let y = 5.0 + (i / 8) as f32 * 1.6;
64 if i % 2 == 0 {
65 let hx = 0.25 + 0.2 * ((i * 7) % 3) as f32 * 0.5;
66 tracked.push(add_box(&mut world, x, y, hx, hx));
67 } else {
68 let r = 0.22 + 0.16 * ((i * 5) % 3) as f32 * 0.5;
69 tracked.push(add_circle(&mut world, x, y, r, 1.0));
70 }
71 }
72
73 for step in 0..600 {
74 world_step(&mut world, 1.0 / 60.0, 4);
75 if step % 100 == 0 {
76 let t = get_body_transform(&world, tracked[0]);
77 println!(
78 "bodies step {step}: body0 = ({:.3}, {:.3}), contacts = {}",
79 t.p.x,
80 t.p.y,
81 world.contact_id_pool.id_count()
82 );
83 }
84 }
85 println!("BODIES SCENE OK");
86
87 // === Stacking demo scene ===
88 let mut world = World::new(&wd);
89 add_static_box(&mut world, 0.0, -0.5, 11.0, 0.5);
90 let h = 0.4f32;
91 let base = 9i32;
92 for row in 0..base {
93 let count = base - row;
94 let y = h + row as f32 * 2.0 * h;
95 for i in 0..count {
96 let x = (i as f32 - (count - 1) as f32 / 2.0) * 2.05 * h;
97 add_box(&mut world, x, y, h, h);
98 }
99 }
100 for _ in 0..600 {
101 world_step(&mut world, 1.0 / 60.0, 4);
102 }
103 println!(
104 "stacking settled: awake = {}",
105 world.solver_sets[box2d_rust::solver_set::AWAKE_SET as usize]
106 .body_sims
107 .len()
108 );
109 // Drop the heavy ball
110 add_circle(&mut world, 0.3, 9.0, 0.5, 4.0);
111 for _ in 0..600 {
112 world_step(&mut world, 1.0 / 60.0, 4);
113 }
114 println!("STACKING SCENE OK");
115}More examples
82fn main() {
83 let benchmarks: [Benchmark; 10] = [
84 Benchmark {
85 name: "compounds",
86 create_fcn: scenes::create_compounds,
87 step_fcn: None,
88 total_step_count: 500,
89 },
90 Benchmark {
91 name: "joint_grid",
92 create_fcn: scenes::create_joint_grid,
93 step_fcn: None,
94 total_step_count: 500,
95 },
96 Benchmark {
97 name: "junkyard",
98 create_fcn: scenes::create_junkyard,
99 step_fcn: Some(scenes::step_junkyard),
100 total_step_count: 800,
101 },
102 Benchmark {
103 name: "large_pyramid",
104 create_fcn: scenes::create_large_pyramid,
105 step_fcn: None,
106 total_step_count: 500,
107 },
108 Benchmark {
109 name: "many_pyramids",
110 create_fcn: scenes::create_many_pyramids,
111 step_fcn: None,
112 total_step_count: 200,
113 },
114 Benchmark {
115 name: "rain",
116 create_fcn: scenes::create_rain,
117 step_fcn: Some(scenes::step_rain),
118 total_step_count: 1000,
119 },
120 Benchmark {
121 name: "smash",
122 create_fcn: scenes::create_smash,
123 step_fcn: None,
124 total_step_count: 300,
125 },
126 Benchmark {
127 name: "spinner",
128 create_fcn: scenes::create_spinner,
129 step_fcn: Some(scenes::step_spinner),
130 total_step_count: 500,
131 },
132 Benchmark {
133 name: "tumbler",
134 create_fcn: scenes::create_tumbler,
135 step_fcn: None,
136 total_step_count: 750,
137 },
138 Benchmark {
139 name: "washer",
140 create_fcn: scenes::create_washer,
141 step_fcn: None,
142 total_step_count: 500,
143 },
144 ];
145
146 let benchmark_count = benchmarks.len() as i32;
147
148 let mut max_steps = benchmarks[0].total_step_count;
149 for b in benchmarks.iter().skip(1) {
150 max_steps = max_steps.max(b.total_step_count);
151 }
152
153 // Profiles persist across all benchmarks, exactly like the C array that is
154 // allocated once before the benchmark loop and never reset.
155 let mut profiles: Vec<Profile> = vec![max_profile(); max_steps as usize];
156 let mut step_results: Vec<f32> = vec![0.0; max_steps as usize];
157
158 let mut run_count = 4;
159 let mut single_benchmark = -1;
160 let mut enable_continuous = true;
161 let mut record_step_times = false;
162
163 for arg in std::env::args().skip(1) {
164 if let Some(value) = arg.strip_prefix("-t=") {
165 // Serial port: the worker count is fixed at 1.
166 let _ = value.parse::<i32>().unwrap_or(0);
167 println!("Note: '-t' ignored; the Rust port runs a single-threaded solver");
168 } else if let Some(value) = arg.strip_prefix("-b=") {
169 single_benchmark = value.parse::<i32>().unwrap_or(0);
170 single_benchmark = clamp_int(single_benchmark, 0, benchmark_count - 1);
171 } else if let Some(value) = arg.strip_prefix("-w=") {
172 // Serial port: a single worker count is the only option.
173 let _ = value.parse::<i32>().unwrap_or(0);
174 println!("Note: '-w' ignored; the Rust port runs a single-threaded solver");
175 } else if let Some(value) = arg.strip_prefix("-r=") {
176 run_count = clamp_int(value.parse::<i32>().unwrap_or(0), 1, 1000);
177 } else if arg.starts_with("-nc") {
178 enable_continuous = false;
179 println!("Continuous disabled");
180 } else if arg.starts_with("-s") {
181 record_step_times = true;
182 } else if arg == "-h" {
183 println!(
184 "Usage\n\
185 -t=<integer>: the maximum number of threads to use (ignored, serial port)\n\
186 -b=<integer>: run a single benchmark\n\
187 -w=<integer>: run a single worker count (ignored, serial port)\n\
188 -r=<integer>: number of repeats (default is 4)\n\
189 -nc: disable continuous collision\n\
190 -s: record step times"
191 );
192 std::process::exit(0);
193 }
194 }
195
196 println!("Starting Box2D benchmarks");
197 println!("======================================");
198
199 // mirrors C's indexed loop
200 #[allow(clippy::needless_range_loop)]
201 for benchmark_index in 0..benchmark_count as usize {
202 if single_benchmark != -1 && benchmark_index as i32 != single_benchmark {
203 continue;
204 }
205
206 let benchmark = &benchmarks[benchmark_index];
207
208 // NDEBUG uses the full step count; debug builds cap at 10 like the C.
209 let step_count = if cfg!(debug_assertions) {
210 10
211 } else {
212 benchmark.total_step_count
213 };
214
215 let mut counters = box2d_rust::types::Counters::default();
216 let mut counters_acquired = false;
217
218 println!("benchmark: {}, steps = {}", benchmark.name, step_count);
219
220 // Single thread only in the serial port.
221 let mut min_time = 0.0f32;
222
223 println!("thread count: 1");
224
225 for run_index in 0..run_count {
226 let world_def = {
227 let mut wd = default_world_def();
228 wd.enable_continuous = enable_continuous;
229 wd.worker_count = 1;
230 wd
231 };
232 let mut world = World::new(&world_def);
233
234 (benchmark.create_fcn)(&mut world);
235
236 let time_step = 1.0 / 60.0;
237 let sub_step_count = 4;
238
239 // Initial step can be expensive and skew benchmark.
240 if let Some(step_fcn) = benchmark.step_fcn {
241 step_results[0] = step_fcn(&mut world, 0);
242 }
243
244 debug_assert!(step_count <= max_steps);
245
246 world_step(&mut world, time_step, sub_step_count);
247
248 let profile = world_get_profile(&world);
249 min_profile(&mut profiles[0], &profile);
250
251 let ticks = get_ticks();
252
253 for step_index in 1..step_count {
254 if let Some(step_fcn) = benchmark.step_fcn {
255 step_results[step_index as usize] = step_fcn(&mut world, step_index);
256 }
257
258 world_step(&mut world, time_step, sub_step_count);
259 let profile = world_get_profile(&world);
260 min_profile(&mut profiles[step_index as usize], &profile);
261 }
262
263 let ms = get_milliseconds(ticks);
264 println!("run {} : {} (ms)", run_index, ms);
265
266 if run_index == 0 {
267 min_time = ms;
268 } else {
269 min_time = min_float(min_time, ms);
270 }
271
272 if !counters_acquired {
273 counters = world_get_counters(&world);
274 counters_acquired = true;
275 }
276
277 // b2DestroyWorld: dropping the world frees it.
278 drop(world);
279 }
280
281 if record_step_times {
282 let file_name = format!("{}_t1.dat", benchmark.name);
283 if let Ok(mut file) = File::create(&file_name) {
284 // mirrors C's indexed loop
285 #[allow(clippy::needless_range_loop)]
286 for step_index in 0..step_count as usize {
287 let p = profiles[step_index];
288 let _ = writeln!(
289 file,
290 "{} {} {} {} {} {} {}",
291 p.step,
292 p.pairs,
293 p.collide,
294 p.constraints,
295 p.transforms,
296 p.refit,
297 p.sleep_islands
298 );
299 }
300 }
301 }
302
303 println!(
304 "body {} / shape {} / contact {} / joint {} / stack {}",
305 counters.body_count,
306 counters.shape_count,
307 counters.contact_count,
308 counters.joint_count,
309 counters.stack_used
310 );
311 print!("color counts:");
312 for c in counters.color_counts.iter() {
313 print!(" {}", c);
314 }
315 println!("\n");
316
317 let file_name = format!("{}.csv", benchmark.name);
318 if let Ok(mut file) = File::create(&file_name) {
319 let _ = writeln!(file, "threads,ms");
320 let _ = writeln!(file, "1,{}", min_time);
321 }
322 }
323
324 println!("======================================");
325 println!("All Box2D benchmarks complete!");
326}Sourcepub fn validate_solver_sets(&self)
pub fn validate_solver_sets(&self)
Validate the solver-set bookkeeping. (b2ValidateSolverSets)
bring-up: the C version (physics_world.c, compiled only with B2_ENABLE_VALIDATION) also cross-checks contacts, joints, and graph colors; those checks are added as their slices land. This subset validates the body <-> sim <-> set <-> island mapping.