1use super::types::{Capsule, MassData, PlaneResult, RayCastInput, ShapeCastInput, Sphere};
7use crate::constants::{linear_slop, overlap_slop};
8use crate::distance::{
9 make_proxy, shape_cast, shape_distance, CastOutput, DistanceInput, ShapeCastPairInput,
10 ShapeProxy, SimplexCache,
11};
12use crate::math_functions::{
13 add, get_length_and_normalize, inv_mul_transforms, length_squared, make_diagonal_matrix, max,
14 min, mul_add, normalize, perp, point_to_segment_distance, sub, transform_point, Aabb, Plane,
15 Transform, Vec3, TRANSFORM_IDENTITY, VEC3_AXIS_Y,
16};
17
18pub fn compute_sphere_mass(shape: &Sphere, density: f32) -> MassData {
20 let center = shape.center;
21 let radius = shape.radius;
22
23 let volume = 4.0 / 3.0 * crate::math_functions::PI * radius * radius * radius;
24 let mass = volume * density;
25 let ixx = 0.4 * mass * radius * radius;
26
27 MassData {
28 mass,
29 center,
30 inertia: make_diagonal_matrix(ixx, ixx, ixx),
32 }
33}
34
35pub fn compute_sphere_aabb(shape: &Sphere, transform: Transform) -> Aabb {
37 let center = transform_point(transform, shape.center);
38 let radius = shape.radius;
39 let extent = Vec3 {
40 x: radius,
41 y: radius,
42 z: radius,
43 };
44 Aabb {
45 lower_bound: sub(center, extent),
46 upper_bound: add(center, extent),
47 }
48}
49
50pub fn compute_swept_sphere_aabb(shape: &Sphere, xf1: Transform, xf2: Transform) -> Aabb {
53 let r = Vec3 {
54 x: shape.radius,
55 y: shape.radius,
56 z: shape.radius,
57 };
58 let center1 = transform_point(xf1, shape.center);
59 let center2 = transform_point(xf2, shape.center);
60 Aabb {
61 lower_bound: sub(min(center1, center2), r),
62 upper_bound: add(max(center1, center2), r),
63 }
64}
65
66pub fn overlap_sphere(shape: &Sphere, shape_transform: Transform, proxy: &ShapeProxy) -> bool {
68 let input = DistanceInput {
69 proxy_a: make_proxy(&[shape.center], shape.radius),
70 proxy_b: *proxy,
71 transform: inv_mul_transforms(shape_transform, TRANSFORM_IDENTITY),
72 use_radii: true,
73 };
74
75 let mut cache = SimplexCache::default();
76 let output = shape_distance(&input, &mut cache, None);
77 output.distance < overlap_slop()
78}
79
80pub fn ray_cast_sphere(shape: &Sphere, input: &RayCastInput) -> CastOutput {
85 debug_assert!(super::is_valid_ray(input));
86 let mut output = CastOutput::default();
87
88 let p = shape.center;
89
90 let s = sub(input.origin, p);
92
93 let r = shape.radius;
94 let rr = r * r;
95
96 let mut length = 0.0;
97 let d = get_length_and_normalize(&mut length, input.translation);
98 if length == 0.0 {
99 if length_squared(s) < rr {
102 output.point = input.origin;
104 output.hit = true;
105 }
106
107 return output;
108 }
109
110 let t = -crate::math_functions::dot(s, d);
114
115 let c = mul_add(s, t, d);
117
118 let cc = crate::math_functions::dot(c, c);
119
120 if cc > rr {
121 return output;
123 }
124
125 let h = (rr - cc).sqrt();
127
128 let fraction = t - h;
129
130 if fraction < 0.0 || input.max_fraction * length < fraction {
131 if length_squared(s) < rr {
134 output.point = input.origin;
136 output.hit = true;
137 }
138
139 return output;
140 }
141
142 let hit_point = mul_add(s, fraction, d);
143
144 output.fraction = fraction / length;
145
146 if output.fraction > input.max_fraction {
147 output.fraction = input.max_fraction;
149 }
150
151 output.normal = normalize(hit_point);
152 output.point = mul_add(p, shape.radius, output.normal);
153 output.hit = true;
154
155 output
156}
157
158pub fn ray_cast_hollow_sphere(sphere: &Sphere, input: &RayCastInput) -> CastOutput {
164 let p = sphere.center;
165
166 let mut output = CastOutput::default();
167
168 let s = sub(input.origin, p);
170 let d = normalize(input.translation);
171
172 let t = -crate::math_functions::dot(s, d);
176
177 let c = mul_add(s, t, d);
179
180 let cc = crate::math_functions::dot(c, c);
181 let r = sphere.radius;
182 let rr = r * r;
183
184 if cc > rr {
185 return output;
187 }
188
189 let h = (rr - cc).sqrt();
191
192 let mut fraction = t - h;
193
194 if fraction < 0.0 {
195 fraction = t + h;
196 }
197
198 if fraction < 0.0 {
199 return output;
201 }
202
203 if fraction > input.max_fraction {
204 return output;
205 }
206
207 let hit_point = mul_add(s, fraction, d);
208
209 output.fraction = fraction;
210 output.normal = normalize(hit_point);
211 output.point = mul_add(p, sphere.radius, output.normal);
212 output.hit = true;
213
214 output
215}
216
217pub fn shape_cast_sphere(sphere: &Sphere, input: &ShapeCastInput) -> CastOutput {
219 let pair_input = ShapeCastPairInput {
220 proxy_a: make_proxy(&[sphere.center], sphere.radius),
221 proxy_b: input.proxy,
222 transform: TRANSFORM_IDENTITY,
223 translation_b: input.translation,
224 max_fraction: input.max_fraction,
225 can_encroach: input.can_encroach,
226 };
227
228 shape_cast(&pair_input)
229}
230
231pub fn collide_mover_and_sphere(result: &mut PlaneResult, shape: &Sphere, mover: &Capsule) -> i32 {
233 let total_radius = mover.radius + shape.radius;
234 let closest = point_to_segment_distance(mover.center1, mover.center2, shape.center);
235
236 let mut distance = 0.0;
238 let mut normal = get_length_and_normalize(&mut distance, sub(closest, shape.center));
239
240 if distance > total_radius {
241 return 0;
242 }
243
244 let linear_slop = linear_slop();
245 if distance < linear_slop {
246 let mut length = 0.0;
249 let axis = get_length_and_normalize(&mut length, sub(mover.center2, mover.center1));
250 normal = if length > linear_slop {
251 perp(axis)
252 } else {
253 VEC3_AXIS_Y
254 };
255 distance = 0.0;
256 }
257
258 result.plane = Plane {
259 normal,
260 offset: total_radius - distance,
261 };
262 result.point = shape.center;
263 1
264}