PeriDyno 1.0.0
Loading...
Searching...
No Matches
FastMarchingMethodGPU.cu
Go to the documentation of this file.
1#include "FastMarchingMethodGPU.h"
2
3#include "Algorithm/Reduction.h"
4
5#include "Collision/Distance3D.h"
6
7namespace dyno
8{
9 IMPLEMENT_TCLASS(FastMarchingMethodGPU, TDataType)
10
11 template<typename TDataType>
12 FastMarchingMethodGPU<TDataType>::FastMarchingMethodGPU()
13 : ComputeModule()
14 {
15 }
16
17 template<typename TDataType>
18 FastMarchingMethodGPU<TDataType>::~FastMarchingMethodGPU()
19 {
20 }
21
22 __device__ void FSM_SWAP(
23 Real& a,
24 Real& b)
25 {
26 Real tmp = b;
27 b = a;
28 a = tmp;
29 }
30
31 __device__ void UpdatePhi(
32 DArray3D<Real>& phi,
33 DArray3D<GridType>& type,
34 DArray3D<bool>& outside,
35 uint i, uint j, uint k,
36 Real dx)
37 {
38 bool outside_ijk = outside(i, j, k);
39
40 uint nx = phi.nx();
41 uint ny = phi.ny();
42 uint nz = phi.nz();
43
44 Real phi_minx, phi_miny, phi_minz;
45
46 if (i == 0) phi_minx = phi(1, j, k);
47 else if (i == nx - 1) phi_minx = phi(nx - 2, j, k);
48 else phi_minx = outside_ijk ? minimum(phi(i - 1, j, k), phi(i + 1, j, k)) : maximum(phi(i - 1, j, k), phi(i + 1, j, k));
49
50 if (j == 0) phi_miny = phi(i, 1, k);
51 else if (j == ny - 1) phi_miny = phi(i, ny - 2, k);
52 else phi_miny = outside_ijk ? minimum(phi(i, j - 1, k), phi(i, j + 1, k)) : maximum(phi(i, j - 1, k), phi(i, j + 1, k));
53
54 if (k == 0) phi_minz = phi(i, j, 1);
55 else if (k == nz - 1) phi_minz = phi(i, j, nz - 2);
56 else phi_minz = outside_ijk ? minimum(phi(i, j, k - 1), phi(i, j, k + 1)) : maximum(phi(i, j, k - 1), phi(i, j, k + 1));
57
58 Real a[3];
59 a[0] = phi_minx;
60 a[1] = phi_miny;
61 a[2] = phi_minz;
62
63 // Sort
64 if (outside_ijk)
65 {
66 if (a[0] > a[1]) FSM_SWAP(a[0], a[1]);
67 if (a[1] > a[2]) FSM_SWAP(a[1], a[2]);
68 if (a[0] > a[1]) FSM_SWAP(a[0], a[1]);
69 }
70 else
71 {
72 if (a[0] < a[1]) FSM_SWAP(a[0], a[1]);
73 if (a[1] < a[2]) FSM_SWAP(a[1], a[2]);
74 if (a[0] < a[1]) FSM_SWAP(a[0], a[1]);
75 }
76
77 Real phi_ijk;
78 Real sum_a = a[0] + a[1] + a[2];
79 Real sum_a2 = a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
80
81 Real sign = outside_ijk ? Real(1) : Real(-1);
82
83 if (glm::abs(a[0] - a[2]) < dx)
84 {
85 phi_ijk = (2 * sum_a + sign * glm::sqrt(4 * sum_a * sum_a - 12 * (sum_a2 - dx * dx))) / Real(6);
86 }
87 else if (glm::abs(a[0] - a[1]) < dx)
88 {
89 phi_ijk = 0.5 * (a[0] + a[1] + sign * glm::sqrt(2 * dx * dx - (a[0] - a[1]) * (a[0] - a[1])));
90 }
91 else
92 {
93 phi_ijk = a[0] + sign * dx;
94 }
95
96 Real phi_ijk_old = phi(i, j, k);
97
98 phi(i, j, k) = phi_ijk;
99
100
101 if (glm::abs(phi_ijk_old - phi_ijk) < EPSILON)
102 {
103 type(i, j, k) = GridType::Accepted;
104 }
105 }
106
107 template<typename Real>
108 __global__ void FSMI_FastMarching(
109 DArray3D<Real> phi,
110 DArray3D<GridType> pointType,
111 DArray3D<bool> outside,
112 Real dx)
113 {
114 int i = threadIdx.x + (blockIdx.x * blockDim.x);
115 int j = threadIdx.y + (blockIdx.y * blockDim.y);
116 int k = threadIdx.z + (blockIdx.z * blockDim.z);
117
118 uint nx = phi.nx();
119 uint ny = phi.ny();
120 uint nz = phi.nz();
121
122 if (i > nx || j >= ny || k >= nz) return;
123
124 if (pointType(i, j, k) != GridType::Tentative)
125 return;
126
127 UpdatePhi(phi, pointType, outside, i, j, k, dx);
128 }
129
130 __global__ void FSMI_CheckTentativeX(
131 DArray3D<GridType> pointType)
132 {
133 int i = threadIdx.x + (blockIdx.x * blockDim.x);
134 int j = threadIdx.y + (blockIdx.y * blockDim.y);
135 int k = threadIdx.z + (blockIdx.z * blockDim.z);
136
137 uint nx = pointType.nx();
138 uint ny = pointType.ny();
139 uint nz = pointType.nz();
140
141 if (i >= nx - 1 || j >= ny || k >= nz) return;
142
143 GridType type0 = pointType(i, j, k);
144 GridType type1 = pointType(i + 1, j, k);
145
146 if (type0 == GridType::Accepted && type1 != GridType::Accepted)
147 pointType(i + 1, j, k) = GridType::Tentative;
148
149 if (type0 != GridType::Accepted && type1 == GridType::Accepted)
150 pointType(i, j, k) = GridType::Tentative;
151 }
152
153 __global__ void FSMI_CheckTentativeY(
154 DArray3D<GridType> pointType)
155 {
156 int i = threadIdx.x + (blockIdx.x * blockDim.x);
157 int j = threadIdx.y + (blockIdx.y * blockDim.y);
158 int k = threadIdx.z + (blockIdx.z * blockDim.z);
159
160 uint nx = pointType.nx();
161 uint ny = pointType.ny();
162 uint nz = pointType.nz();
163
164 if (i >= nx || j >= ny - 1 || k >= nz) return;
165
166 GridType type0 = pointType(i, j, k);
167 GridType type1 = pointType(i, j + 1, k);
168
169 if (type0 == GridType::Accepted && type1 != GridType::Accepted)
170 pointType(i, j + 1, k) = GridType::Tentative;
171
172 if (type0 != GridType::Accepted && type1 == GridType::Accepted)
173 pointType(i, j, k) = GridType::Tentative;
174 }
175
176 __global__ void FSMI_CheckTentativeZ(
177 DArray3D<GridType> pointType)
178 {
179 int i = threadIdx.x + (blockIdx.x * blockDim.x);
180 int j = threadIdx.y + (blockIdx.y * blockDim.y);
181 int k = threadIdx.z + (blockIdx.z * blockDim.z);
182
183 uint nx = pointType.nx();
184 uint ny = pointType.ny();
185 uint nz = pointType.nz();
186
187 if (i >= nx || j >= ny || k >= nz - 1) return;
188
189 GridType type0 = pointType(i, j, k);
190 GridType type1 = pointType(i, j, k + 1);
191
192 if (type0 == GridType::Accepted && type1 != GridType::Accepted)
193 pointType(i, j, k + 1) = GridType::Tentative;
194
195 if (type0 != GridType::Accepted && type1 == GridType::Accepted)
196 pointType(i, j, k) = GridType::Tentative;
197 }
198
199 template<typename Real, typename Coord, typename TDataType>
200 __global__ void FSMI_BooleanOp(
201 DArray3D<Real> distance,
202 DArray3D<GridType> type,
203 DArray3D<bool> outside,
204 DistanceField3D<TDataType> fieldA,
205 DistanceField3D<TDataType> fieldB,
206 Coord origin,
207 Real dx,
208 int boolType)
209 {
210 int i = threadIdx.x + (blockIdx.x * blockDim.x);
211 int j = threadIdx.y + (blockIdx.y * blockDim.y);
212 int k = threadIdx.z + (blockIdx.z * blockDim.z);
213
214 uint nx = distance.nx();
215 uint ny = distance.ny();
216 uint nz = distance.nz();
217
218 if (i >= nx || j >= ny || k >= nz) return;
219
220 Coord point = origin + Coord(i * dx, j * dx, k * dx);
221
222 Real a;
223 Coord normal;
224 fieldA.getDistance(point, a, normal);
225
226 Real b;
227 fieldB.getDistance(point, b, normal);
228
229 Real op = FARWAY_DISTANCE;
230 switch (boolType)
231 {
232 case 0://A intersect B
233 op = a > b ? a : b;
234 type(i, j, k) = (a <= 0 && b <= 0) ? GridType::Accepted : GridType::Infinite;
235 outside(i, j, k) = (a <= 0 && b <= 0) ? true : false;
236 break;
237 case 1://A union B
238 op = a > b ? b : a;
239 type(i, j, k) = (a >= 0 && b >= 0 && op < 3.5 * dx) ? GridType::Accepted : GridType::Infinite;
240 outside(i, j, k) = (a >= 0 && b >= 0) ? true : false;
241 break;
242 case 2://A minus B
243 op = a > -b ? a : -b;
244 type(i, j, k) = (a <= 0 && -b <= 0) ? GridType::Accepted : GridType::Infinite;
245 outside(i, j, k) = (a <= 0 && -b <= 0) ? true : false;
246 break;
247 default:
248 break;
249 }
250
251 distance(i, j, k) = op;
252 }
253
254 template<typename TDataType>
255 void FastMarchingMethodGPU<TDataType>::compute()
256 {
257 auto& inA = this->inLevelSetA()->getDataPtr()->getSDF();
258 auto& inB = this->inLevelSetB()->getDataPtr()->getSDF();
259
260 if (this->outLevelSet()->isEmpty()) {
261 this->outLevelSet()->allocate();
262 }
263
264 DistanceField3D<TDataType>& out = this->outLevelSet()->getDataPtr()->getSDF();
265
266 //Calculate the bounding box
267 Coord min_box(std::numeric_limits<Real>::max(), std::numeric_limits<Real>::max(), std::numeric_limits<Real>::max());
268 Coord max_box(-std::numeric_limits<Real>::max(), -std::numeric_limits<Real>::max(), -std::numeric_limits<Real>::max());
269
270 min_box = min_box.minimum(inA.lowerBound());
271 min_box = min_box.minimum(inB.lowerBound());
272
273 max_box = max_box.maximum(inA.upperBound());
274 max_box = max_box.maximum(inB.upperBound());
275
276 //Align the bounding box with a background grid to avoid flickering artifacts
277 Real dx = this->varSpacing()->getValue();
278
279 int min_i = std::floor(min_box[0] / dx);
280 int min_j = std::floor(min_box[1] / dx);
281 int min_k = std::floor(min_box[2] / dx);
282
283 int max_i = std::floor(max_box[0] / dx);
284 int max_j = std::floor(max_box[1] / dx);
285 int max_k = std::floor(max_box[2] / dx);
286
287 int ni = max_i - min_i + 1;
288 int nj = max_j - min_j + 1;
289 int nk = max_k - min_k + 1;
290
291 min_box = Coord(min_i * dx, min_j * dx, min_k * dx);
292 max_box = Coord(max_i * dx, max_j * dx, max_k * dx);
293
294 out.setSpace(min_box, max_box, dx);
295
296 auto& phi = out.distances();
297
298 if (ni != mGridType.nx() || nj != mGridType.ny() || nk != mGridType.nz()) {
299 mGridType.resize(ni, nj, nk);
300 mOutside.resize(ni, nj, nk);
301 }
302
303 //Calculate the boolean of two distance fields
304 cuExecute3D(make_uint3(ni, nj, nk),
305 FSMI_BooleanOp,
306 phi,
307 mGridType,
308 mOutside,
309 inA,
310 inB,
311 out.lowerBound(),
312 out.getGridSpacing(),
313 this->varBoolType()->currentKey());
314
315 for (uint t = 0; t < this->varMarchingNumber()->getValue(); t++)
316 {
317 // x direction
318 cuExecute3D(make_uint3(ni - 1, nj, nk),
319 FSMI_CheckTentativeX,
320 mGridType);
321
322 // y direction
323 cuExecute3D(make_uint3(ni, nj - 1, nk),
324 FSMI_CheckTentativeY,
325 mGridType);
326
327 // z direction
328 cuExecute3D(make_uint3(ni, nj, nk - 1),
329 FSMI_CheckTentativeZ,
330 mGridType);
331
332 cuExecute3D(make_uint3(ni, nj, nk),
333 FSMI_FastMarching,
334 phi,
335 mGridType,
336 mOutside,
337 dx);
338 }
339
340 mGridType.clear();
341 mOutside.clear();
342 }
343
344 DEFINE_CLASS(FastMarchingMethodGPU);
345}