1 /*
2 * Copyright (c) 2011-2021, The DART development contributors
3 * All rights reserved.
4 *
5 * The list of contributors can be found at:
6 * https://github.com/dartsim/dart/blob/master/LICENSE
7 *
8 * This file is provided under the following "BSD-style" License:
9 * Redistribution and use in source and binary forms, with or
10 * without modification, are permitted provided that the following
11 * conditions are met:
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "dart/collision/fcl/FCLCollisionDetector.hpp"
34
35 #include <assimp/scene.h>
36
37 #include "dart/collision/CollisionFilter.hpp"
38 #include "dart/collision/CollisionObject.hpp"
39 #include "dart/collision/DistanceFilter.hpp"
40 #include "dart/collision/fcl/FCLCollisionGroup.hpp"
41 #include "dart/collision/fcl/FCLCollisionObject.hpp"
42 #include "dart/collision/fcl/FCLTypes.hpp"
43 #include "dart/collision/fcl/tri_tri_intersection_test.hpp"
44 #include "dart/common/Console.hpp"
45 #include "dart/dynamics/BoxShape.hpp"
46 #include "dart/dynamics/ConeShape.hpp"
47 #include "dart/dynamics/CylinderShape.hpp"
48 #include "dart/dynamics/EllipsoidShape.hpp"
49 #include "dart/dynamics/MeshShape.hpp"
50 #include "dart/dynamics/PlaneShape.hpp"
51 #include "dart/dynamics/PyramidShape.hpp"
52 #include "dart/dynamics/Shape.hpp"
53 #include "dart/dynamics/ShapeFrame.hpp"
54 #include "dart/dynamics/SoftMeshShape.hpp"
55 #include "dart/dynamics/SphereShape.hpp"
56 #include "dart/dynamics/VoxelGridShape.hpp"
57
58 namespace dart {
59 namespace collision {
60
61 namespace {
62
63 bool collisionCallback(
64 fcl::CollisionObject* o1, fcl::CollisionObject* o2, void* cdata);
65
66 bool distanceCallback(
67 fcl::CollisionObject* o1,
68 fcl::CollisionObject* o2,
69 void* cdata,
70 double& dist);
71
72 void postProcessFCL(
73 const fcl::CollisionResult& fclResult,
74 fcl::CollisionObject* o1,
75 fcl::CollisionObject* o2,
76 const CollisionOption& option,
77 CollisionResult& result);
78
79 void postProcessDART(
80 const fcl::CollisionResult& fclResult,
81 fcl::CollisionObject* o1,
82 fcl::CollisionObject* o2,
83 const CollisionOption& option,
84 CollisionResult& result);
85
86 void interpreteDistanceResult(
87 const fcl::DistanceResult& fclResult,
88 fcl::CollisionObject* o1,
89 fcl::CollisionObject* o2,
90 const DistanceOption& option,
91 DistanceResult& result);
92
93 int evalContactPosition(
94 const fcl::Contact& fclContact,
95 const ::fcl::BVHModel<fcl::OBBRSS>& mesh1,
96 const ::fcl::BVHModel<fcl::OBBRSS>& mesh2,
97 const fcl::Transform3& transform1,
98 const fcl::Transform3& transform2,
99 Eigen::Vector3d& contactPosition1,
100 Eigen::Vector3d& contactPosition2);
101
102 Eigen::Vector3d getDiff(const Contact& contact1, const Contact& contact2);
103
104 fcl::Vector3 getDiff(
105 const fcl::Contact& contact1, const fcl::Contact& contact2);
106
107 bool isColinear(
108 const Contact& contact1,
109 const Contact& contact2,
110 const Contact& contact3,
111 double tol);
112
113 bool isColinear(
114 const fcl::Contact& contact1,
115 const fcl::Contact& contact2,
116 const fcl::Contact& contact3,
117 double tol);
118
119 template <typename T>
120 bool isColinear(const T& pos1, const T& pos2, const T& pos3, double tol);
121
122 int FFtest(
123 const fcl::Vector3& r1,
124 const fcl::Vector3& r2,
125 const fcl::Vector3& r3,
126 const fcl::Vector3& R1,
127 const fcl::Vector3& R2,
128 const fcl::Vector3& R3,
129 fcl::Vector3* res1,
130 fcl::Vector3* res2);
131
132 double triArea(
133 const fcl::Vector3& p1, const fcl::Vector3& p2, const fcl::Vector3& p3);
134
135 void convertOption(
136 const CollisionOption& option, fcl::CollisionRequest& request);
137
138 void convertOption(const DistanceOption& option, fcl::DistanceRequest& request);
139
140 Contact convertContact(
141 const fcl::Contact& fclContact,
142 fcl::CollisionObject* o1,
143 fcl::CollisionObject* o2,
144 const CollisionOption& option);
145
146 /// Collision data stores the collision request and the result given by
147 /// collision algorithm.
148 struct FCLCollisionCallbackData
149 {
150 /// FCL collision request
151 fcl::CollisionRequest fclRequest;
152
153 /// FCL collision result
154 fcl::CollisionResult fclResult;
155
156 /// Collision option of DART
157 const CollisionOption& option;
158
159 /// Collision result of DART
160 CollisionResult* result;
161
162 /// True if at least one contact is found. This flag is used only when
163 /// mResult is nullptr; otherwise the actual collision result is in mResult.
164 bool foundCollision;
165
166 FCLCollisionDetector::PrimitiveShape primitiveShapeType;
167
168 FCLCollisionDetector::ContactPointComputationMethod
169 contactPointComputationMethod;
170
171 /// Whether the collision iteration can stop
172 bool done;
173
isCollisiondart::collision::__anonebd856190111::FCLCollisionCallbackData174 bool isCollision() const
175 {
176 if (result)
177 return result->isCollision();
178 else
179 return foundCollision;
180 }
181
182 /// Constructor
FCLCollisionCallbackDatadart::collision::__anonebd856190111::FCLCollisionCallbackData183 FCLCollisionCallbackData(
184 const CollisionOption& option,
185 CollisionResult* result,
186 FCLCollisionDetector::PrimitiveShape type = FCLCollisionDetector::MESH,
187 FCLCollisionDetector::ContactPointComputationMethod method
188 = FCLCollisionDetector::DART)
189 : option(option),
190 result(result),
191 foundCollision(false),
192 primitiveShapeType(type),
193 contactPointComputationMethod(method),
194 done(false)
195 {
196 convertOption(option, fclRequest);
197
198 fclRequest.num_max_contacts
199 = std::max(static_cast<std::size_t>(100u), option.maxNumContacts);
200 // Since some contact points can be filtered out in the post process, we ask
201 // more than the demend. 100 is randomly picked.
202 }
203 };
204
205 struct FCLDistanceCallbackData
206 {
207 /// FCL distance request
208 fcl::DistanceRequest fclRequest;
209
210 /// FCL distance result
211 fcl::DistanceResult fclResult;
212
213 /// Distance option of DART
214 const DistanceOption& option;
215
216 /// Minimum distance identical to DistanceResult::minDistnace if result is not
217 /// nullptr.
218 double unclampedMinDistance;
219
220 /// Distance result of DART
221 DistanceResult* result;
222
223 /// @brief Whether the distance iteration can stop
224 bool done;
225
FCLDistanceCallbackDatadart::collision::__anonebd856190111::FCLDistanceCallbackData226 FCLDistanceCallbackData(const DistanceOption& option, DistanceResult* result)
227 : option(option), result(result), done(false)
228 {
229 convertOption(option, fclRequest);
230 }
231 };
232
233 //==============================================================================
234 // Create a cube mesh for collision detection
235 template <class BV>
createCube(float _sizeX,float _sizeY,float _sizeZ)236 ::fcl::BVHModel<BV>* createCube(float _sizeX, float _sizeY, float _sizeZ)
237 {
238 int faces[6][4] = {{0, 1, 2, 3},
239 {3, 2, 6, 7},
240 {7, 6, 5, 4},
241 {4, 5, 1, 0},
242 {5, 6, 2, 1},
243 {7, 4, 0, 3}};
244 float v[8][3];
245
246 v[0][0] = v[1][0] = v[2][0] = v[3][0] = -_sizeX / 2;
247 v[4][0] = v[5][0] = v[6][0] = v[7][0] = _sizeX / 2;
248 v[0][1] = v[1][1] = v[4][1] = v[5][1] = -_sizeY / 2;
249 v[2][1] = v[3][1] = v[6][1] = v[7][1] = _sizeY / 2;
250 v[0][2] = v[3][2] = v[4][2] = v[7][2] = -_sizeZ / 2;
251 v[1][2] = v[2][2] = v[5][2] = v[6][2] = _sizeZ / 2;
252
253 ::fcl::BVHModel<BV>* model = new ::fcl::BVHModel<BV>;
254 fcl::Vector3 p1, p2, p3;
255 model->beginModel();
256
257 for (int i = 0; i < 6; i++)
258 {
259 p1 = fcl::Vector3(v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2]);
260 p2 = fcl::Vector3(v[faces[i][1]][0], v[faces[i][1]][1], v[faces[i][1]][2]);
261 p3 = fcl::Vector3(v[faces[i][2]][0], v[faces[i][2]][1], v[faces[i][2]][2]);
262 model->addTriangle(p1, p2, p3);
263
264 p1 = fcl::Vector3(v[faces[i][0]][0], v[faces[i][0]][1], v[faces[i][0]][2]);
265 p2 = fcl::Vector3(v[faces[i][2]][0], v[faces[i][2]][1], v[faces[i][2]][2]);
266 p3 = fcl::Vector3(v[faces[i][3]][0], v[faces[i][3]][1], v[faces[i][3]][2]);
267 model->addTriangle(p1, p2, p3);
268 }
269 model->endModel();
270 return model;
271 }
272
273 //==============================================================================
274 template <class BV>
createEllipsoid(float _sizeX,float _sizeY,float _sizeZ)275 ::fcl::BVHModel<BV>* createEllipsoid(float _sizeX, float _sizeY, float _sizeZ)
276 {
277 float v[59][3] = {{0, 0, 0},
278 {0.135299, -0.461940, -0.135299},
279 {0.000000, -0.461940, -0.191342},
280 {-0.135299, -0.461940, -0.135299},
281 {-0.191342, -0.461940, 0.000000},
282 {-0.135299, -0.461940, 0.135299},
283 {0.000000, -0.461940, 0.191342},
284 {0.135299, -0.461940, 0.135299},
285 {0.191342, -0.461940, 0.000000},
286 {0.250000, -0.353553, -0.250000},
287 {0.000000, -0.353553, -0.353553},
288 {-0.250000, -0.353553, -0.250000},
289 {-0.353553, -0.353553, 0.000000},
290 {-0.250000, -0.353553, 0.250000},
291 {0.000000, -0.353553, 0.353553},
292 {0.250000, -0.353553, 0.250000},
293 {0.353553, -0.353553, 0.000000},
294 {0.326641, -0.191342, -0.326641},
295 {0.000000, -0.191342, -0.461940},
296 {-0.326641, -0.191342, -0.326641},
297 {-0.461940, -0.191342, 0.000000},
298 {-0.326641, -0.191342, 0.326641},
299 {0.000000, -0.191342, 0.461940},
300 {0.326641, -0.191342, 0.326641},
301 {0.461940, -0.191342, 0.000000},
302 {0.353553, 0.000000, -0.353553},
303 {0.000000, 0.000000, -0.500000},
304 {-0.353553, 0.000000, -0.353553},
305 {-0.500000, 0.000000, 0.000000},
306 {-0.353553, 0.000000, 0.353553},
307 {0.000000, 0.000000, 0.500000},
308 {0.353553, 0.000000, 0.353553},
309 {0.500000, 0.000000, 0.000000},
310 {0.326641, 0.191342, -0.326641},
311 {0.000000, 0.191342, -0.461940},
312 {-0.326641, 0.191342, -0.326641},
313 {-0.461940, 0.191342, 0.000000},
314 {-0.326641, 0.191342, 0.326641},
315 {0.000000, 0.191342, 0.461940},
316 {0.326641, 0.191342, 0.326641},
317 {0.461940, 0.191342, 0.000000},
318 {0.250000, 0.353553, -0.250000},
319 {0.000000, 0.353553, -0.353553},
320 {-0.250000, 0.353553, -0.250000},
321 {-0.353553, 0.353553, 0.000000},
322 {-0.250000, 0.353553, 0.250000},
323 {0.000000, 0.353553, 0.353553},
324 {0.250000, 0.353553, 0.250000},
325 {0.353553, 0.353553, 0.000000},
326 {0.135299, 0.461940, -0.135299},
327 {0.000000, 0.461940, -0.191342},
328 {-0.135299, 0.461940, -0.135299},
329 {-0.191342, 0.461940, 0.000000},
330 {-0.135299, 0.461940, 0.135299},
331 {0.000000, 0.461940, 0.191342},
332 {0.135299, 0.461940, 0.135299},
333 {0.191342, 0.461940, 0.000000},
334 {0.000000, -0.500000, 0.000000},
335 {0.000000, 0.500000, 0.000000}};
336
337 int f[112][3]
338 = {{1, 2, 9}, {9, 2, 10}, {2, 3, 10}, {10, 3, 11}, {3, 4, 11},
339 {11, 4, 12}, {4, 5, 12}, {12, 5, 13}, {5, 6, 13}, {13, 6, 14},
340 {6, 7, 14}, {14, 7, 15}, {7, 8, 15}, {15, 8, 16}, {8, 1, 16},
341 {16, 1, 9}, {9, 10, 17}, {17, 10, 18}, {10, 11, 18}, {18, 11, 19},
342 {11, 12, 19}, {19, 12, 20}, {12, 13, 20}, {20, 13, 21}, {13, 14, 21},
343 {21, 14, 22}, {14, 15, 22}, {22, 15, 23}, {15, 16, 23}, {23, 16, 24},
344 {16, 9, 24}, {24, 9, 17}, {17, 18, 25}, {25, 18, 26}, {18, 19, 26},
345 {26, 19, 27}, {19, 20, 27}, {27, 20, 28}, {20, 21, 28}, {28, 21, 29},
346 {21, 22, 29}, {29, 22, 30}, {22, 23, 30}, {30, 23, 31}, {23, 24, 31},
347 {31, 24, 32}, {24, 17, 32}, {32, 17, 25}, {25, 26, 33}, {33, 26, 34},
348 {26, 27, 34}, {34, 27, 35}, {27, 28, 35}, {35, 28, 36}, {28, 29, 36},
349 {36, 29, 37}, {29, 30, 37}, {37, 30, 38}, {30, 31, 38}, {38, 31, 39},
350 {31, 32, 39}, {39, 32, 40}, {32, 25, 40}, {40, 25, 33}, {33, 34, 41},
351 {41, 34, 42}, {34, 35, 42}, {42, 35, 43}, {35, 36, 43}, {43, 36, 44},
352 {36, 37, 44}, {44, 37, 45}, {37, 38, 45}, {45, 38, 46}, {38, 39, 46},
353 {46, 39, 47}, {39, 40, 47}, {47, 40, 48}, {40, 33, 48}, {48, 33, 41},
354 {41, 42, 49}, {49, 42, 50}, {42, 43, 50}, {50, 43, 51}, {43, 44, 51},
355 {51, 44, 52}, {44, 45, 52}, {52, 45, 53}, {45, 46, 53}, {53, 46, 54},
356 {46, 47, 54}, {54, 47, 55}, {47, 48, 55}, {55, 48, 56}, {48, 41, 56},
357 {56, 41, 49}, {2, 1, 57}, {3, 2, 57}, {4, 3, 57}, {5, 4, 57},
358 {6, 5, 57}, {7, 6, 57}, {8, 7, 57}, {1, 8, 57}, {49, 50, 58},
359 {50, 51, 58}, {51, 52, 58}, {52, 53, 58}, {53, 54, 58}, {54, 55, 58},
360 {55, 56, 58}, {56, 49, 58}};
361
362 ::fcl::BVHModel<BV>* model = new ::fcl::BVHModel<BV>;
363 fcl::Vector3 p1, p2, p3;
364 model->beginModel();
365
366 for (int i = 0; i < 112; i++)
367 {
368 p1 = fcl::Vector3(
369 v[f[i][0]][0] * _sizeX, v[f[i][0]][1] * _sizeY, v[f[i][0]][2] * _sizeZ);
370 p2 = fcl::Vector3(
371 v[f[i][1]][0] * _sizeX, v[f[i][1]][1] * _sizeY, v[f[i][1]][2] * _sizeZ);
372 p3 = fcl::Vector3(
373 v[f[i][2]][0] * _sizeX, v[f[i][2]][1] * _sizeY, v[f[i][2]][2] * _sizeZ);
374
375 model->addTriangle(p1, p2, p3);
376 }
377
378 model->endModel();
379
380 return model;
381 }
382
383 //==============================================================================
384 template <class BV>
createCylinder(double _baseRadius,double _topRadius,double _height,int _slices,int _stacks)385 ::fcl::BVHModel<BV>* createCylinder(
386 double _baseRadius,
387 double _topRadius,
388 double _height,
389 int _slices,
390 int _stacks)
391 {
392 const int CACHE_SIZE = 240;
393
394 int i, j;
395 float sinCache[CACHE_SIZE];
396 float cosCache[CACHE_SIZE];
397 float angle;
398 float zBase;
399 float zLow, zHigh;
400 float sintemp, costemp;
401 float deltaRadius;
402 float radiusLow, radiusHigh;
403
404 if (_slices >= CACHE_SIZE)
405 _slices = CACHE_SIZE - 1;
406
407 if (_slices < 2 || _stacks < 1 || _baseRadius < 0.0 || _topRadius < 0.0
408 || _height < 0.0)
409 {
410 return nullptr;
411 }
412
413 /* Center at CoM */
414 zBase = -_height / 2;
415
416 /* Compute delta */
417 deltaRadius = _baseRadius - _topRadius;
418
419 /* Cache is the vertex locations cache */
420 for (i = 0; i < _slices; i++)
421 {
422 angle = 2 * math::constantsd::pi() * i / _slices;
423 sinCache[i] = sin(angle);
424 cosCache[i] = cos(angle);
425 }
426
427 sinCache[_slices] = sinCache[0];
428 cosCache[_slices] = cosCache[0];
429
430 ::fcl::BVHModel<BV>* model = new ::fcl::BVHModel<BV>;
431 fcl::Vector3 p1, p2, p3, p4;
432
433 model->beginModel();
434
435 /* Base of cylinder */
436 sintemp = sinCache[0];
437 costemp = cosCache[0];
438 radiusLow = _baseRadius;
439 zLow = zBase;
440 p1 = fcl::Vector3(radiusLow * sintemp, radiusLow * costemp, zLow);
441 for (i = 1; i < _slices; i++)
442 {
443 p2 = fcl::Vector3(radiusLow * sinCache[i], radiusLow * cosCache[i], zLow);
444 p3 = fcl::Vector3(
445 radiusLow * sinCache[i + 1], radiusLow * cosCache[i + 1], zLow);
446 model->addTriangle(p1, p2, p3);
447 }
448
449 /* Body of cylinder */
450 for (i = 0; i < _slices; i++)
451 {
452 for (j = 0; j < _stacks; j++)
453 {
454 zLow = j * _height / _stacks + zBase;
455 zHigh = (j + 1) * _height / _stacks + zBase;
456 radiusLow = _baseRadius - deltaRadius * (static_cast<float>(j) / _stacks);
457 radiusHigh
458 = _baseRadius - deltaRadius * (static_cast<float>(j + 1) / _stacks);
459
460 p1 = fcl::Vector3(radiusLow * sinCache[i], radiusLow * cosCache[i], zLow);
461 p2 = fcl::Vector3(
462 radiusLow * sinCache[i + 1], radiusLow * cosCache[i + 1], zLow);
463 p3 = fcl::Vector3(
464 radiusHigh * sinCache[i], radiusHigh * cosCache[i], zHigh);
465 p4 = fcl::Vector3(
466 radiusHigh * sinCache[i + 1], radiusHigh * cosCache[i + 1], zHigh);
467
468 model->addTriangle(p1, p2, p3);
469 model->addTriangle(p2, p3, p4);
470 }
471 }
472
473 /* Top of cylinder */
474 sintemp = sinCache[0];
475 costemp = cosCache[0];
476 radiusLow = _topRadius;
477 zLow = zBase + _height;
478 p1 = fcl::Vector3(radiusLow * sintemp, radiusLow * costemp, zLow);
479 for (i = 1; i < _slices; i++)
480 {
481 p2 = fcl::Vector3(radiusLow * sinCache[i], radiusLow * cosCache[i], zLow);
482 p3 = fcl::Vector3(
483 radiusLow * sinCache[i + 1], radiusLow * cosCache[i + 1], zLow);
484 model->addTriangle(p1, p2, p3);
485 }
486
487 model->endModel();
488 return model;
489 }
490
491 //==============================================================================
492 template <typename BV>
createPyramid(const dynamics::PyramidShape & shape,const fcl::Transform3 & pose)493 ::fcl::BVHModel<BV>* createPyramid(
494 const dynamics::PyramidShape& shape, const fcl::Transform3& pose)
495 {
496 ::fcl::BVHModel<BV>* model = new ::fcl::BVHModel<BV>;
497
498 std::vector<fcl::Vector3> points(5);
499 std::vector<::fcl::Triangle> faces(6);
500
501 const double w = shape.getBaseWidth();
502 const double d = shape.getBaseDepth();
503 const double h = shape.getHeight();
504
505 const double hTop = h / 2;
506 const double hBottom = -hTop;
507 const double left = -w / 2;
508 const double right = w / 2;
509 const double front = -d / 2;
510 const double back = d / 2;
511
512 #if FCL_VERSION_AT_LEAST(0, 6, 0)
513 points[0] << 0, 0, hTop;
514 points[1] << right, back, hBottom;
515 points[2] << left, back, hBottom;
516 points[3] << left, front, hBottom;
517 points[4] << right, front, hBottom;
518 #else
519 points[0].setValue(0, 0, hTop);
520 points[1].setValue(right, back, hBottom);
521 points[2].setValue(left, back, hBottom);
522 points[3].setValue(left, front, hBottom);
523 points[4].setValue(right, front, hBottom);
524 #endif
525
526 faces[0].set(0, 1, 2);
527 faces[1].set(0, 2, 3);
528 faces[2].set(0, 3, 4);
529 faces[3].set(0, 4, 1);
530 faces[4].set(1, 3, 2);
531 faces[5].set(1, 4, 3);
532
533 for (unsigned int i = 0; i < points.size(); ++i)
534 {
535 #if FCL_VERSION_AT_LEAST(0, 6, 0)
536 points[i] = pose * points[i];
537 #else
538 points[i] = pose.transform(points[i]);
539 #endif
540 }
541
542 model->beginModel();
543 model->addSubModel(points, faces);
544 model->endModel();
545 model->computeLocalAABB();
546
547 return model;
548 }
549
550 //==============================================================================
551 template <class BV>
createMesh(float _scaleX,float _scaleY,float _scaleZ,const aiScene * _mesh)552 ::fcl::BVHModel<BV>* createMesh(
553 float _scaleX, float _scaleY, float _scaleZ, const aiScene* _mesh)
554 {
555 // Create FCL mesh from Assimp mesh
556
557 assert(_mesh);
558 ::fcl::BVHModel<BV>* model = new ::fcl::BVHModel<BV>;
559 model->beginModel();
560 for (std::size_t i = 0; i < _mesh->mNumMeshes; i++)
561 {
562 for (std::size_t j = 0; j < _mesh->mMeshes[i]->mNumFaces; j++)
563 {
564 fcl::Vector3 vertices[3];
565 for (std::size_t k = 0; k < 3; k++)
566 {
567 const aiVector3D& vertex
568 = _mesh->mMeshes[i]
569 ->mVertices[_mesh->mMeshes[i]->mFaces[j].mIndices[k]];
570 vertices[k] = fcl::Vector3(
571 vertex.x * _scaleX, vertex.y * _scaleY, vertex.z * _scaleZ);
572 }
573 model->addTriangle(vertices[0], vertices[1], vertices[2]);
574 }
575 }
576 model->endModel();
577 return model;
578 }
579
580 //==============================================================================
581 template <class BV>
createSoftMesh(const aiMesh * _mesh)582 ::fcl::BVHModel<BV>* createSoftMesh(const aiMesh* _mesh)
583 {
584 // Create FCL mesh from Assimp mesh
585
586 assert(_mesh);
587 ::fcl::BVHModel<BV>* model = new ::fcl::BVHModel<BV>;
588 model->beginModel();
589
590 for (std::size_t i = 0; i < _mesh->mNumFaces; i++)
591 {
592 fcl::Vector3 vertices[3];
593 for (std::size_t j = 0; j < 3; j++)
594 {
595 const aiVector3D& vertex = _mesh->mVertices[_mesh->mFaces[i].mIndices[j]];
596 vertices[j] = fcl::Vector3(vertex.x, vertex.y, vertex.z);
597 }
598 model->addTriangle(vertices[0], vertices[1], vertices[2]);
599 }
600
601 model->endModel();
602 return model;
603 }
604
605 } // anonymous namespace
606
607 //==============================================================================
608 FCLCollisionDetector::Registrar<FCLCollisionDetector>
609 FCLCollisionDetector::mRegistrar{
610 FCLCollisionDetector::getStaticType(),
__anonebd856190202() 611 []() -> std::shared_ptr<FCLCollisionDetector> {
612 return FCLCollisionDetector::create();
613 }};
614
615 //==============================================================================
create()616 std::shared_ptr<FCLCollisionDetector> FCLCollisionDetector::create()
617 {
618 return std::shared_ptr<FCLCollisionDetector>(new FCLCollisionDetector());
619 }
620
621 //==============================================================================
~FCLCollisionDetector()622 FCLCollisionDetector::~FCLCollisionDetector()
623 {
624 assert(mShapeMap.empty());
625 }
626
627 //==============================================================================
628 std::shared_ptr<CollisionDetector>
cloneWithoutCollisionObjects() const629 FCLCollisionDetector::cloneWithoutCollisionObjects() const
630 {
631 return FCLCollisionDetector::create();
632 }
633
634 //==============================================================================
getType() const635 const std::string& FCLCollisionDetector::getType() const
636 {
637 return getStaticType();
638 }
639
640 //==============================================================================
getStaticType()641 const std::string& FCLCollisionDetector::getStaticType()
642 {
643 static const std::string type = "fcl";
644 return type;
645 }
646
647 //==============================================================================
createCollisionGroup()648 std::unique_ptr<CollisionGroup> FCLCollisionDetector::createCollisionGroup()
649 {
650 return std::make_unique<FCLCollisionGroup>(shared_from_this());
651 }
652
653 //==============================================================================
checkGroupValidity(FCLCollisionDetector * cd,CollisionGroup * group)654 static bool checkGroupValidity(FCLCollisionDetector* cd, CollisionGroup* group)
655 {
656 if (cd != group->getCollisionDetector().get())
657 {
658 dterr << "[FCLCollisionDetector::collide] Attempting to check collision "
659 << "for a collision group that is created from a different collision "
660 << "detector instance.\n";
661
662 return false;
663 }
664
665 return true;
666 }
667
668 //==============================================================================
collide(CollisionGroup * group,const CollisionOption & option,CollisionResult * result)669 bool FCLCollisionDetector::collide(
670 CollisionGroup* group,
671 const CollisionOption& option,
672 CollisionResult* result)
673 {
674 if (result)
675 result->clear();
676
677 if (0u == option.maxNumContacts)
678 return false;
679
680 if (!checkGroupValidity(this, group))
681 return false;
682
683 auto casted = static_cast<FCLCollisionGroup*>(group);
684 casted->updateEngineData();
685
686 FCLCollisionCallbackData collData(
687 option, result, mPrimitiveShapeType, mContactPointComputationMethod);
688
689 const auto* collMgr = casted->getFCLCollisionManager();
690 assert(collMgr);
691 collMgr->collide(&collData, collisionCallback);
692
693 return collData.isCollision();
694 }
695
696 //==============================================================================
collide(CollisionGroup * group1,CollisionGroup * group2,const CollisionOption & option,CollisionResult * result)697 bool FCLCollisionDetector::collide(
698 CollisionGroup* group1,
699 CollisionGroup* group2,
700 const CollisionOption& option,
701 CollisionResult* result)
702 {
703 if (result)
704 result->clear();
705
706 if (0u == option.maxNumContacts)
707 return false;
708
709 if (!checkGroupValidity(this, group1))
710 return false;
711
712 if (!checkGroupValidity(this, group2))
713 return false;
714
715 auto casted1 = static_cast<FCLCollisionGroup*>(group1);
716 auto casted2 = static_cast<FCLCollisionGroup*>(group2);
717 casted1->updateEngineData();
718 casted2->updateEngineData();
719
720 FCLCollisionCallbackData collData(
721 option, result, mPrimitiveShapeType, mContactPointComputationMethod);
722
723 auto broadPhaseAlg1 = casted1->getFCLCollisionManager();
724 auto broadPhaseAlg2 = casted2->getFCLCollisionManager();
725
726 broadPhaseAlg1->collide(broadPhaseAlg2, &collData, collisionCallback);
727
728 return collData.isCollision();
729 }
730
731 //==============================================================================
distance(CollisionGroup * group,const DistanceOption & option,DistanceResult * result)732 double FCLCollisionDetector::distance(
733 CollisionGroup* group, const DistanceOption& option, DistanceResult* result)
734 {
735 if (result)
736 result->clear();
737
738 if (!checkGroupValidity(this, group))
739 return 0.0;
740
741 auto casted = static_cast<FCLCollisionGroup*>(group);
742 casted->updateEngineData();
743
744 FCLDistanceCallbackData distData(option, result);
745
746 casted->getFCLCollisionManager()->distance(&distData, distanceCallback);
747
748 return std::max(distData.unclampedMinDistance, option.distanceLowerBound);
749 }
750
751 //==============================================================================
distance(CollisionGroup * group1,CollisionGroup * group2,const DistanceOption & option,DistanceResult * result)752 double FCLCollisionDetector::distance(
753 CollisionGroup* group1,
754 CollisionGroup* group2,
755 const DistanceOption& option,
756 DistanceResult* result)
757 {
758 if (result)
759 result->clear();
760
761 if (!checkGroupValidity(this, group1))
762 return 0.0;
763
764 if (!checkGroupValidity(this, group2))
765 return 0.0;
766
767 auto casted1 = static_cast<FCLCollisionGroup*>(group1);
768 auto casted2 = static_cast<FCLCollisionGroup*>(group2);
769 casted1->updateEngineData();
770 casted2->updateEngineData();
771
772 FCLDistanceCallbackData distData(option, result);
773
774 auto broadPhaseAlg1 = casted1->getFCLCollisionManager();
775 auto broadPhaseAlg2 = casted2->getFCLCollisionManager();
776
777 broadPhaseAlg1->distance(broadPhaseAlg2, &distData, distanceCallback);
778
779 return std::max(distData.unclampedMinDistance, option.distanceLowerBound);
780 }
781
782 //==============================================================================
setPrimitiveShapeType(FCLCollisionDetector::PrimitiveShape type)783 void FCLCollisionDetector::setPrimitiveShapeType(
784 FCLCollisionDetector::PrimitiveShape type)
785 {
786 if (type == PRIMITIVE)
787 {
788 dtwarn << "[FCLCollisionDetector::setPrimitiveShapeType] You chose to use "
789 << "FCL's primitive shape collision feature while it's not complete "
790 << "(at least until 0.4.0) especially in use of dynamics "
791 << "simulation. It's recommended to use mesh even for primitive "
792 << "shapes by settting "
793 << "FCLCollisionDetector::setPrimitiveShapeType(MESH).\n";
794 }
795
796 mPrimitiveShapeType = type;
797 }
798
799 //==============================================================================
800 FCLCollisionDetector::PrimitiveShape
getPrimitiveShapeType() const801 FCLCollisionDetector::getPrimitiveShapeType() const
802 {
803 return mPrimitiveShapeType;
804 }
805
806 //==============================================================================
setContactPointComputationMethod(FCLCollisionDetector::ContactPointComputationMethod method)807 void FCLCollisionDetector::setContactPointComputationMethod(
808 FCLCollisionDetector::ContactPointComputationMethod method)
809 {
810 if (method == FCL)
811 {
812 dtwarn << "[FCLCollisionDetector::setContactPointComputationMethod] You "
813 << "chose to use FCL's built in contact point computation while"
814 << "it's buggy (see https://github.com/flexible-collision-library/"
815 << "fcl/issues/106) at least until 0.4.0. It's recommended to use "
816 << "DART's implementation for the contact point computation by "
817 << "setting "
818 << "FCLCollisionDetector::setContactPointComputationMethod(DART).\n";
819 }
820
821 mContactPointComputationMethod = method;
822 }
823
824 //==============================================================================
825 FCLCollisionDetector::ContactPointComputationMethod
getContactPointComputationMethod() const826 FCLCollisionDetector::getContactPointComputationMethod() const
827 {
828 return mContactPointComputationMethod;
829 }
830
831 //==============================================================================
FCLCollisionDetector()832 FCLCollisionDetector::FCLCollisionDetector()
833 : CollisionDetector(),
834 mPrimitiveShapeType(MESH),
835 mContactPointComputationMethod(DART)
836 {
837 mCollisionObjectManager.reset(new ManagerForSharableCollisionObjects(this));
838 }
839
840 //==============================================================================
createCollisionObject(const dynamics::ShapeFrame * shapeFrame)841 std::unique_ptr<CollisionObject> FCLCollisionDetector::createCollisionObject(
842 const dynamics::ShapeFrame* shapeFrame)
843 {
844 auto fclCollGeom = claimFCLCollisionGeometry(shapeFrame->getShape());
845
846 return std::unique_ptr<FCLCollisionObject>(
847 new FCLCollisionObject(this, shapeFrame, fclCollGeom));
848 }
849
850 //==============================================================================
refreshCollisionObject(CollisionObject * const object)851 void FCLCollisionDetector::refreshCollisionObject(CollisionObject* const object)
852 {
853 FCLCollisionObject* fcl = static_cast<FCLCollisionObject*>(object);
854
855 fcl->mFCLCollisionObject = std::unique_ptr<fcl::CollisionObject>(
856 new fcl::CollisionObject(claimFCLCollisionGeometry(object->getShape())));
857 }
858
859 //==============================================================================
860 fcl_shared_ptr<fcl::CollisionGeometry>
claimFCLCollisionGeometry(const dynamics::ConstShapePtr & shape)861 FCLCollisionDetector::claimFCLCollisionGeometry(
862 const dynamics::ConstShapePtr& shape)
863 {
864 const std::size_t currentVersion = shape->getVersion();
865
866 const auto search = mShapeMap.insert(std::make_pair(shape, ShapeInfo()));
867 const bool inserted = search.second;
868 ShapeInfo& info = search.first->second;
869
870 if (!inserted && currentVersion == info.mLastKnownVersion)
871 {
872 const auto& fclCollGeom = info.mShape;
873 assert(fclCollGeom.lock());
874 // Ensure all the collision geometry in the map should be alive pointers.
875
876 return fclCollGeom.lock();
877 }
878
879 auto newfclCollGeom = createFCLCollisionGeometry(
880 shape, mPrimitiveShapeType, FCLCollisionGeometryDeleter(this, shape));
881 info.mShape = newfclCollGeom;
882 info.mLastKnownVersion = currentVersion;
883
884 return newfclCollGeom;
885 }
886
887 //==============================================================================
888 fcl_shared_ptr<fcl::CollisionGeometry>
createFCLCollisionGeometry(const dynamics::ConstShapePtr & shape,FCLCollisionDetector::PrimitiveShape type,const FCLCollisionGeometryDeleter & deleter)889 FCLCollisionDetector::createFCLCollisionGeometry(
890 const dynamics::ConstShapePtr& shape,
891 FCLCollisionDetector::PrimitiveShape type,
892 const FCLCollisionGeometryDeleter& deleter)
893 {
894 using dynamics::BoxShape;
895 using dynamics::ConeShape;
896 using dynamics::CylinderShape;
897 using dynamics::EllipsoidShape;
898 using dynamics::MeshShape;
899 using dynamics::PlaneShape;
900 using dynamics::PyramidShape;
901 using dynamics::Shape;
902 using dynamics::SoftMeshShape;
903 using dynamics::SphereShape;
904 #if HAVE_OCTOMAP
905 using dynamics::VoxelGridShape;
906 #endif // HAVE_OCTOMAP
907
908 fcl::CollisionGeometry* geom = nullptr;
909 const auto& shapeType = shape->getType();
910
911 if (SphereShape::getStaticType() == shapeType)
912 {
913 assert(dynamic_cast<const SphereShape*>(shape.get()));
914
915 auto* sphere = static_cast<const SphereShape*>(shape.get());
916 const auto radius = sphere->getRadius();
917
918 if (FCLCollisionDetector::PRIMITIVE == type)
919 geom = new fcl::Sphere(radius);
920 else
921 geom = createEllipsoid<fcl::OBBRSS>(
922 radius * 2.0, radius * 2.0, radius * 2.0);
923 }
924 else if (BoxShape::getStaticType() == shapeType)
925 {
926 assert(dynamic_cast<const BoxShape*>(shape.get()));
927
928 auto box = static_cast<const BoxShape*>(shape.get());
929 const Eigen::Vector3d& size = box->getSize();
930
931 if (FCLCollisionDetector::PRIMITIVE == type)
932 geom = new fcl::Box(size[0], size[1], size[2]);
933 else
934 geom = createCube<fcl::OBBRSS>(size[0], size[1], size[2]);
935 }
936 else if (EllipsoidShape::getStaticType() == shapeType)
937 {
938 assert(dynamic_cast<const EllipsoidShape*>(shape.get()));
939
940 auto ellipsoid = static_cast<const EllipsoidShape*>(shape.get());
941 const Eigen::Vector3d& radii = ellipsoid->getRadii();
942
943 if (FCLCollisionDetector::PRIMITIVE == type)
944 {
945 #if FCL_VERSION_AT_LEAST(0, 4, 0)
946 geom = new fcl::Ellipsoid(FCLTypes::convertVector3(radii));
947 #else
948 geom = createEllipsoid<fcl::OBBRSS>(
949 radii[0] * 2.0, radii[1] * 2.0, radii[2] * 2.0);
950 #endif
951 }
952 else
953 {
954 geom = createEllipsoid<fcl::OBBRSS>(
955 radii[0] * 2.0, radii[1] * 2.0, radii[2] * 2.0);
956 }
957 }
958 else if (CylinderShape::getStaticType() == shapeType)
959 {
960 assert(dynamic_cast<const CylinderShape*>(shape.get()));
961
962 const auto cylinder = static_cast<const CylinderShape*>(shape.get());
963 const auto radius = cylinder->getRadius();
964 const auto height = cylinder->getHeight();
965
966 if (FCLCollisionDetector::PRIMITIVE == type)
967 {
968 geom = createCylinder<fcl::OBBRSS>(radius, radius, height, 16, 16);
969 // TODO(JS): We still need to use mesh for cylinder because FCL 0.4.0
970 // returns single contact point for cylinder yet. Once FCL support
971 // multiple contact points then above code will be replaced by:
972 // fclCollGeom.reset(new fcl::Cylinder(radius, height));
973 }
974 else
975 {
976 geom = createCylinder<fcl::OBBRSS>(radius, radius, height, 16, 16);
977 }
978 }
979 else if (ConeShape::getStaticType() == shapeType)
980 {
981 assert(dynamic_cast<const ConeShape*>(shape.get()));
982
983 const auto cone = std::static_pointer_cast<const ConeShape>(shape);
984 const auto radius = cone->getRadius();
985 const auto height = cone->getHeight();
986
987 if (FCLCollisionDetector::PRIMITIVE == type)
988 {
989 // TODO(JS): We still need to use mesh for cone because FCL 0.4.0
990 // returns single contact point for cone yet. Once FCL support
991 // multiple contact points then above code will be replaced by:
992 // fclCollGeom.reset(new fcl::Cone(radius, height));
993 auto fclMesh = new ::fcl::BVHModel<fcl::OBBRSS>();
994 auto fclCone = fcl::Cone(radius, height);
995 ::fcl::generateBVHModel(*fclMesh, fclCone, fcl::Transform3(), 16, 16);
996 geom = fclMesh;
997 }
998 else
999 {
1000 auto fclMesh = new ::fcl::BVHModel<fcl::OBBRSS>();
1001 auto fclCone = fcl::Cone(radius, height);
1002 ::fcl::generateBVHModel(*fclMesh, fclCone, fcl::Transform3(), 16, 16);
1003 geom = fclMesh;
1004 }
1005 }
1006 else if (PyramidShape::getStaticType() == shapeType)
1007 {
1008 assert(dynamic_cast<const PyramidShape*>(shape.get()));
1009
1010 const auto pyramid = std::static_pointer_cast<const PyramidShape>(shape);
1011 // Use mesh since FCL doesn't support pyramid shape.
1012 geom = createPyramid<fcl::OBBRSS>(*pyramid, fcl::Transform3());
1013 }
1014 else if (PlaneShape::getStaticType() == shapeType)
1015 {
1016 if (FCLCollisionDetector::PRIMITIVE == type)
1017 {
1018 assert(dynamic_cast<const PlaneShape*>(shape.get()));
1019 auto plane = static_cast<const PlaneShape*>(shape.get());
1020 const Eigen::Vector3d normal = plane->getNormal();
1021 const double offset = plane->getOffset();
1022
1023 geom = new fcl::Halfspace(FCLTypes::convertVector3(normal), offset);
1024 }
1025 else
1026 {
1027 geom = createCube<fcl::OBBRSS>(1000.0, 0.0, 1000.0);
1028 dtwarn << "[FCLCollisionDetector] PlaneShape is not supported by "
1029 << "FCLCollisionDetector. We create a thin box mesh insted, where "
1030 << "the size is [1000 0 1000].\n";
1031 }
1032 }
1033 else if (MeshShape::getStaticType() == shapeType)
1034 {
1035 assert(dynamic_cast<const MeshShape*>(shape.get()));
1036
1037 auto shapeMesh = static_cast<const MeshShape*>(shape.get());
1038 const Eigen::Vector3d& scale = shapeMesh->getScale();
1039 auto aiScene = shapeMesh->getMesh();
1040
1041 geom = createMesh<fcl::OBBRSS>(scale[0], scale[1], scale[2], aiScene);
1042 }
1043 else if (SoftMeshShape::getStaticType() == shapeType)
1044 {
1045 assert(dynamic_cast<const SoftMeshShape*>(shape.get()));
1046
1047 auto softMeshShape = static_cast<const SoftMeshShape*>(shape.get());
1048 auto aiMesh = softMeshShape->getAssimpMesh();
1049
1050 geom = createSoftMesh<fcl::OBBRSS>(aiMesh);
1051 }
1052 #if HAVE_OCTOMAP
1053 else if (VoxelGridShape::getStaticType() == shapeType)
1054 {
1055 # if FCL_HAVE_OCTOMAP
1056 assert(dynamic_cast<const VoxelGridShape*>(shape.get()));
1057
1058 auto octreeShape = static_cast<const VoxelGridShape*>(shape.get());
1059 auto octree = octreeShape->getOctree();
1060
1061 geom = new fcl::OcTree(octree);
1062 # else
1063 dterr << "[FCLCollisionDetector::createFCLCollisionGeometry] "
1064 << "Attempting to create an collision geometry for VoxelGridShape, "
1065 << "but the installed FCL isn't built with Octomap support. "
1066 << "Creating a sphere with 0.1 radius instead.\n";
1067
1068 geom = createEllipsoid<fcl::OBBRSS>(0.1, 0.1, 0.1);
1069 # endif // FCL_HAVE_OCTOMAP
1070 }
1071 #endif // HAVE_OCTOMAP
1072 else
1073 {
1074 dterr << "[FCLCollisionDetector::createFCLCollisionGeometry] "
1075 << "Attempting to create an unsupported shape type [" << shapeType
1076 << "]. Creating a sphere with 0.1 radius "
1077 << "instead.\n";
1078
1079 geom = createEllipsoid<fcl::OBBRSS>(0.1, 0.1, 0.1);
1080 }
1081
1082 return fcl_shared_ptr<fcl::CollisionGeometry>(geom, deleter);
1083 }
1084
1085 //==============================================================================
FCLCollisionGeometryDeleter(FCLCollisionDetector * cd,const dynamics::ConstShapePtr & shape)1086 FCLCollisionDetector::FCLCollisionGeometryDeleter::FCLCollisionGeometryDeleter(
1087 FCLCollisionDetector* cd, const dynamics::ConstShapePtr& shape)
1088 : mFCLCollisionDetector(cd), mShape(shape)
1089 {
1090 assert(cd);
1091 assert(shape);
1092 }
1093
1094 //==============================================================================
operator ()(fcl::CollisionGeometry * geom) const1095 void FCLCollisionDetector::FCLCollisionGeometryDeleter::operator()(
1096 fcl::CollisionGeometry* geom) const
1097 {
1098 mFCLCollisionDetector->mShapeMap.erase(mShape);
1099
1100 delete geom;
1101 }
1102
1103 namespace {
1104
1105 //==============================================================================
collisionCallback(fcl::CollisionObject * o1,fcl::CollisionObject * o2,void * cdata)1106 bool collisionCallback(
1107 fcl::CollisionObject* o1, fcl::CollisionObject* o2, void* cdata)
1108 {
1109 // Return true if you don't want more narrow phase collision checking after
1110 // this callback function returns, return false otherwise.
1111
1112 auto collData = static_cast<FCLCollisionCallbackData*>(cdata);
1113
1114 if (collData->done)
1115 return true;
1116
1117 const auto& fclRequest = collData->fclRequest;
1118 auto& fclResult = collData->fclResult;
1119 auto* result = collData->result;
1120 const auto& option = collData->option;
1121 const auto& filter = option.collisionFilter;
1122
1123 // Filtering
1124 if (filter)
1125 {
1126 auto collisionObject1 = static_cast<FCLCollisionObject*>(o1->getUserData());
1127 auto collisionObject2 = static_cast<FCLCollisionObject*>(o2->getUserData());
1128 assert(collisionObject1);
1129 assert(collisionObject2);
1130
1131 if (filter->ignoresCollision(collisionObject2, collisionObject1))
1132 return collData->done;
1133 }
1134
1135 // Clear previous results
1136 fclResult.clear();
1137
1138 // Perform narrow-phase detection
1139 ::fcl::collide(o1, o2, fclRequest, fclResult);
1140
1141 if (result)
1142 {
1143 // Post processing -- converting fcl contact information to ours if needed
1144 if (FCLCollisionDetector::DART == collData->contactPointComputationMethod
1145 && FCLCollisionDetector::MESH == collData->primitiveShapeType)
1146 {
1147 postProcessDART(fclResult, o1, o2, option, *result);
1148 }
1149 else
1150 {
1151 postProcessFCL(fclResult, o1, o2, option, *result);
1152 }
1153
1154 // Check satisfaction of the stopping conditions
1155 if (result->getNumContacts() >= option.maxNumContacts)
1156 collData->done = true;
1157 }
1158 else
1159 {
1160 // If no result is passed, stop checking when the first contact is found
1161 if (fclResult.isCollision())
1162 {
1163 collData->foundCollision = true;
1164 collData->done = true;
1165 }
1166 }
1167
1168 return collData->done;
1169 }
1170
1171 //==============================================================================
distanceCallback(fcl::CollisionObject * o1,fcl::CollisionObject * o2,void * ddata,double & dist)1172 bool distanceCallback(
1173 fcl::CollisionObject* o1,
1174 fcl::CollisionObject* o2,
1175 void* ddata,
1176 double& dist)
1177 {
1178 auto* distData = static_cast<FCLDistanceCallbackData*>(ddata);
1179
1180 const auto& fclRequest = distData->fclRequest;
1181 auto& fclResult = distData->fclResult;
1182 auto* result = distData->result;
1183 const auto& option = distData->option;
1184 const auto& filter = option.distanceFilter;
1185
1186 if (distData->done)
1187 {
1188 dist = distData->unclampedMinDistance;
1189 return true;
1190 }
1191
1192 // Filtering
1193 if (filter)
1194 {
1195 auto collisionObject1 = static_cast<FCLCollisionObject*>(o1->getUserData());
1196 auto collisionObject2 = static_cast<FCLCollisionObject*>(o2->getUserData());
1197 assert(collisionObject1);
1198 assert(collisionObject2);
1199
1200 if (!filter->needDistance(collisionObject2, collisionObject1))
1201 return distData->done;
1202 }
1203
1204 // Clear previous results
1205 fclResult.clear();
1206
1207 // Perform narrow-phase check
1208 ::fcl::distance(o1, o2, fclRequest, fclResult);
1209
1210 // Store the minimum distance just in case result is nullptr.
1211 distData->unclampedMinDistance = fclResult.min_distance;
1212
1213 if (result)
1214 interpreteDistanceResult(fclResult, o1, o2, option, *result);
1215
1216 if (distData->unclampedMinDistance <= option.distanceLowerBound)
1217 distData->done = true;
1218
1219 return distData->done;
1220 }
1221
1222 //==============================================================================
getDiff(const Contact & contact1,const Contact & contact2)1223 Eigen::Vector3d getDiff(const Contact& contact1, const Contact& contact2)
1224 {
1225 return contact1.point - contact2.point;
1226 }
1227
1228 //==============================================================================
getDiff(const fcl::Contact & contact1,const fcl::Contact & contact2)1229 fcl::Vector3 getDiff(const fcl::Contact& contact1, const fcl::Contact& contact2)
1230 {
1231 return contact1.pos - contact2.pos;
1232 }
1233
1234 //==============================================================================
1235 template <
1236 typename ResultT,
1237 typename ContactT,
1238 const ContactT& (ResultT::*GetFun)(std::size_t) const>
markRepeatedPoints(std::vector<bool> & markForDeletion,const ResultT & fclResult,double tol)1239 void markRepeatedPoints(
1240 std::vector<bool>& markForDeletion, const ResultT& fclResult, double tol)
1241 {
1242 const auto checkSize = markForDeletion.size();
1243
1244 if (checkSize == 0u)
1245 return;
1246
1247 for (auto i = 0u; i < checkSize - 1u; ++i)
1248 {
1249 const auto& contact1 = (fclResult.*GetFun)(i);
1250
1251 for (auto j = i + 1u; j < checkSize; ++j)
1252 {
1253 const auto& contact2 = (fclResult.*GetFun)(j);
1254
1255 const auto diff = getDiff(contact1, contact2);
1256
1257 if (diff.dot(diff) < tol)
1258 {
1259 markForDeletion[i] = true;
1260 break;
1261 }
1262 }
1263 }
1264 }
1265
1266 //==============================================================================
1267 template <
1268 typename ResultT,
1269 typename ContactT,
1270 const ContactT& (ResultT::*GetFun)(std::size_t) const>
markColinearPoints(std::vector<bool> & markForDeletion,const ResultT & fclResult,double tol)1271 void markColinearPoints(
1272 std::vector<bool>& markForDeletion, const ResultT& fclResult, double tol)
1273 {
1274 const auto checkSize = markForDeletion.size();
1275
1276 for (auto i = 0u; i < checkSize; ++i)
1277 {
1278 if (markForDeletion[i])
1279 continue;
1280
1281 const auto& contact1 = (fclResult.*GetFun)(i);
1282
1283 for (auto j = i + 1u; j < checkSize; ++j)
1284 {
1285 if (i == j || markForDeletion[j])
1286 continue;
1287
1288 if (markForDeletion[i])
1289 break;
1290
1291 const auto& contact2 = (fclResult.*GetFun)(j);
1292
1293 for (auto k = j + 1u; k < checkSize; ++k)
1294 {
1295 if (i == k)
1296 continue;
1297
1298 const auto& contact3 = (fclResult.*GetFun)(k);
1299
1300 if (isColinear(contact1, contact2, contact3, tol))
1301 {
1302 markForDeletion[i] = true;
1303 break;
1304 }
1305 }
1306 }
1307 }
1308 }
1309
1310 //==============================================================================
postProcessFCL(const fcl::CollisionResult & fclResult,fcl::CollisionObject * o1,fcl::CollisionObject * o2,const CollisionOption & option,CollisionResult & result)1311 void postProcessFCL(
1312 const fcl::CollisionResult& fclResult,
1313 fcl::CollisionObject* o1,
1314 fcl::CollisionObject* o2,
1315 const CollisionOption& option,
1316 CollisionResult& result)
1317 {
1318 const auto numContacts = fclResult.numContacts();
1319
1320 if (0u == numContacts)
1321 return;
1322
1323 // For binary check, return after adding the first contact point to the result
1324 // without the checkings of repeatidity and co-linearity.
1325 if (1u == option.maxNumContacts)
1326 {
1327 for (auto i = 0u; i < numContacts; ++i)
1328 {
1329 if (fcl::length2(fclResult.getContact(i).normal)
1330 < Contact::getNormalEpsilonSquared())
1331 {
1332 // Skip this contact. This is because we assume that a contact with
1333 // zero-length normal is invalid.
1334 continue;
1335 }
1336
1337 result.addContact(
1338 convertContact(fclResult.getContact(i), o1, o2, option));
1339 break;
1340 }
1341 return;
1342 }
1343
1344 const auto tol = 1e-12;
1345 const auto tol3 = tol * 3.0;
1346
1347 std::vector<bool> markForDeletion(numContacts, false);
1348
1349 // mark all the repeated points
1350 markRepeatedPoints<
1351 fcl::CollisionResult,
1352 fcl::Contact,
1353 &fcl::CollisionResult::getContact>(markForDeletion, fclResult, tol3);
1354
1355 // remove all the co-linear contact points
1356 markColinearPoints<
1357 fcl::CollisionResult,
1358 fcl::Contact,
1359 &fcl::CollisionResult::getContact>(markForDeletion, fclResult, tol);
1360
1361 for (auto i = 0u; i < numContacts; ++i)
1362 {
1363 if (markForDeletion[i])
1364 continue;
1365
1366 if (fcl::length2(fclResult.getContact(i).normal)
1367 < Contact::getNormalEpsilonSquared())
1368 {
1369 // Skip this contact. This is because we assume that a contact with
1370 // zero-length normal is invalid.
1371 continue;
1372 }
1373
1374 result.addContact(convertContact(fclResult.getContact(i), o1, o2, option));
1375
1376 if (result.getNumContacts() >= option.maxNumContacts)
1377 return;
1378 }
1379 }
1380
1381 //==============================================================================
postProcessDART(const fcl::CollisionResult & fclResult,fcl::CollisionObject * o1,fcl::CollisionObject * o2,const CollisionOption & option,CollisionResult & result)1382 void postProcessDART(
1383 const fcl::CollisionResult& fclResult,
1384 fcl::CollisionObject* o1,
1385 fcl::CollisionObject* o2,
1386 const CollisionOption& option,
1387 CollisionResult& result)
1388 {
1389 const auto numFilteredContacts = fclResult.numContacts();
1390
1391 if (0u == numFilteredContacts)
1392 return;
1393
1394 auto numContacts = 0u;
1395
1396 std::vector<Contact> unfiltered;
1397 unfiltered.reserve(numFilteredContacts * 2);
1398
1399 for (auto i = 0u; i < numFilteredContacts; ++i)
1400 {
1401 const auto& c = fclResult.getContact(i);
1402
1403 // for each pair of intersecting triangles, we create two contact points
1404 Contact pair1;
1405 Contact pair2;
1406
1407 pair1.collisionObject1
1408 = static_cast<FCLCollisionObject*>(o1->getUserData());
1409 pair1.collisionObject2
1410 = static_cast<FCLCollisionObject*>(o2->getUserData());
1411
1412 if (option.enableContact)
1413 {
1414 pair1.normal = FCLTypes::convertVector3(-c.normal);
1415 if (Contact::isZeroNormal(pair1.normal))
1416 {
1417 // This is an invalid contact, as it contains a zero length normal.
1418 // Skip this contact.
1419 continue;
1420 }
1421
1422 const auto* fclMeshA
1423 = dynamic_cast<const ::fcl::BVHModel<fcl::OBBRSS>*>(c.o1);
1424 const auto* fclMeshB
1425 = dynamic_cast<const ::fcl::BVHModel<fcl::OBBRSS>*>(c.o2);
1426
1427 // If at least one of object is not mesh, then fallback to using contact
1428 // info computed by fcl.
1429 if (!fclMeshA || !fclMeshB)
1430 {
1431 unfiltered.push_back(convertContact(c, o1, o2, option));
1432 continue;
1433 }
1434
1435 pair1.penetrationDepth = c.penetration_depth;
1436 pair1.triID1 = c.b1;
1437 pair1.triID2 = c.b2;
1438 pair2 = pair1;
1439
1440 auto contactResult = evalContactPosition(
1441 c,
1442 *fclMeshA,
1443 *fclMeshB,
1444 FCLTypes::convertTransform(pair1.collisionObject1->getTransform()),
1445 FCLTypes::convertTransform(pair1.collisionObject2->getTransform()),
1446 pair1.point,
1447 pair2.point);
1448
1449 if (contactResult == COPLANAR_CONTACT)
1450 {
1451 if (numContacts > 2u)
1452 continue;
1453 }
1454 else if (contactResult == NO_CONTACT)
1455 {
1456 continue;
1457 }
1458 else
1459 {
1460 numContacts++;
1461 }
1462 }
1463
1464 // For binary check, return after adding the first contact point to the
1465 // result without the checkings of repeatidity and co-linearity.
1466 if (1u == option.maxNumContacts)
1467 {
1468 result.addContact(pair1);
1469
1470 return;
1471 }
1472
1473 unfiltered.push_back(pair1);
1474 unfiltered.push_back(pair2);
1475 }
1476
1477 const auto tol = 1e-12;
1478 const auto tol3 = tol * 3.0;
1479
1480 const auto unfilteredSize = unfiltered.size();
1481
1482 std::vector<bool> markForDeletion(unfilteredSize, false);
1483
1484 // mark all the repeated points
1485 markRepeatedPoints<std::vector<Contact>, Contact, &std::vector<Contact>::at>(
1486 markForDeletion, unfiltered, tol3);
1487
1488 // remove all the co-linear contact points
1489 markColinearPoints<std::vector<Contact>, Contact, &std::vector<Contact>::at>(
1490 markForDeletion, unfiltered, tol);
1491
1492 for (auto i = 0u; i < unfilteredSize; ++i)
1493 {
1494 if (markForDeletion[i])
1495 continue;
1496
1497 result.addContact(unfiltered[i]);
1498
1499 if (result.getNumContacts() >= option.maxNumContacts)
1500 return;
1501 }
1502 }
1503
1504 //==============================================================================
interpreteDistanceResult(const fcl::DistanceResult & fclResult,fcl::CollisionObject * o1,fcl::CollisionObject * o2,const DistanceOption & option,DistanceResult & result)1505 void interpreteDistanceResult(
1506 const fcl::DistanceResult& fclResult,
1507 fcl::CollisionObject* o1,
1508 fcl::CollisionObject* o2,
1509 const DistanceOption& option,
1510 DistanceResult& result)
1511 {
1512 result.unclampedMinDistance = fclResult.min_distance;
1513 result.minDistance
1514 = std::max(fclResult.min_distance, option.distanceLowerBound);
1515
1516 result.shapeFrame1
1517 = static_cast<FCLCollisionObject*>(o1->getUserData())->getShapeFrame();
1518 result.shapeFrame2
1519 = static_cast<FCLCollisionObject*>(o2->getUserData())->getShapeFrame();
1520
1521 if (option.enableNearestPoints)
1522 {
1523 result.nearestPoint1
1524 = FCLTypes::convertVector3(fclResult.nearest_points[0]);
1525 result.nearestPoint2
1526 = FCLTypes::convertVector3(fclResult.nearest_points[1]);
1527 }
1528 }
1529
1530 //==============================================================================
evalContactPosition(const fcl::Contact & fclContact,const::fcl::BVHModel<fcl::OBBRSS> & mesh1,const::fcl::BVHModel<fcl::OBBRSS> & mesh2,const fcl::Transform3 & transform1,const fcl::Transform3 & transform2,Eigen::Vector3d & contactPosition1,Eigen::Vector3d & contactPosition2)1531 int evalContactPosition(
1532 const fcl::Contact& fclContact,
1533 const ::fcl::BVHModel<fcl::OBBRSS>& mesh1,
1534 const ::fcl::BVHModel<fcl::OBBRSS>& mesh2,
1535 const fcl::Transform3& transform1,
1536 const fcl::Transform3& transform2,
1537 Eigen::Vector3d& contactPosition1,
1538 Eigen::Vector3d& contactPosition2)
1539 {
1540 const auto& id1 = fclContact.b1;
1541 const auto& id2 = fclContact.b2;
1542 const auto& tri1 = mesh1.tri_indices[id1];
1543 const auto& tri2 = mesh2.tri_indices[id2];
1544
1545 const auto v1 = fcl::transform(transform1, mesh1.vertices[tri1[0]]);
1546 const auto v2 = fcl::transform(transform1, mesh1.vertices[tri1[1]]);
1547 const auto v3 = fcl::transform(transform1, mesh1.vertices[tri1[2]]);
1548
1549 const auto p1 = fcl::transform(transform2, mesh2.vertices[tri2[0]]);
1550 const auto p2 = fcl::transform(transform2, mesh2.vertices[tri2[1]]);
1551 const auto p3 = fcl::transform(transform2, mesh2.vertices[tri2[2]]);
1552
1553 fcl::Vector3 contact1;
1554 fcl::Vector3 contact2;
1555 const auto testRes = FFtest(v1, v2, v3, p1, p2, p3, &contact1, &contact2);
1556
1557 if (testRes == COPLANAR_CONTACT)
1558 {
1559 const auto area1 = triArea(v1, v2, v3);
1560 const auto area2 = triArea(p1, p2, p3);
1561
1562 if (area1 < area2)
1563 contact1 = v1 + v2 + v3;
1564 else
1565 contact1 = p1 + p2 + p3;
1566
1567 contact1[0] /= 3.0;
1568 contact1[1] /= 3.0;
1569 contact1[2] /= 3.0;
1570 contact2 = contact1;
1571 }
1572
1573 contactPosition1 << contact1[0], contact1[1], contact1[2];
1574 contactPosition2 << contact2[0], contact2[1], contact2[2];
1575
1576 return testRes;
1577 }
1578
1579 //==============================================================================
FFtest(const fcl::Vector3 & r1,const fcl::Vector3 & r2,const fcl::Vector3 & r3,const fcl::Vector3 & R1,const fcl::Vector3 & R2,const fcl::Vector3 & R3,fcl::Vector3 * res1,fcl::Vector3 * res2)1580 int FFtest(
1581 const fcl::Vector3& r1,
1582 const fcl::Vector3& r2,
1583 const fcl::Vector3& r3,
1584 const fcl::Vector3& R1,
1585 const fcl::Vector3& R2,
1586 const fcl::Vector3& R3,
1587 fcl::Vector3* res1,
1588 fcl::Vector3* res2)
1589 {
1590 float U0[3], U1[3], U2[3], V0[3], V1[3], V2[3], RES1[3], RES2[3];
1591 DART_SET(U0, r1);
1592 DART_SET(U1, r2);
1593 DART_SET(U2, r3);
1594 DART_SET(V0, R1);
1595 DART_SET(V1, R2);
1596 DART_SET(V2, R3);
1597
1598 int contactResult = tri_tri_intersect(V0, V1, V2, U0, U1, U2, RES1, RES2);
1599
1600 DART_SET((*res1), RES1);
1601 DART_SET((*res2), RES2);
1602
1603 return contactResult;
1604 }
1605
1606 //==============================================================================
triArea(const fcl::Vector3 & p1,const fcl::Vector3 & p2,const fcl::Vector3 & p3)1607 double triArea(
1608 const fcl::Vector3& p1, const fcl::Vector3& p2, const fcl::Vector3& p3)
1609 {
1610 const fcl::Vector3 a = p2 - p1;
1611 const fcl::Vector3 b = p3 - p1;
1612 const double aMag = a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
1613 const double bMag = b[0] * b[0] + b[1] * b[1] + b[2] * b[2];
1614 const double dp = a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
1615 const double area = 0.5 * std::sqrt(aMag * bMag - dp * dp);
1616
1617 return area;
1618 }
1619
1620 //==============================================================================
isColinear(const Contact & contact1,const Contact & contact2,const Contact & contact3,double tol)1621 bool isColinear(
1622 const Contact& contact1,
1623 const Contact& contact2,
1624 const Contact& contact3,
1625 double tol)
1626 {
1627 return isColinear(contact1.point, contact2.point, contact3.point, tol);
1628 }
1629
1630 //==============================================================================
isColinear(const fcl::Contact & contact1,const fcl::Contact & contact2,const fcl::Contact & contact3,double tol)1631 bool isColinear(
1632 const fcl::Contact& contact1,
1633 const fcl::Contact& contact2,
1634 const fcl::Contact& contact3,
1635 double tol)
1636 {
1637 return isColinear(contact1.pos, contact2.pos, contact3.pos, tol);
1638 }
1639
1640 //==============================================================================
1641 template <typename T>
isColinear(const T & pos1,const T & pos2,const T & pos3,double tol)1642 bool isColinear(const T& pos1, const T& pos2, const T& pos3, double tol)
1643 {
1644 const auto va = pos1 - pos2;
1645 const auto vb = pos1 - pos3;
1646 const auto v = va.cross(vb);
1647
1648 const auto cond1 = v.dot(v) < tol;
1649 const auto cond2 = va.dot(vb) < 0.0;
1650
1651 return cond1 && cond2;
1652 }
1653
1654 //==============================================================================
convertOption(const CollisionOption & option,fcl::CollisionRequest & request)1655 void convertOption(
1656 const CollisionOption& option, fcl::CollisionRequest& request)
1657 {
1658 request.num_max_contacts = option.maxNumContacts;
1659 request.enable_contact = option.enableContact;
1660 #if FCL_VERSION_AT_LEAST(0, 3, 0)
1661 request.gjk_solver_type = ::fcl::GST_LIBCCD;
1662 #endif
1663 }
1664
1665 //==============================================================================
convertOption(const DistanceOption & option,fcl::DistanceRequest & request)1666 void convertOption(const DistanceOption& option, fcl::DistanceRequest& request)
1667 {
1668 request.enable_nearest_points = option.enableNearestPoints;
1669 }
1670
1671 //==============================================================================
convertContact(const fcl::Contact & fclContact,fcl::CollisionObject * o1,fcl::CollisionObject * o2,const CollisionOption & option)1672 Contact convertContact(
1673 const fcl::Contact& fclContact,
1674 fcl::CollisionObject* o1,
1675 fcl::CollisionObject* o2,
1676 const CollisionOption& option)
1677 {
1678 Contact contact;
1679
1680 contact.collisionObject1
1681 = static_cast<FCLCollisionObject*>(o1->getUserData());
1682 contact.collisionObject2
1683 = static_cast<FCLCollisionObject*>(o2->getUserData());
1684
1685 if (option.enableContact)
1686 {
1687 contact.point = FCLTypes::convertVector3(fclContact.pos);
1688 contact.normal = -FCLTypes::convertVector3(fclContact.normal);
1689 contact.penetrationDepth = fclContact.penetration_depth;
1690 contact.triID1 = fclContact.b1;
1691 contact.triID2 = fclContact.b2;
1692 }
1693
1694 return contact;
1695 }
1696
1697 } // anonymous namespace
1698
1699 } // namespace collision
1700 } // namespace dart
1701