scirs2_spatial/collision/
response.rs1use super::shapes::{Box3D, Sphere, Triangle3D};
8
9#[derive(Debug, Clone)]
11pub struct CollisionInfo {
12 pub contact_point: [f64; 3],
14 pub normal: [f64; 3],
16 pub penetration: f64,
18}
19
20#[allow(dead_code)]
36#[allow(clippy::too_many_arguments)]
37pub fn sphere_sphere_impulse(
38 sphere1_pos: &[f64; 3],
39 sphere1_vel: &[f64; 3],
40 sphere1_mass: f64,
41 sphere2_pos: &[f64; 3],
42 sphere2_vel: &[f64; 3],
43 sphere2_mass: f64,
44 restitution: f64,
45) -> ([f64; 3], [f64; 3]) {
46 let normal = [
48 sphere2_pos[0] - sphere1_pos[0],
49 sphere2_pos[1] - sphere1_pos[1],
50 sphere2_pos[2] - sphere1_pos[2],
51 ];
52
53 let normal_length =
55 (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
56 if normal_length < 1e-10 {
57 return ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
59 }
60
61 let unit_normal = [
62 normal[0] / normal_length,
63 normal[1] / normal_length,
64 normal[2] / normal_length,
65 ];
66
67 let relative_velocity = [
69 sphere2_vel[0] - sphere1_vel[0],
70 sphere2_vel[1] - sphere1_vel[1],
71 sphere2_vel[2] - sphere1_vel[2],
72 ];
73
74 let velocity_along_normal = relative_velocity[0] * unit_normal[0]
76 + relative_velocity[1] * unit_normal[1]
77 + relative_velocity[2] * unit_normal[2];
78
79 if velocity_along_normal > 0.0 {
81 return ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
82 }
83
84 let inverse_mass1 = if sphere1_mass == 0.0 {
86 0.0
87 } else {
88 1.0 / sphere1_mass
89 };
90 let inverse_mass2 = if sphere2_mass == 0.0 {
91 0.0
92 } else {
93 1.0 / sphere2_mass
94 };
95
96 let impulse_scalar =
97 -(1.0 + restitution) * velocity_along_normal / (inverse_mass1 + inverse_mass2);
98
99 let impulse1 = [
101 -impulse_scalar * unit_normal[0] * inverse_mass1,
102 -impulse_scalar * unit_normal[1] * inverse_mass1,
103 -impulse_scalar * unit_normal[2] * inverse_mass1,
104 ];
105
106 let impulse2 = [
107 impulse_scalar * unit_normal[0] * inverse_mass2,
108 impulse_scalar * unit_normal[1] * inverse_mass2,
109 impulse_scalar * unit_normal[2] * inverse_mass2,
110 ];
111
112 (impulse1, impulse2)
113}
114
115#[allow(dead_code)]
128pub fn resolve_sphere_sphere_penetration(
129 sphere1: &Sphere,
130 sphere1_mass: f64,
131 sphere2: &Sphere,
132 sphere2_mass: f64,
133) -> ([f64; 3], [f64; 3]) {
134 let normal = [
136 sphere2.center[0] - sphere1.center[0],
137 sphere2.center[1] - sphere1.center[1],
138 sphere2.center[2] - sphere1.center[2],
139 ];
140
141 let distance = (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
142
143 if distance >= sphere1.radius + sphere2.radius {
145 return ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
146 }
147
148 let penetration = sphere1.radius + sphere2.radius - distance;
150
151 let unit_normal = if distance < 1e-10 {
153 [1.0, 0.0, 0.0]
155 } else {
156 [
157 normal[0] / distance,
158 normal[1] / distance,
159 normal[2] / distance,
160 ]
161 };
162
163 let total_mass = sphere1_mass + sphere2_mass;
165 let mass_ratio1 = if total_mass == 0.0 {
166 0.5
167 } else {
168 sphere2_mass / total_mass
169 };
170 let mass_ratio2 = if total_mass == 0.0 {
171 0.5
172 } else {
173 sphere1_mass / total_mass
174 };
175
176 let adjustment1 = [
178 -unit_normal[0] * penetration * mass_ratio1,
179 -unit_normal[1] * penetration * mass_ratio1,
180 -unit_normal[2] * penetration * mass_ratio1,
181 ];
182
183 let adjustment2 = [
184 unit_normal[0] * penetration * mass_ratio2,
185 unit_normal[1] * penetration * mass_ratio2,
186 unit_normal[2] * penetration * mass_ratio2,
187 ];
188
189 (adjustment1, adjustment2)
190}
191
192#[allow(clippy::too_many_arguments)]
210#[allow(dead_code)]
211pub fn sphere_box_impulse(
212 _sphere_pos: &[f64; 3],
213 sphere_vel: &[f64; 3],
214 sphere_mass: f64,
215 _box_pos: &[f64; 3],
216 box_vel: &[f64; 3],
217 box_mass: f64,
218 _box_dims: &[f64; 3],
219 collision_info: &CollisionInfo,
220 restitution: f64,
221) -> ([f64; 3], [f64; 3]) {
222 let relative_velocity = [
224 sphere_vel[0] - box_vel[0],
225 sphere_vel[1] - box_vel[1],
226 sphere_vel[2] - box_vel[2],
227 ];
228
229 let velocity_along_normal = relative_velocity[0] * collision_info.normal[0]
231 + relative_velocity[1] * collision_info.normal[1]
232 + relative_velocity[2] * collision_info.normal[2];
233
234 if velocity_along_normal > 0.0 {
236 return ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
237 }
238
239 let inverse_mass1 = if sphere_mass == 0.0 {
241 0.0
242 } else {
243 1.0 / sphere_mass
244 };
245 let inverse_mass2 = if box_mass == 0.0 { 0.0 } else { 1.0 / box_mass };
246
247 let impulse_scalar =
248 -(1.0 + restitution) * velocity_along_normal / (inverse_mass1 + inverse_mass2);
249
250 let impulse1 = [
252 impulse_scalar * collision_info.normal[0] * inverse_mass1,
253 impulse_scalar * collision_info.normal[1] * inverse_mass1,
254 impulse_scalar * collision_info.normal[2] * inverse_mass1,
255 ];
256
257 let impulse2 = [
258 -impulse_scalar * collision_info.normal[0] * inverse_mass2,
259 -impulse_scalar * collision_info.normal[1] * inverse_mass2,
260 -impulse_scalar * collision_info.normal[2] * inverse_mass2,
261 ];
262
263 (impulse1, impulse2)
264}
265
266#[allow(dead_code)]
277pub fn find_sphere_box_collision(sphere: &Sphere, box3d: &Box3D) -> Option<CollisionInfo> {
278 let closest_x = sphere.center[0].max(box3d.min[0]).min(box3d.max[0]);
280 let closest_y = sphere.center[1].max(box3d.min[1]).min(box3d.max[1]);
281 let closest_z = sphere.center[2].max(box3d.min[2]).min(box3d.max[2]);
282
283 let closest_point = [closest_x, closest_y, closest_z];
284
285 let dx = sphere.center[0] - closest_point[0];
287 let dy = sphere.center[1] - closest_point[1];
288 let dz = sphere.center[2] - closest_point[2];
289 let distance_squared = dx * dx + dy * dy + dz * dz;
290
291 if distance_squared > sphere.radius * sphere.radius {
293 return None;
294 }
295
296 let distance = distance_squared.sqrt();
297
298 let normal = if distance < 1e-10 {
300 let box_center = [
303 (box3d.min[0] + box3d.max[0]) * 0.5,
304 (box3d.min[1] + box3d.max[1]) * 0.5,
305 (box3d.min[2] + box3d.max[2]) * 0.5,
306 ];
307
308 let half_width = (box3d.max[0] - box3d.min[0]) * 0.5;
309 let half_height = (box3d.max[1] - box3d.min[1]) * 0.5;
310 let half_depth = (box3d.max[2] - box3d.min[2]) * 0.5;
311
312 let dx = (sphere.center[0] - box_center[0]).abs() / half_width;
313 let dy = (sphere.center[1] - box_center[1]).abs() / half_height;
314 let dz = (sphere.center[2] - box_center[2]).abs() / half_depth;
315
316 if dx > dy && dx > dz {
317 [(sphere.center[0] - box_center[0]).signum(), 0.0, 0.0]
318 } else if dy > dz {
319 [0.0, (sphere.center[1] - box_center[1]).signum(), 0.0]
320 } else {
321 [0.0, 0.0, (sphere.center[2] - box_center[2]).signum()]
322 }
323 } else {
324 [dx / distance, dy / distance, dz / distance]
325 };
326
327 let penetration = sphere.radius - distance;
328
329 Some(CollisionInfo {
330 contact_point: closest_point,
331 normal,
332 penetration,
333 })
334}
335
336#[allow(dead_code)]
347pub fn find_sphere_triangle_collision(
348 sphere: &Sphere,
349 triangle: &Triangle3D,
350) -> Option<CollisionInfo> {
351 let edge1 = [
353 triangle.v2[0] - triangle.v1[0],
354 triangle.v2[1] - triangle.v1[1],
355 triangle.v2[2] - triangle.v1[2],
356 ];
357
358 let edge2 = [
359 triangle.v3[0] - triangle.v1[0],
360 triangle.v3[1] - triangle.v1[1],
361 triangle.v3[2] - triangle.v1[2],
362 ];
363
364 let normal = [
366 edge1[1] * edge2[2] - edge1[2] * edge2[1],
367 edge1[2] * edge2[0] - edge1[0] * edge2[2],
368 edge1[0] * edge2[1] - edge1[1] * edge2[0],
369 ];
370
371 let normal_length =
373 (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2]).sqrt();
374 if normal_length < 1e-10 {
375 return None; }
377
378 let normalized_normal = [
379 normal[0] / normal_length,
380 normal[1] / normal_length,
381 normal[2] / normal_length,
382 ];
383
384 let dist_to_plane = (sphere.center[0] - triangle.v1[0]) * normalized_normal[0]
386 + (sphere.center[1] - triangle.v1[1]) * normalized_normal[1]
387 + (sphere.center[2] - triangle.v1[2]) * normalized_normal[2];
388
389 if dist_to_plane.abs() > sphere.radius {
391 return None;
392 }
393
394 let projected_center = [
396 sphere.center[0] - dist_to_plane * normalized_normal[0],
397 sphere.center[1] - dist_to_plane * normalized_normal[1],
398 sphere.center[2] - dist_to_plane * normalized_normal[2],
399 ];
400
401 let inside_triangle =
403 super::narrowphase::point_triangle3d_collision(&projected_center, triangle);
404
405 if inside_triangle {
407 let contact_normal = if dist_to_plane > 0.0 {
408 normalized_normal
409 } else {
410 [
411 -normalized_normal[0],
412 -normalized_normal[1],
413 -normalized_normal[2],
414 ]
415 };
416
417 return Some(CollisionInfo {
418 contact_point: [
419 sphere.center[0] - contact_normal[0] * sphere.radius,
420 sphere.center[1] - contact_normal[1] * sphere.radius,
421 sphere.center[2] - contact_normal[2] * sphere.radius,
422 ],
423 normal: contact_normal,
424 penetration: sphere.radius - dist_to_plane.abs(),
425 });
426 }
427
428 let edges = [
430 (triangle.v1, triangle.v2),
431 (triangle.v2, triangle.v3),
432 (triangle.v3, triangle.v1),
433 ];
434
435 let mut closest_point = [0.0, 0.0, 0.0];
436 let mut min_distance = f64::INFINITY;
437
438 for &(start, end) in &edges {
439 let edge_vec = [end[0] - start[0], end[1] - start[1], end[2] - start[2]];
441
442 let sphere_vec = [
444 sphere.center[0] - start[0],
445 sphere.center[1] - start[1],
446 sphere.center[2] - start[2],
447 ];
448
449 let edge_length_squared =
451 edge_vec[0] * edge_vec[0] + edge_vec[1] * edge_vec[1] + edge_vec[2] * edge_vec[2];
452
453 let t = (sphere_vec[0] * edge_vec[0]
455 + sphere_vec[1] * edge_vec[1]
456 + sphere_vec[2] * edge_vec[2])
457 / edge_length_squared;
458
459 let t_clamped = t.clamp(0.0, 1.0);
461
462 let point = [
464 start[0] + t_clamped * edge_vec[0],
465 start[1] + t_clamped * edge_vec[1],
466 start[2] + t_clamped * edge_vec[2],
467 ];
468
469 let dx = sphere.center[0] - point[0];
471 let dy = sphere.center[1] - point[1];
472 let dz = sphere.center[2] - point[2];
473 let distance_squared = dx * dx + dy * dy + dz * dz;
474
475 if distance_squared < min_distance {
476 min_distance = distance_squared;
477 closest_point = point;
478 }
479 }
480
481 let vertices = [triangle.v1, triangle.v2, triangle.v3];
483
484 for &vertex in &vertices {
485 let dx = sphere.center[0] - vertex[0];
486 let dy = sphere.center[1] - vertex[1];
487 let dz = sphere.center[2] - vertex[2];
488 let distance_squared = dx * dx + dy * dy + dz * dz;
489
490 if distance_squared < min_distance {
491 min_distance = distance_squared;
492 closest_point = vertex;
493 }
494 }
495
496 if min_distance > sphere.radius * sphere.radius {
498 return None;
499 }
500
501 let distance = min_distance.sqrt();
502 let normal = [
503 (sphere.center[0] - closest_point[0]) / distance,
504 (sphere.center[1] - closest_point[1]) / distance,
505 (sphere.center[2] - closest_point[2]) / distance,
506 ];
507
508 Some(CollisionInfo {
509 contact_point: closest_point,
510 normal,
511 penetration: sphere.radius - distance,
512 })
513}