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