Skip to main content

box3d_rust/
mover.rs

1//! Character mover plane solver from box3d-cpp-reference/src/mover.c.
2//!
3//! SPDX-FileCopyrightText: 2025 Erin Catto
4//! SPDX-License-Identifier: MIT
5
6use crate::constants::linear_slop;
7use crate::geometry::{CollisionPlane, PlaneSolverResult};
8use crate::math_functions::{
9    abs_float, clamp_float, dot, min_float, mul_add, mul_sub, plane_separation, Vec3,
10};
11
12/// Iteratively solve a character mover against collected collision planes.
13/// (b3SolvePlanes)
14pub fn solve_planes(target_delta: Vec3, planes: &mut [CollisionPlane]) -> PlaneSolverResult {
15    for plane in planes.iter_mut() {
16        plane.push = 0.0;
17    }
18
19    let mut delta = target_delta;
20    let tolerance = linear_slop();
21
22    let mut iteration = 0;
23    while iteration < 20 {
24        let mut total_push = 0.0;
25        for plane in planes.iter_mut() {
26            // Add slop to prevent jitter
27            let separation = plane_separation(plane.plane, delta) + linear_slop();
28
29            let mut push = -separation;
30
31            // Clamp accumulated push
32            let accumulated_push = plane.push;
33            plane.push = clamp_float(plane.push + push, 0.0, plane.push_limit);
34            push = plane.push - accumulated_push;
35            delta = mul_add(delta, push, plane.plane.normal);
36
37            // Track maximum push for convergence
38            total_push += abs_float(push);
39        }
40
41        if total_push < tolerance {
42            break;
43        }
44
45        iteration += 1;
46    }
47
48    PlaneSolverResult {
49        delta,
50        iteration_count: iteration,
51    }
52}
53
54/// Clip a velocity (or other) vector against solved collision planes.
55/// (b3ClipVector)
56pub fn clip_vector(vector: Vec3, planes: &[CollisionPlane]) -> Vec3 {
57    let mut v = vector;
58
59    for plane in planes {
60        if plane.push == 0.0 || !plane.clip_velocity {
61            continue;
62        }
63
64        v = mul_sub(
65            v,
66            min_float(0.0, dot(v, plane.plane.normal)),
67            plane.plane.normal,
68        );
69    }
70
71    v
72}