1use crate::DevicePointer;
2use core::marker::PhantomData;
3use na::ComplexField;
4use parry::query::{PointProjection, PointQueryWithLocation};
5use parry::shape::{Cuboid, CudaHeightFieldPtr, CudaTriMeshPtr, Segment, SegmentPointLocation};
6use parry::utils::CudaArrayPointer1;
7use sparkl_core::dynamics::solver::BoundaryHandling;
8use sparkl_core::math::{Isometry, Point, Real};
9
10#[cfg_attr(not(target_os = "cuda"), derive(cust::DeviceCopy))]
11#[derive(Copy, Clone)]
12#[repr(C)]
13pub enum GpuColliderShape {
14 Cuboid(Cuboid),
15 HeightField {
16 heightfield: CudaHeightFieldPtr,
17 bellow_heightfield_is_solid: bool,
18 flip_interior: bool,
19 },
20 TriMesh {
21 trimesh: CudaTriMeshPtr,
22 flip_interior: bool,
23 },
24 Polyline {
25 vertices: parry::utils::CudaArrayPointer1<Point<Real>>,
26 flip_interior: bool,
27 },
28}
29
30impl GpuColliderShape {
31 #[cfg(not(target_os = "cuda"))]
32 pub fn project_point_with_max_dist(
33 &self,
34 _position: &Isometry<Real>,
35 _point: &Point<Real>,
36 _solid: bool,
37 _max_dist: Real,
38 ) -> Option<PointProjection> {
39 unreachable!()
40 }
41
42 #[cfg(target_os = "cuda")]
43 pub fn project_point_with_max_dist(
44 &self,
45 position: &Isometry<Real>,
46 point: &Point<Real>,
47 solid: bool,
48 max_dist: Real,
49 ) -> Option<PointProjection> {
50 use parry::query::PointQuery;
51
52 match self {
53 Self::Cuboid(s) => Some(s.project_point(position, point, solid)),
54 Self::Polyline {
55 vertices,
56 flip_interior,
57 } => polyline_project_point(vertices, position, point, solid, *flip_interior),
58 Self::TriMesh {
59 trimesh,
60 flip_interior,
61 } => trimesh
62 .project_point_with_max_dist(position, point, solid, Real::MAX) .map(|mut proj| {
64 if *flip_interior {
65 proj.is_inside = !proj.is_inside;
66 }
67 proj
68 }),
69 Self::HeightField {
70 heightfield,
71 bellow_heightfield_is_solid,
72 flip_interior,
73 } => {
74 let local_point = position.inverse_transform_point(point);
75 let mut local_aabb = heightfield.local_aabb();
76 local_aabb.mins.y = -Real::MAX;
77 local_aabb.maxs.y = Real::MAX;
78 let mut local_proj =
79 heightfield.project_local_point_with_max_dist(&local_point, solid, max_dist)?;
80
81 if *bellow_heightfield_is_solid {
82 if !flip_interior {
83 local_proj.is_inside = local_aabb.contains_local_point(&local_point)
84 && local_point.y <= local_proj.point.y;
85 } else {
86 local_proj.is_inside = local_aabb.contains_local_point(&local_point)
87 && local_point.y >= local_proj.point.y;
88 }
89 }
90
91 Some(local_proj.transform_by(position))
92 }
93 _ => None,
94 }
95 }
96}
97
98#[cfg(target_os = "cuda")]
99pub fn polyline_project_point(
100 vertices: &CudaArrayPointer1<Point<Real>>,
101 position: &Isometry<Real>,
102 point: &Point<Real>,
103 solid: bool,
104 flip_interior: bool,
105) -> Option<PointProjection> {
106 #[derive(Copy, Clone)]
107 struct BestProjection {
108 proj: PointProjection,
109 location: SegmentPointLocation,
110 dist: Real,
111 id: usize,
112 segment: Segment,
113 }
114
115 let local_point = position.inverse_transform_point(point);
116
117 let mut best_proj: Option<BestProjection> = None;
119
120 let ith_segment = |i| Segment::new(vertices.get(i), vertices.get((i + 1) % vertices.len()));
121
122 for i in 0..vertices.len() {
123 let segment = ith_segment(i);
124 let (proj, location) = segment.project_local_point_and_get_location(&local_point, false);
125 let candidate_dist = na::distance(&local_point, &proj.point);
126
127 if best_proj.map(|p| candidate_dist < p.dist).unwrap_or(true) {
128 best_proj = Some(BestProjection {
129 proj,
130 location,
131 dist: candidate_dist,
132 id: i,
133 segment,
134 });
135 }
136 }
137
138 if let Some(best_proj) = &mut best_proj {
139 #[cfg(feature = "dim2")]
141 let normal1 = best_proj.segment.normal();
142 #[cfg(feature = "dim3")]
143 let normal1 = best_proj.segment.planar_normal(2);
144
145 if let Some(normal1) = normal1 {
146 best_proj.proj.is_inside = match best_proj.location {
147 SegmentPointLocation::OnVertex(i) => {
148 let dir2 = if i == 0 {
149 let adj_seg = if best_proj.id == 0 {
150 vertices.len() - 1
151 } else {
152 best_proj.id - 1
153 };
154
155 assert_eq!(best_proj.segment.a, ith_segment(adj_seg).b);
156 -ith_segment(adj_seg).scaled_direction()
157 } else {
158 assert_eq!(i, 1);
159 let adj_seg = (best_proj.id + 1) % vertices.len();
160 assert_eq!(best_proj.segment.b, ith_segment(adj_seg).a);
161
162 ith_segment(adj_seg).scaled_direction()
163 };
164
165 let dot = normal1.dot(&dir2);
166 let threshold = 1.0e-3 * dir2.norm();
172 if dot.abs() > threshold {
173 dot >= 0.0
176 } else {
177 (point - best_proj.proj.point).dot(&normal1) <= 0.0
180 }
181 }
182 SegmentPointLocation::OnEdge(_) => {
183 (point - best_proj.proj.point).dot(&normal1) <= 0.0
184 }
185 };
186 }
187 }
188
189 best_proj.map(|mut p| {
190 #[cfg(feature = "dim3")]
191 {
192 p.proj.point.z = local_point.z;
193 }
194
195 if flip_interior {
196 p.proj.is_inside = !p.proj.is_inside;
197 }
198
199 p.proj.transform_by(position)
200 })
201}
202
203#[cfg_attr(not(target_os = "cuda"), derive(cust::DeviceCopy))]
204#[derive(Copy, Clone)]
205#[repr(C)]
206pub struct GpuCollider {
207 pub shape: GpuColliderShape,
208 pub position: Isometry<Real>,
209 pub friction: Real,
210 pub penalty_stiffness: Real,
211 pub grid_boundary_handling: BoundaryHandling,
212}
213
214pub struct GpuColliderSet {
215 pub ptr: *const GpuCollider,
216 pub len: usize,
217}
218
219impl GpuColliderSet {
220 pub fn get(&self, i: usize) -> Option<&GpuCollider> {
221 if i >= self.len {
222 None
223 } else {
224 unsafe { Some(&*self.ptr.add(i)) }
225 }
226 }
227
228 pub fn iter(&self) -> GpuColliderIter {
229 GpuColliderIter {
230 ptr: self.ptr,
231 len: self.len,
232 _marker: PhantomData,
233 }
234 }
235}
236
237pub struct GpuColliderIter<'a> {
238 ptr: *const GpuCollider,
239 len: usize,
240 _marker: PhantomData<&'a ()>,
241}
242
243impl<'a> Iterator for GpuColliderIter<'a> {
244 type Item = &'a GpuCollider;
245
246 fn next(&mut self) -> Option<Self::Item> {
247 if self.len == 0 {
248 None
249 } else {
250 let curr = self.ptr;
251
252 unsafe {
253 self.ptr = self.ptr.offset(1);
254 self.len -= 1;
255 Some(&*curr)
256 }
257 }
258 }
259}