Skip to main content

box2d_rust/world/
draw.rs

1// Debug draw traversal for the world (b2World_Draw in physics_world.c).
2//
3// SPDX-FileCopyrightText: 2023 Erin Catto
4// SPDX-License-Identifier: MIT
5
6use crate::body::body_flags;
7use crate::collision::ShapeGeometry;
8use crate::constants::linear_slop;
9use crate::contact::{Contact, ContactSim};
10use crate::core::NULL_INDEX;
11use crate::debug_draw::{DebugDraw, HexColor};
12use crate::joint::draw_joint;
13use crate::math_functions::{
14    aabb_union, is_valid_aabb, lerp_position, mul_sv, normalize, offset_pos, right_perp, sub_pos,
15    transform_world_point, Aabb, Vec2, WorldTransform,
16};
17use crate::shape::Shape;
18use crate::solver_set::{AWAKE_SET, DISABLED_SET};
19use crate::types::{BodyType, BODY_TYPE_COUNT};
20use crate::world::World;
21
22/// (b2DrawShape)
23fn draw_shape(
24    draw: &mut dyn DebugDraw,
25    shape: &Shape,
26    transform: WorldTransform,
27    color: HexColor,
28    draw_chain_normals: bool,
29) {
30    match &shape.geometry {
31        ShapeGeometry::Capsule(capsule) => {
32            let p1 = transform_world_point(transform, capsule.center1);
33            let p2 = transform_world_point(transform, capsule.center2);
34            draw.draw_solid_capsule(p1, p2, capsule.radius, color);
35        }
36
37        ShapeGeometry::Circle(circle) => {
38            draw.draw_solid_circle(transform, circle.center, circle.radius, color);
39        }
40
41        ShapeGeometry::Polygon(poly) => {
42            draw.draw_solid_polygon(
43                transform,
44                &poly.vertices[..poly.count as usize],
45                poly.radius,
46                color,
47            );
48        }
49
50        ShapeGeometry::Segment(segment) => {
51            let p1 = transform_world_point(transform, segment.point1);
52            let p2 = transform_world_point(transform, segment.point2);
53            draw.draw_line(p1, p2, color);
54        }
55
56        ShapeGeometry::ChainSegment(chain_segment) => {
57            let segment = &chain_segment.segment;
58            let p1 = transform_world_point(transform, segment.point1);
59            let p2 = transform_world_point(transform, segment.point2);
60            draw.draw_line(p1, p2, color);
61            draw.draw_point(p2, 4.0, color);
62
63            if draw_chain_normals {
64                let c = lerp_position(p1, p2, 0.5);
65                let e = normalize(sub_pos(p2, p1));
66                let n = right_perp(e);
67                let length = 0.2 * crate::core::get_length_units_per_meter();
68                draw.draw_line(c, offset_pos(c, mul_sv(length, n)), HexColor::PALE_GREEN);
69            }
70        }
71    }
72}
73
74/// Immutable counterpart of get_contact_sim for the draw traversal.
75/// (b2GetContactSim)
76fn get_contact_sim_ref<'a>(world: &'a World, contact: &Contact) -> &'a ContactSim {
77    if contact.set_index == AWAKE_SET && contact.color_index != NULL_INDEX {
78        // contact lives in constraint graph
79        &world.constraint_graph.colors[contact.color_index as usize].contact_sims
80            [contact.local_index as usize]
81    } else {
82        &world.solver_sets[contact.set_index as usize].contact_sims[contact.local_index as usize]
83    }
84}
85
86/// The per-shape body of the C DrawQueryCallback: colors the shape by body
87/// state and marks its body for the second pass.
88fn draw_query_callback(world: &mut World, draw: &mut dyn DebugDraw, shape_id: i32) {
89    let shape = &world.shapes[shape_id as usize];
90    debug_assert!(shape.id == shape_id);
91    let body = &world.bodies[shape.body_id as usize];
92    let body_sim = &world.solver_sets[body.set_index as usize].body_sims[body.local_index as usize];
93
94    if draw.draw_shapes() {
95        let color = if shape.material.custom_color != 0 {
96            HexColor(shape.material.custom_color)
97        } else if body.type_ == BodyType::Dynamic && body.mass == 0.0 {
98            // Bad body
99            HexColor::RED
100        } else if body.set_index == DISABLED_SET {
101            HexColor::SLATE_GRAY
102        } else if shape.sensor_index != NULL_INDEX {
103            HexColor::WHEAT
104        } else if body.flags & body_flags::HAD_TIME_OF_IMPACT != 0 {
105            HexColor::LIME
106        } else if (body_sim.flags & body_flags::IS_BULLET != 0) && body.set_index == AWAKE_SET {
107            HexColor::TURQUOISE
108        } else if body.flags & body_flags::IS_SPEED_CAPPED != 0 {
109            HexColor::YELLOW
110        } else if body_sim.flags & body_flags::IS_FAST != 0 {
111            HexColor::SALMON
112        } else if body.type_ == BodyType::Static {
113            HexColor::PALE_GREEN
114        } else if body.type_ == BodyType::Kinematic {
115            HexColor::ROYAL_BLUE
116        } else if body.set_index == AWAKE_SET {
117            HexColor::PINK
118        } else {
119            HexColor::GRAY
120        };
121
122        let draw_chain_normals = draw.draw_chain_normals();
123        draw_shape(draw, shape, body_sim.transform, color, draw_chain_normals);
124    }
125
126    if draw.draw_bounds_boxes() {
127        draw.draw_bounds(shape.fat_aabb, HexColor::GOLD);
128    }
129
130    let body_id = shape.body_id;
131    world.debug_body_set.set_bit(body_id as u32);
132}
133
134/// Draw joints attached to this body that have not been drawn yet.
135fn draw_body_joints(world: &mut World, draw: &mut dyn DebugDraw, body_id: i32) {
136    let mut joint_key = world.bodies[body_id as usize].head_joint_key;
137    while joint_key != NULL_INDEX {
138        let joint_id = joint_key >> 1;
139        let edge_index = joint_key & 1;
140
141        // avoid double draw
142        if !world.debug_joint_set.get_bit(joint_id as u32) {
143            let joint = &world.joints[joint_id as usize];
144            draw_joint(draw, world, joint);
145            world.debug_joint_set.set_bit(joint_id as u32);
146        }
147
148        let joint = &world.joints[joint_id as usize];
149        joint_key = joint.edges[edge_index as usize].next_key;
150    }
151}
152
153/// Draw contact points on this dynamic body that have not been drawn yet.
154fn draw_body_contacts(world: &mut World, draw: &mut dyn DebugDraw, body_id: i32) {
155    const K_AXIS_SCALE: f32 = 0.3;
156    let speculative_color = HexColor::GAINSBORO;
157    let add_color = HexColor::GREEN;
158    let persist_color = HexColor::BLUE;
159    let normal_color = HexColor::DIM_GRAY;
160    let impulse_color = HexColor::MAGENTA;
161    let friction_color = HexColor::YELLOW;
162
163    let slop = linear_slop();
164
165    let mut contact_key = world.bodies[body_id as usize].head_contact_key;
166    while contact_key != NULL_INDEX {
167        let contact_id = contact_key >> 1;
168        let edge_index = contact_key & 1;
169        let contact = &world.contacts[contact_id as usize];
170        contact_key = contact.edges[edge_index as usize].next_key;
171
172        // avoid double draw
173        if !world.debug_contact_set.get_bit(contact_id as u32) {
174            let contact_sim = get_contact_sim_ref(world, contact);
175            let body_a = &world.bodies[contact.edges[0].body_id as usize];
176            let body_sim_a = &world.solver_sets[body_a.set_index as usize].body_sims
177                [body_a.local_index as usize];
178            let body_b = &world.bodies[contact.edges[1].body_id as usize];
179            let body_sim_b = &world.solver_sets[body_b.set_index as usize].body_sims
180                [body_b.local_index as usize];
181            let point_count = contact_sim.manifold.point_count;
182            let normal = contact_sim.manifold.normal;
183
184            for j in 0..point_count as usize {
185                let mp = &contact_sim.manifold.points[j];
186
187                let p = if draw.draw_anchor_a() {
188                    offset_pos(body_sim_a.center, mp.anchor_a)
189                } else {
190                    offset_pos(body_sim_b.center, mp.anchor_b)
191                };
192
193                if draw.draw_graph_colors() && contact.color_index != NULL_INDEX {
194                    // graph color
195                    let point_size =
196                        if contact.color_index == crate::constraint_graph::OVERFLOW_INDEX {
197                            7.5
198                        } else {
199                            5.0
200                        };
201                    draw.draw_point(
202                        p,
203                        point_size,
204                        crate::constraint_graph::get_graph_color(contact.color_index),
205                    );
206                } else if mp.separation > slop {
207                    // Speculative
208                    draw.draw_point(p, 5.0, speculative_color);
209                } else if !mp.persisted {
210                    // Add
211                    draw.draw_point(p, 10.0, add_color);
212                } else {
213                    // Persist
214                    draw.draw_point(p, 5.0, persist_color);
215                }
216
217                if draw.draw_contact_normals() {
218                    let p1 = p;
219                    let p2 = offset_pos(p1, mul_sv(K_AXIS_SCALE, normal));
220                    draw.draw_line(p1, p2, normal_color);
221
222                    let buffer = format!(" {:.2}", mp.separation);
223                    draw.draw_string(p1, &buffer, HexColor::WHITE);
224                } else if draw.draw_contact_forces() {
225                    // todo validate
226                    // multiply by one-half due to relax iteration
227                    let force = 0.5 * mp.total_normal_impulse * world.inv_dt;
228                    let p1 = p;
229                    let p2 = offset_pos(p1, mul_sv(draw.force_scale() * force, normal));
230                    draw.draw_line(p1, p2, impulse_color);
231                    let buffer = format!("{:.1}", force);
232                    draw.draw_string(p1, &buffer, HexColor::WHITE);
233                }
234
235                if draw.draw_contact_features() {
236                    let buffer = format!("{}", mp.id);
237                    draw.draw_string(p, &buffer, HexColor::ORANGE);
238                }
239
240                if draw.draw_friction_forces() {
241                    let force = 0.5 * mp.tangent_impulse * world.inv_h;
242                    let tangent = right_perp(normal);
243                    let p1 = p;
244                    let p2 = offset_pos(p1, mul_sv(draw.force_scale() * force, tangent));
245                    draw.draw_line(p1, p2, friction_color);
246                    let buffer = format!("{:.1}", force);
247                    draw.draw_string(p1, &buffer, HexColor::WHITE);
248                }
249            }
250
251            world.debug_contact_set.set_bit(contact_id as u32);
252        }
253    }
254}
255
256/// Draw the island containing this body as a bounding box, once per island.
257fn draw_body_island(world: &mut World, draw: &mut dyn DebugDraw, body_id: i32) {
258    let island_id = world.bodies[body_id as usize].island_id;
259    if island_id != NULL_INDEX && !world.debug_island_set.get_bit(island_id as u32) {
260        let island = &world.islands[island_id as usize];
261        if island.set_index == NULL_INDEX {
262            // C `continue`s here without clearing the body bit, which would
263            // never terminate; a valid islandId never has a null set index.
264            return;
265        }
266
267        let mut shape_count = 0;
268        let mut aabb = Aabb {
269            lower_bound: Vec2 {
270                x: f32::MAX,
271                y: f32::MAX,
272            },
273            upper_bound: Vec2 {
274                x: -f32::MAX,
275                y: -f32::MAX,
276            },
277        };
278
279        for body_index in 0..island.bodies.len() {
280            let island_body_id = island.bodies[body_index];
281            let island_body = &world.bodies[island_body_id as usize];
282            let mut shape_id = island_body.head_shape_id;
283            while shape_id != NULL_INDEX {
284                let shape = &world.shapes[shape_id as usize];
285                aabb = aabb_union(aabb, shape.fat_aabb);
286                shape_count += 1;
287                shape_id = shape.next_shape_id;
288            }
289        }
290
291        if shape_count > 0 {
292            draw.draw_bounds(aabb, HexColor::ORANGE_RED);
293        }
294
295        world.debug_island_set.set_bit(island_id as u32);
296    }
297}
298
299// todo this has varying order for moving shapes, causing flicker when overlapping shapes are moving
300// solution: display order by shape id modulus 3, keep 3 buckets in GLSolid* and flush in 3 passes.
301/// (b2World_Draw)
302pub fn world_draw(world: &mut World, draw: &mut dyn DebugDraw) {
303    debug_assert!(!world.locked);
304    if world.locked {
305        return;
306    }
307
308    debug_assert!(is_valid_aabb(draw.drawing_bounds()));
309
310    let body_capacity = world.body_id_pool.id_capacity();
311    world
312        .debug_body_set
313        .set_bit_count_and_clear(body_capacity as u32);
314
315    let joint_capacity = world.joint_id_pool.id_capacity();
316    world
317        .debug_joint_set
318        .set_bit_count_and_clear(joint_capacity as u32);
319
320    let contact_capacity = world.contact_id_pool.id_capacity();
321    world
322        .debug_contact_set
323        .set_bit_count_and_clear(contact_capacity as u32);
324
325    let island_capacity = world.island_id_pool.id_capacity();
326    world
327        .debug_island_set
328        .set_bit_count_and_clear(island_capacity as u32);
329
330    for i in 0..BODY_TYPE_COUNT {
331        // The C DrawQueryCallback runs during traversal; the shape ids are
332        // collected first here so the callback body can borrow the world
333        // mutably, preserving the per-tree visit order.
334        let mut shape_ids = Vec::new();
335        world.broad_phase.trees[i].query_all(draw.drawing_bounds(), |_, user_data| {
336            shape_ids.push(user_data as i32);
337            true
338        });
339
340        for shape_id in shape_ids {
341            draw_query_callback(world, draw, shape_id);
342        }
343    }
344
345    let word_count = world.debug_body_set.block_count as usize;
346    // The body-bit iteration only appends draw output, never new body bits, so
347    // a copy of the words matches C iterating the live set.
348    let words: Vec<u64> = world.debug_body_set.blocks[..word_count].to_vec();
349    for (k, word) in words.into_iter().enumerate() {
350        let mut word = word;
351        while word != 0 {
352            let ctz = word.trailing_zeros();
353            let body_id = (64 * k as u32 + ctz) as i32;
354
355            if draw.draw_body_names() && !world.bodies[body_id as usize].name.is_empty() {
356                let offset = Vec2 { x: 0.1, y: 0.1 };
357                let body = &world.bodies[body_id as usize];
358                let body_sim = &world.solver_sets[body.set_index as usize].body_sims
359                    [body.local_index as usize];
360
361                let transform = WorldTransform {
362                    p: body_sim.center,
363                    q: body_sim.transform.q,
364                };
365                let p = transform_world_point(transform, offset);
366                let name = world.bodies[body_id as usize].name.clone();
367                draw.draw_string(p, &name, HexColor::BLUE_VIOLET);
368            }
369
370            if draw.draw_mass() && world.bodies[body_id as usize].type_ == BodyType::Dynamic {
371                let offset = Vec2 { x: 0.1, y: 0.1 };
372                let body = &world.bodies[body_id as usize];
373                let body_sim = &world.solver_sets[body.set_index as usize].body_sims
374                    [body.local_index as usize];
375
376                let transform = WorldTransform {
377                    p: body_sim.center,
378                    q: body_sim.transform.q,
379                };
380                draw.draw_line(body_sim.center0, body_sim.center, HexColor::WHITE_SMOKE);
381                draw.draw_transform(transform);
382
383                let p = transform_world_point(transform, offset);
384                let buffer = format!("  {:.2}", world.bodies[body_id as usize].mass);
385                draw.draw_string(p, &buffer, HexColor::WHITE);
386            }
387
388            if draw.draw_joints() {
389                draw_body_joints(world, draw, body_id);
390            }
391
392            if draw.draw_contacts() && world.bodies[body_id as usize].type_ == BodyType::Dynamic {
393                draw_body_contacts(world, draw, body_id);
394            }
395
396            if draw.draw_islands() {
397                draw_body_island(world, draw, body_id);
398            }
399
400            // Clear the smallest set bit
401            word &= word - 1;
402        }
403    }
404}