1pub(crate) mod array;
2pub(crate) mod heap;
3pub(crate) mod refer;
4
5use alloc::vec::Vec;
6#[cfg(not(feature = "std"))]
7#[allow(unused_imports)]
8use crate::F32Ext;
9
10use glam::Vec3;
11use wide::f32x8;
12
13use crate::{Capsule, ConvexPolytope, Cuboid, Sphere, convex_polytope::array::ArrayConvexPolytope};
14
15#[inline]
17pub(crate) fn max_projection(vertices: &[Vec3], normal: Vec3) -> f32 {
18 let nx = f32x8::splat(normal.x);
19 let ny = f32x8::splat(normal.y);
20 let nz = f32x8::splat(normal.z);
21 let mut max_val = f32x8::splat(f32::NEG_INFINITY);
22
23 let chunks = vertices.chunks_exact(8);
24 let remainder = chunks.remainder();
25
26 for chunk in chunks {
27 let mut vx = [0.0f32; 8];
28 let mut vy = [0.0f32; 8];
29 let mut vz = [0.0f32; 8];
30 for (i, v) in chunk.iter().enumerate() {
31 vx[i] = v.x;
32 vy[i] = v.y;
33 vz[i] = v.z;
34 }
35 let proj = nx * f32x8::new(vx) + ny * f32x8::new(vy) + nz * f32x8::new(vz);
36 max_val = max_val.max(proj);
37 }
38
39 let arr = max_val.to_array();
40 let mut result = arr[0];
41 for &v in &arr[1..] {
42 if v > result {
43 result = v;
44 }
45 }
46 for v in remainder {
47 let p = normal.dot(*v);
48 if p > result {
49 result = p;
50 }
51 }
52 result
53}
54
55#[inline]
57pub(crate) fn min_projection(vertices: &[Vec3], normal: Vec3) -> f32 {
58 let nx = f32x8::splat(normal.x);
59 let ny = f32x8::splat(normal.y);
60 let nz = f32x8::splat(normal.z);
61 let mut min_val = f32x8::splat(f32::INFINITY);
62
63 let chunks = vertices.chunks_exact(8);
64 let remainder = chunks.remainder();
65
66 for chunk in chunks {
67 let mut vx = [0.0f32; 8];
68 let mut vy = [0.0f32; 8];
69 let mut vz = [0.0f32; 8];
70 for (i, v) in chunk.iter().enumerate() {
71 vx[i] = v.x;
72 vy[i] = v.y;
73 vz[i] = v.z;
74 }
75 let proj = nx * f32x8::new(vx) + ny * f32x8::new(vy) + nz * f32x8::new(vz);
76 min_val = min_val.min(proj);
77 }
78
79 let arr = min_val.to_array();
80 let mut result = arr[0];
81 for &v in &arr[1..] {
82 if v < result {
83 result = v;
84 }
85 }
86 for v in remainder {
87 let p = normal.dot(*v);
88 if p < result {
89 result = p;
90 }
91 }
92 result
93}
94
95fn compute_obb(vertices: &[Vec3]) -> Cuboid {
96 if vertices.is_empty() {
97 return Cuboid::new(Vec3::ZERO, [Vec3::X, Vec3::Y, Vec3::Z], [0.0; 3]);
98 }
99
100 let n = vertices.len() as f32;
101 let mean = vertices.iter().copied().sum::<Vec3>() / n;
102
103 let mut cov = [[0.0f32; 3]; 3];
105 for v in vertices {
106 let d = *v - mean;
107 let da = [d.x, d.y, d.z];
108 for i in 0..3 {
109 for j in i..3 {
110 cov[i][j] += da[i] * da[j];
111 }
112 }
113 }
114 for i in 0..3 {
115 for j in i..3 {
116 cov[i][j] /= n;
117 if j != i {
118 cov[j][i] = cov[i][j];
119 }
120 }
121 }
122
123 let axes = jacobi_eigenvectors_3x3(cov);
125
126 let mut min_proj = [0.0f32; 3];
128 let mut max_proj = [0.0f32; 3];
129 for i in 0..3 {
130 max_proj[i] = max_projection(vertices, axes[i]);
131 min_proj[i] = min_projection(vertices, axes[i]);
132 }
133
134 let center_proj: Vec3 = Vec3::new(
135 (min_proj[0] + max_proj[0]) * 0.5,
136 (min_proj[1] + max_proj[1]) * 0.5,
137 (min_proj[2] + max_proj[2]) * 0.5,
138 );
139 let center = axes[0] * center_proj.x + axes[1] * center_proj.y + axes[2] * center_proj.z;
140 let half_extents = [
141 (max_proj[0] - min_proj[0]) * 0.5,
142 (max_proj[1] - min_proj[1]) * 0.5,
143 (max_proj[2] - min_proj[2]) * 0.5,
144 ];
145
146 Cuboid::new(center, axes, half_extents)
147}
148
149fn jacobi_eigenvectors_3x3(mut a: [[f32; 3]; 3]) -> [Vec3; 3] {
150 let mut v = [[0.0f32; 3]; 3];
151 for i in 0..3 {
152 v[i][i] = 1.0;
153 }
154
155 for _ in 0..50 {
156 let mut p = 0;
158 let mut q = 1;
159 let mut max_val = a[0][1].abs();
160 for i in 0..3 {
161 for j in (i + 1)..3 {
162 if a[i][j].abs() > max_val {
163 max_val = a[i][j].abs();
164 p = i;
165 q = j;
166 }
167 }
168 }
169
170 if max_val < 1e-10 {
171 break;
172 }
173
174 let theta = 0.5 * (a[q][q] - a[p][p]).atan2(a[p][q]);
175 let c = theta.cos();
176 let s = theta.sin();
177
178 let mut new_a = a;
180 for i in 0..3 {
181 new_a[i][p] = c * a[i][p] + s * a[i][q];
182 new_a[i][q] = -s * a[i][p] + c * a[i][q];
183 }
184 a = new_a;
185 let mut new_a = a;
186 for j in 0..3 {
187 new_a[p][j] = c * a[p][j] + s * a[q][j];
188 new_a[q][j] = -s * a[p][j] + c * a[q][j];
189 }
190 a = new_a;
191
192 let mut new_v = v;
194 for i in 0..3 {
195 new_v[i][p] = c * v[i][p] + s * v[i][q];
196 new_v[i][q] = -s * v[i][p] + c * v[i][q];
197 }
198 v = new_v;
199 }
200
201 [
202 Vec3::new(v[0][0], v[1][0], v[2][0]).normalize_or_zero(),
203 Vec3::new(v[0][1], v[1][1], v[2][1]).normalize_or_zero(),
204 Vec3::new(v[0][2], v[1][2], v[2][2]).normalize_or_zero(),
205 ]
206}
207
208use crate::Collides;
209use refer::RefConvexPolytope;
210
211impl<const P: usize, const V: usize> Collides<ConvexPolytope> for ArrayConvexPolytope<P, V> {
212 #[inline]
213 fn test<const BROADPHASE: bool>(&self, other: &ConvexPolytope) -> bool {
214 RefConvexPolytope::from_array(self)
215 .collides_polytope::<BROADPHASE>(&RefConvexPolytope::from_heap(other))
216 }
217}
218
219impl<const P: usize, const V: usize> Collides<ArrayConvexPolytope<P, V>> for ConvexPolytope {
220 #[inline]
221 fn test<const BROADPHASE: bool>(&self, other: &ArrayConvexPolytope<P, V>) -> bool {
222 RefConvexPolytope::from_heap(self)
223 .collides_polytope::<BROADPHASE>(&RefConvexPolytope::from_array(other))
224 }
225}
226
227impl From<Sphere> for ConvexPolytope {
229 fn from(sphere: Sphere) -> Self {
230 let phi = (1.0 + 5.0_f32.sqrt()) / 2.0;
232 let len = (1.0 + phi * phi).sqrt();
233 let a = 1.0 / len;
234 let b = phi / len;
235
236 let ico = [
238 Vec3::new(-a, b, 0.0),
239 Vec3::new(a, b, 0.0),
240 Vec3::new(-a, -b, 0.0),
241 Vec3::new(a, -b, 0.0),
242 Vec3::new(0.0, -a, b),
243 Vec3::new(0.0, a, b),
244 Vec3::new(0.0, -a, -b),
245 Vec3::new(0.0, a, -b),
246 Vec3::new(b, 0.0, -a),
247 Vec3::new(b, 0.0, a),
248 Vec3::new(-b, 0.0, -a),
249 Vec3::new(-b, 0.0, a),
250 ];
251
252 let faces: [[usize; 3]; 20] = [
254 [0, 11, 5],
255 [0, 5, 1],
256 [0, 1, 7],
257 [0, 7, 10],
258 [0, 10, 11],
259 [1, 5, 9],
260 [5, 11, 4],
261 [11, 10, 2],
262 [10, 7, 6],
263 [7, 1, 8],
264 [3, 9, 4],
265 [3, 4, 2],
266 [3, 2, 6],
267 [3, 6, 8],
268 [3, 8, 9],
269 [4, 9, 5],
270 [2, 4, 11],
271 [6, 2, 10],
272 [8, 6, 7],
273 [9, 8, 1],
274 ];
275
276 let mut vertices = Vec::new();
278 let mut normals_set: Vec<Vec3> = Vec::new();
279
280 #[cfg(feature = "std")]
281 let mut get_or_insert = {
282 let mut vert_map = std::collections::HashMap::new();
283 move |v: Vec3, verts: &mut Vec<Vec3>| -> usize {
284 let key = ((v.x * 1e5) as i32, (v.y * 1e5) as i32, (v.z * 1e5) as i32);
285 *vert_map.entry(key).or_insert_with(|| {
286 let idx = verts.len();
287 verts.push(v);
288 idx
289 })
290 }
291 };
292
293 #[cfg(not(feature = "std"))]
294 let get_or_insert = |v: Vec3, verts: &mut Vec<Vec3>| -> usize {
295 let key = ((v.x * 1e5) as i32, (v.y * 1e5) as i32, (v.z * 1e5) as i32);
296 for (i, existing) in verts.iter().enumerate() {
297 let ek = (
298 (existing.x * 1e5) as i32,
299 (existing.y * 1e5) as i32,
300 (existing.z * 1e5) as i32,
301 );
302 if ek == key {
303 return i;
304 }
305 }
306 let idx = verts.len();
307 verts.push(v);
308 idx
309 };
310
311 let mut sub_faces: Vec<[usize; 3]> = Vec::new();
312 for face in &faces {
313 let v0 = ico[face[0]];
314 let v1 = ico[face[1]];
315 let v2 = ico[face[2]];
316 let m01 = ((v0 + v1) * 0.5).normalize();
317 let m12 = ((v1 + v2) * 0.5).normalize();
318 let m20 = ((v2 + v0) * 0.5).normalize();
319
320 let i0 = get_or_insert(v0, &mut vertices);
321 let i1 = get_or_insert(v1, &mut vertices);
322 let i2 = get_or_insert(v2, &mut vertices);
323 let i01 = get_or_insert(m01, &mut vertices);
324 let i12 = get_or_insert(m12, &mut vertices);
325 let i20 = get_or_insert(m20, &mut vertices);
326
327 sub_faces.push([i0, i01, i20]);
328 sub_faces.push([i01, i1, i12]);
329 sub_faces.push([i20, i12, i2]);
330 sub_faces.push([i01, i12, i20]);
331 }
332
333 for face in &sub_faces {
335 let v0 = vertices[face[0]];
336 let v1 = vertices[face[1]];
337 let v2 = vertices[face[2]];
338 let n = (v1 - v0).cross(v2 - v0);
339 if n.length_squared() > 1e-10 {
340 let n = n.normalize();
341 let n = if n.dot(v0) > 0.0 { n } else { -n };
343 if !normals_set.iter().any(|existing| existing.dot(n) > 0.9999) {
344 normals_set.push(n);
345 }
346 }
347 }
348
349 let scaled_verts: Vec<Vec3> = vertices
351 .iter()
352 .map(|v| sphere.center + *v * sphere.radius)
353 .collect();
354
355 let planes: Vec<(Vec3, f32)> = normals_set
357 .iter()
358 .map(|&n| {
359 let d = max_projection(&scaled_verts, n);
360 (n, d)
361 })
362 .collect();
363
364 let obb = Cuboid::new(
365 sphere.center,
366 [Vec3::X, Vec3::Y, Vec3::Z],
367 [sphere.radius; 3],
368 );
369
370 ConvexPolytope::with_obb(planes, scaled_verts, obb)
371 }
372}
373
374impl From<Cuboid> for ConvexPolytope {
375 fn from(cuboid: Cuboid) -> Self {
376 let planes = vec![
378 (
379 cuboid.axes[0],
380 cuboid.axes[0].dot(cuboid.center) + cuboid.half_extents[0],
381 ),
382 (
383 -cuboid.axes[0],
384 (-cuboid.axes[0]).dot(cuboid.center) + cuboid.half_extents[0],
385 ),
386 (
387 cuboid.axes[1],
388 cuboid.axes[1].dot(cuboid.center) + cuboid.half_extents[1],
389 ),
390 (
391 -cuboid.axes[1],
392 (-cuboid.axes[1]).dot(cuboid.center) + cuboid.half_extents[1],
393 ),
394 (
395 cuboid.axes[2],
396 cuboid.axes[2].dot(cuboid.center) + cuboid.half_extents[2],
397 ),
398 (
399 -cuboid.axes[2],
400 (-cuboid.axes[2]).dot(cuboid.center) + cuboid.half_extents[2],
401 ),
402 ];
403
404 let mut vertices = Vec::with_capacity(8);
406 for &sx in &[-1.0_f32, 1.0] {
407 for &sy in &[-1.0_f32, 1.0] {
408 for &sz in &[-1.0_f32, 1.0] {
409 vertices.push(
410 cuboid.center
411 + cuboid.axes[0] * (sx * cuboid.half_extents[0])
412 + cuboid.axes[1] * (sy * cuboid.half_extents[1])
413 + cuboid.axes[2] * (sz * cuboid.half_extents[2]),
414 );
415 }
416 }
417 }
418
419 ConvexPolytope::with_obb(planes, vertices, cuboid)
420 }
421}
422
423impl From<Capsule> for ConvexPolytope {
424 fn from(capsule: Capsule) -> Self {
425 let p1 = capsule.p1;
428 let p2 = capsule.p2();
429 let dir = capsule.dir;
430 let dir_len = dir.length();
431
432 let (ax_fwd, ax_u, ax_v) = if dir_len > 1e-6 {
434 let fwd = dir / dir_len;
435 let up = if fwd.y.abs() < 0.9 { Vec3::Y } else { Vec3::X };
436 let u = fwd.cross(up).normalize();
437 let v = u.cross(fwd);
438 (fwd, u, v)
439 } else {
440 return ConvexPolytope::from(Sphere::new(p1, capsule.radius));
442 };
443
444 let r = capsule.radius;
445 let n_ring = 8;
446 let mut vertices = Vec::new();
447
448 vertices.push(p1 - ax_fwd * r); for i in 0..n_ring {
451 let angle = core::f32::consts::TAU * i as f32 / n_ring as f32;
452 let (sin_a, cos_a) = angle.sin_cos();
453 vertices.push(p1 + (ax_u * cos_a + ax_v * sin_a) * r);
455 let lat = core::f32::consts::FRAC_PI_4;
457 vertices.push(
458 p1 - ax_fwd * (r * lat.sin()) + (ax_u * cos_a + ax_v * sin_a) * (r * lat.cos()),
459 );
460 }
461
462 vertices.push(p2 + ax_fwd * r); for i in 0..n_ring {
465 let angle = core::f32::consts::TAU * i as f32 / n_ring as f32;
466 let (sin_a, cos_a) = angle.sin_cos();
467 vertices.push(p2 + (ax_u * cos_a + ax_v * sin_a) * r);
469 let lat = core::f32::consts::FRAC_PI_4;
471 vertices.push(
472 p2 + ax_fwd * (r * lat.sin()) + (ax_u * cos_a + ax_v * sin_a) * (r * lat.cos()),
473 );
474 }
475
476 let mut planes: Vec<(Vec3, f32)> = vec![
479 (ax_fwd, ax_fwd.dot(p2) + r),
480 (-ax_fwd, (-ax_fwd).dot(p1) + r),
481 ];
482
483 for i in 0..n_ring {
485 let angle = core::f32::consts::TAU * i as f32 / n_ring as f32;
486 let (sin_a, cos_a) = angle.sin_cos();
487 let radial = (ax_u * cos_a + ax_v * sin_a).normalize();
488 let d = max_projection(&vertices, radial);
489 planes.push((radial, d));
490
491 for &blend_fwd in &[0.5_f32, -0.5] {
493 let diag = (radial + ax_fwd * blend_fwd).normalize();
494 let d = max_projection(&vertices, diag);
495 planes.push((diag, d));
496 }
497 }
498
499 let obb = compute_obb(&vertices);
500 ConvexPolytope::with_obb(planes, vertices, obb)
501 }
502}
503
504impl<const P: usize, const V: usize> From<ArrayConvexPolytope<P, V>> for ConvexPolytope {
505 fn from(polytope: ArrayConvexPolytope<P, V>) -> Self {
506 ConvexPolytope::with_obb(
507 polytope.planes.to_vec(),
508 polytope.vertices.to_vec(),
509 polytope.obb,
510 )
511 }
512}