box3d_rust/distance/
cast.rs1use super::gjk::shape_distance;
6use super::types::{CastOutput, DistanceInput, ShapeCastPairInput, SimplexCache, Sweep};
7use crate::constants::linear_slop;
8use crate::math_functions::{
9 dot, is_normalized, lerp, max_float, mul_add, nlerp, rotate_vector, sub, Transform, VEC3_ZERO,
10};
11
12pub fn get_sweep_transform(sweep: &Sweep, time: f32) -> Transform {
14 let mut transform = Transform {
15 q: nlerp(sweep.q1, sweep.q2, time),
16 p: VEC3_ZERO,
17 };
18 transform.p = sub(
19 lerp(sweep.c1, sweep.c2, time),
20 rotate_vector(transform.q, sweep.local_center),
21 );
22 transform
23}
24
25pub(crate) fn get_final_sweep_transform(sweep: &Sweep) -> Transform {
27 let mut transform = Transform {
28 q: sweep.q2,
29 p: VEC3_ZERO,
30 };
31 transform.p = sub(sweep.c2, rotate_vector(transform.q, sweep.local_center));
32 transform
33}
34
35pub fn shape_cast(input: &ShapeCastPairInput) -> CastOutput {
42 let linear_slop = linear_slop();
44 let total_radius = input.proxy_a.radius + input.proxy_b.radius;
45 let mut target = max_float(linear_slop, total_radius - linear_slop);
46 let tolerance = 0.25 * linear_slop;
47
48 debug_assert!(target > tolerance);
49
50 let mut cache = SimplexCache::default();
52
53 let mut alpha = 0.0;
54
55 let mut distance_input = DistanceInput {
56 proxy_a: input.proxy_a,
57 proxy_b: input.proxy_b,
58 transform: input.transform,
62 use_radii: false,
63 };
64
65 let delta2 = input.translation_b;
66 let mut output = CastOutput::default();
67
68 let max_iterations = 20;
69
70 for iteration in 0..max_iterations {
71 output.iterations += 1;
72
73 let distance_output = shape_distance(&distance_input, &mut cache, None);
74
75 if distance_output.distance < target + tolerance {
76 if iteration == 0 {
77 if input.can_encroach && distance_output.distance > 2.0 * linear_slop {
78 target = distance_output.distance - linear_slop;
79 } else {
80 output.hit = true;
82
83 let c1 = mul_add(
85 distance_output.point_a,
86 input.proxy_a.radius,
87 distance_output.normal,
88 );
89 let c2 = mul_add(
90 distance_output.point_b,
91 -input.proxy_b.radius,
92 distance_output.normal,
93 );
94 output.point = lerp(c1, c2, 0.5);
95 return output;
96 }
97 } else {
98 if distance_output.distance > 0.0 && !is_normalized(distance_output.normal) {
100 return output;
102 }
103
104 output.fraction = alpha;
108 output.point = mul_add(
109 distance_output.point_a,
110 input.proxy_a.radius,
111 distance_output.normal,
112 );
113 output.normal = distance_output.normal;
114 output.hit = true;
115 return output;
116 }
117 }
118
119 debug_assert!(distance_output.distance > 0.0);
120 debug_assert!(is_normalized(distance_output.normal));
121
122 let denominator = dot(delta2, distance_output.normal);
124 if denominator >= 0.0 {
125 return output;
127 }
128
129 alpha += (target - distance_output.distance) / denominator;
131 if alpha >= input.max_fraction {
132 return output;
134 }
135
136 distance_input.transform.p = mul_add(input.transform.p, alpha, delta2);
137 }
138
139 output
141}