1#include "FastMarchingMethodGPU.h"
3#include "Algorithm/Reduction.h"
5#include "Collision/Distance3D.h"
9 IMPLEMENT_TCLASS(FastMarchingMethodGPU, TDataType)
11 template<typename TDataType>
12 FastMarchingMethodGPU<TDataType>::FastMarchingMethodGPU()
17 template<typename TDataType>
18 FastMarchingMethodGPU<TDataType>::~FastMarchingMethodGPU()
22 __device__ void FSM_SWAP(
31 __device__ void UpdatePhi(
33 DArray3D<GridType>& type,
34 DArray3D<bool>& outside,
35 uint i, uint j, uint k,
38 bool outside_ijk = outside(i, j, k);
44 Real phi_minx, phi_miny, phi_minz;
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));
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));
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));
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]);
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]);
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];
81 Real sign = outside_ijk ? Real(1) : Real(-1);
83 if (glm::abs(a[0] - a[2]) < dx)
85 phi_ijk = (2 * sum_a + sign * glm::sqrt(4 * sum_a * sum_a - 12 * (sum_a2 - dx * dx))) / Real(6);
87 else if (glm::abs(a[0] - a[1]) < dx)
89 phi_ijk = 0.5 * (a[0] + a[1] + sign * glm::sqrt(2 * dx * dx - (a[0] - a[1]) * (a[0] - a[1])));
93 phi_ijk = a[0] + sign * dx;
96 Real phi_ijk_old = phi(i, j, k);
98 phi(i, j, k) = phi_ijk;
101 if (glm::abs(phi_ijk_old - phi_ijk) < EPSILON)
103 type(i, j, k) = GridType::Accepted;
107 template<typename Real>
108 __global__ void FSMI_FastMarching(
110 DArray3D<GridType> pointType,
111 DArray3D<bool> outside,
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);
122 if (i > nx || j >= ny || k >= nz) return;
124 if (pointType(i, j, k) != GridType::Tentative)
127 UpdatePhi(phi, pointType, outside, i, j, k, dx);
130 __global__ void FSMI_CheckTentativeX(
131 DArray3D<GridType> pointType)
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);
137 uint nx = pointType.nx();
138 uint ny = pointType.ny();
139 uint nz = pointType.nz();
141 if (i >= nx - 1 || j >= ny || k >= nz) return;
143 GridType type0 = pointType(i, j, k);
144 GridType type1 = pointType(i + 1, j, k);
146 if (type0 == GridType::Accepted && type1 != GridType::Accepted)
147 pointType(i + 1, j, k) = GridType::Tentative;
149 if (type0 != GridType::Accepted && type1 == GridType::Accepted)
150 pointType(i, j, k) = GridType::Tentative;
153 __global__ void FSMI_CheckTentativeY(
154 DArray3D<GridType> pointType)
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);
160 uint nx = pointType.nx();
161 uint ny = pointType.ny();
162 uint nz = pointType.nz();
164 if (i >= nx || j >= ny - 1 || k >= nz) return;
166 GridType type0 = pointType(i, j, k);
167 GridType type1 = pointType(i, j + 1, k);
169 if (type0 == GridType::Accepted && type1 != GridType::Accepted)
170 pointType(i, j + 1, k) = GridType::Tentative;
172 if (type0 != GridType::Accepted && type1 == GridType::Accepted)
173 pointType(i, j, k) = GridType::Tentative;
176 __global__ void FSMI_CheckTentativeZ(
177 DArray3D<GridType> pointType)
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);
183 uint nx = pointType.nx();
184 uint ny = pointType.ny();
185 uint nz = pointType.nz();
187 if (i >= nx || j >= ny || k >= nz - 1) return;
189 GridType type0 = pointType(i, j, k);
190 GridType type1 = pointType(i, j, k + 1);
192 if (type0 == GridType::Accepted && type1 != GridType::Accepted)
193 pointType(i, j, k + 1) = GridType::Tentative;
195 if (type0 != GridType::Accepted && type1 == GridType::Accepted)
196 pointType(i, j, k) = GridType::Tentative;
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,
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);
214 uint nx = distance.nx();
215 uint ny = distance.ny();
216 uint nz = distance.nz();
218 if (i >= nx || j >= ny || k >= nz) return;
220 Coord point = origin + Coord(i * dx, j * dx, k * dx);
224 fieldA.getDistance(point, a, normal);
227 fieldB.getDistance(point, b, normal);
229 Real op = FARWAY_DISTANCE;
232 case 0://A intersect 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;
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;
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;
251 distance(i, j, k) = op;
254 template<typename TDataType>
255 void FastMarchingMethodGPU<TDataType>::compute()
257 auto& inA = this->inLevelSetA()->getDataPtr()->getSDF();
258 auto& inB = this->inLevelSetB()->getDataPtr()->getSDF();
260 if (this->outLevelSet()->isEmpty()) {
261 this->outLevelSet()->allocate();
264 DistanceField3D<TDataType>& out = this->outLevelSet()->getDataPtr()->getSDF();
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());
270 min_box = min_box.minimum(inA.lowerBound());
271 min_box = min_box.minimum(inB.lowerBound());
273 max_box = max_box.maximum(inA.upperBound());
274 max_box = max_box.maximum(inB.upperBound());
276 //Align the bounding box with a background grid to avoid flickering artifacts
277 Real dx = this->varSpacing()->getValue();
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);
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);
287 int ni = max_i - min_i + 1;
288 int nj = max_j - min_j + 1;
289 int nk = max_k - min_k + 1;
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);
294 out.setSpace(min_box, max_box, dx);
296 auto& phi = out.distances();
298 if (ni != mGridType.nx() || nj != mGridType.ny() || nk != mGridType.nz()) {
299 mGridType.resize(ni, nj, nk);
300 mOutside.resize(ni, nj, nk);
303 //Calculate the boolean of two distance fields
304 cuExecute3D(make_uint3(ni, nj, nk),
312 out.getGridSpacing(),
313 this->varBoolType()->currentKey());
315 for (uint t = 0; t < this->varMarchingNumber()->getValue(); t++)
318 cuExecute3D(make_uint3(ni - 1, nj, nk),
319 FSMI_CheckTentativeX,
323 cuExecute3D(make_uint3(ni, nj - 1, nk),
324 FSMI_CheckTentativeY,
328 cuExecute3D(make_uint3(ni, nj, nk - 1),
329 FSMI_CheckTentativeZ,
332 cuExecute3D(make_uint3(ni, nj, nk),
344 DEFINE_CLASS(FastMarchingMethodGPU);