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 // Must be included before any Bullet headers.
34 #include "dart/config.hpp"
35
36 #include <algorithm>
37
38 #include "dart/collision/bullet/BulletCollisionDetector.hpp"
39
40 #include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
41 #include <BulletCollision/Gimpact/btGImpactShape.h>
42
43 #include "dart/collision/CollisionFilter.hpp"
44 #include "dart/collision/CollisionObject.hpp"
45 #include "dart/collision/bullet/BulletCollisionGroup.hpp"
46 #include "dart/collision/bullet/BulletCollisionObject.hpp"
47 #include "dart/collision/bullet/BulletTypes.hpp"
48 #include "dart/collision/bullet/detail/BulletCollisionDispatcher.hpp"
49 #include "dart/collision/bullet/detail/BulletOverlapFilterCallback.hpp"
50 #include "dart/common/Console.hpp"
51 #include "dart/dynamics/BoxShape.hpp"
52 #include "dart/dynamics/CapsuleShape.hpp"
53 #include "dart/dynamics/ConeShape.hpp"
54 #include "dart/dynamics/CylinderShape.hpp"
55 #include "dart/dynamics/EllipsoidShape.hpp"
56 #include "dart/dynamics/HeightmapShape.hpp"
57 #include "dart/dynamics/MeshShape.hpp"
58 #include "dart/dynamics/MultiSphereConvexHullShape.hpp"
59 #include "dart/dynamics/PlaneShape.hpp"
60 #include "dart/dynamics/Shape.hpp"
61 #include "dart/dynamics/ShapeFrame.hpp"
62 #include "dart/dynamics/SoftMeshShape.hpp"
63 #include "dart/dynamics/SphereShape.hpp"
64
65 namespace dart {
66 namespace collision {
67
68 namespace {
69
70 Contact convertContact(
71 const btManifoldPoint& bulletManifoldPoint,
72 BulletCollisionObject* collObj1,
73 BulletCollisionObject* collObj2);
74
75 void reportContacts(
76 btCollisionWorld* collWorld,
77 const CollisionOption& option,
78 CollisionResult& result);
79
80 void reportRayHits(
81 const btCollisionWorld::ClosestRayResultCallback callback,
82 const RaycastOption& option,
83 RaycastResult& result);
84
85 void reportRayHits(
86 const btCollisionWorld::AllHitsRayResultCallback callback,
87 const RaycastOption& option,
88 RaycastResult& result);
89
90 std::unique_ptr<btCollisionShape> createBulletEllipsoidMesh(
91 float sizeX, float sizeY, float sizeZ);
92
93 std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpScene(
94 const Eigen::Vector3d& scale, const aiScene* scene);
95
96 std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpMesh(
97 const aiMesh* mesh);
98
99 template <typename HeightmapShapeT>
100 std::unique_ptr<BulletCollisionShape> createBulletCollisionShapeFromHeightmap(
101 const HeightmapShapeT* heightMap);
102
103 } // anonymous namespace
104
105 //==============================================================================
106 BulletCollisionDetector::Registrar<BulletCollisionDetector>
107 BulletCollisionDetector::mRegistrar{
108 BulletCollisionDetector::getStaticType(),
__anon0b06599f0202() 109 []() -> std::shared_ptr<dart::collision::BulletCollisionDetector> {
110 return dart::collision::BulletCollisionDetector::create();
111 }};
112
113 //==============================================================================
create()114 std::shared_ptr<BulletCollisionDetector> BulletCollisionDetector::create()
115 {
116 return std::shared_ptr<BulletCollisionDetector>(
117 new BulletCollisionDetector());
118 }
119
120 //==============================================================================
~BulletCollisionDetector()121 BulletCollisionDetector::~BulletCollisionDetector()
122 {
123 assert(mShapeMap.empty());
124 }
125
126 //==============================================================================
127 std::shared_ptr<CollisionDetector>
cloneWithoutCollisionObjects() const128 BulletCollisionDetector::cloneWithoutCollisionObjects() const
129 {
130 return BulletCollisionDetector::create();
131 }
132
133 //==============================================================================
getType() const134 const std::string& BulletCollisionDetector::getType() const
135 {
136 return getStaticType();
137 }
138
139 //==============================================================================
getStaticType()140 const std::string& BulletCollisionDetector::getStaticType()
141 {
142 static const std::string type = "bullet";
143 return type;
144 }
145
146 //==============================================================================
createCollisionGroup()147 std::unique_ptr<CollisionGroup> BulletCollisionDetector::createCollisionGroup()
148 {
149 return std::make_unique<BulletCollisionGroup>(shared_from_this());
150 }
151
152 //==============================================================================
checkGroupValidity(BulletCollisionDetector * cd,CollisionGroup * group)153 static bool checkGroupValidity(
154 BulletCollisionDetector* cd, CollisionGroup* group)
155 {
156 if (cd != group->getCollisionDetector().get())
157 {
158 dterr << "[BulletCollisionDetector::collide] Attempting to check collision "
159 << "for a collision group that is created from a different collision "
160 << "detector instance.\n";
161
162 return false;
163 }
164
165 return true;
166 }
167
168 //==============================================================================
isCollision(btCollisionWorld * world)169 static bool isCollision(btCollisionWorld* world)
170 {
171 assert(world);
172
173 auto dispatcher = world->getDispatcher();
174 assert(dispatcher);
175
176 const auto numManifolds = dispatcher->getNumManifolds();
177
178 for (auto i = 0; i < numManifolds; ++i)
179 {
180 const auto* contactManifold = dispatcher->getManifoldByIndexInternal(i);
181
182 if (contactManifold->getNumContacts() > 0)
183 return true;
184 }
185
186 return false;
187 }
188
189 //==============================================================================
filterOutCollisions(btCollisionWorld * world)190 void filterOutCollisions(btCollisionWorld* world)
191 {
192 assert(world);
193
194 auto dispatcher
195 = static_cast<detail::BulletCollisionDispatcher*>(world->getDispatcher());
196 assert(dispatcher);
197
198 const auto filter = dispatcher->getFilter();
199 if (!filter)
200 return;
201
202 const auto numManifolds = dispatcher->getNumManifolds();
203
204 std::vector<btPersistentManifold*> manifoldsToRelease;
205
206 for (auto i = 0; i < numManifolds; ++i)
207 {
208 const auto contactManifold = dispatcher->getManifoldByIndexInternal(i);
209
210 const auto body0 = contactManifold->getBody0();
211 const auto body1 = contactManifold->getBody1();
212
213 const auto userPtr0 = body0->getUserPointer();
214 const auto userPtr1 = body1->getUserPointer();
215
216 const auto collObj0 = static_cast<BulletCollisionObject*>(userPtr0);
217 const auto collObj1 = static_cast<BulletCollisionObject*>(userPtr1);
218
219 if (filter->ignoresCollision(collObj0, collObj1))
220 manifoldsToRelease.push_back(contactManifold);
221 }
222
223 for (const auto& manifold : manifoldsToRelease)
224 dispatcher->clearManifold(manifold);
225 }
226
227 //==============================================================================
collide(CollisionGroup * group,const CollisionOption & option,CollisionResult * result)228 bool BulletCollisionDetector::collide(
229 CollisionGroup* group,
230 const CollisionOption& option,
231 CollisionResult* result)
232 {
233 if (result)
234 result->clear();
235
236 if (0u == option.maxNumContacts)
237 return false;
238
239 // Check if 'this' is the collision engine of 'group'.
240 if (!checkGroupValidity(this, group))
241 return false;
242
243 auto castedGroup = static_cast<BulletCollisionGroup*>(group);
244 auto collisionWorld = castedGroup->getBulletCollisionWorld();
245
246 auto dispatcher = static_cast<detail::BulletCollisionDispatcher*>(
247 collisionWorld->getDispatcher());
248 dispatcher->setFilter(option.collisionFilter);
249
250 // Filter out persistent contact pairs already existing in the world
251 filterOutCollisions(collisionWorld);
252
253 castedGroup->updateEngineData();
254 collisionWorld->performDiscreteCollisionDetection();
255
256 if (result)
257 {
258 reportContacts(collisionWorld, option, *result);
259
260 return result->isCollision();
261 }
262 else
263 {
264 return isCollision(collisionWorld);
265 }
266 }
267
268 //==============================================================================
collide(CollisionGroup * group1,CollisionGroup * group2,const CollisionOption & option,CollisionResult * result)269 bool BulletCollisionDetector::collide(
270 CollisionGroup* group1,
271 CollisionGroup* group2,
272 const CollisionOption& option,
273 CollisionResult* result)
274 {
275 if (result)
276 result->clear();
277
278 if (0u == option.maxNumContacts)
279 return false;
280
281 if (!checkGroupValidity(this, group1))
282 return false;
283
284 if (!checkGroupValidity(this, group2))
285 return false;
286
287 // Create a new collision group, merging the two groups into
288 mGroupForFiltering.reset(new BulletCollisionGroup(shared_from_this()));
289 auto bulletCollisionWorld = mGroupForFiltering->getBulletCollisionWorld();
290 auto bulletPairCache = bulletCollisionWorld->getPairCache();
291 auto filterCallback = new detail::BulletOverlapFilterCallback(
292 option.collisionFilter, group1, group2);
293 bulletPairCache->setOverlapFilterCallback(filterCallback);
294
295 mGroupForFiltering->addShapeFramesOf(group1, group2);
296 mGroupForFiltering->updateEngineData();
297
298 bulletCollisionWorld->performDiscreteCollisionDetection();
299
300 if (result)
301 {
302 reportContacts(bulletCollisionWorld, option, *result);
303
304 return result->isCollision();
305 }
306 else
307 {
308 return isCollision(bulletCollisionWorld);
309 }
310 }
311
312 //==============================================================================
distance(CollisionGroup *,const DistanceOption &,DistanceResult *)313 double BulletCollisionDetector::distance(
314 CollisionGroup* /*group*/,
315 const DistanceOption& /*option*/,
316 DistanceResult* /*result*/)
317 {
318 static bool warned = false;
319 if (!warned)
320 {
321 dtwarn
322 << "[BulletCollisionDetector::distance] This collision detector does "
323 << "not support (signed) distance queries. Returning 0.0.\n";
324 warned = true;
325 }
326
327 return 0.0;
328 }
329
330 //==============================================================================
distance(CollisionGroup *,CollisionGroup *,const DistanceOption &,DistanceResult *)331 double BulletCollisionDetector::distance(
332 CollisionGroup* /*group1*/,
333 CollisionGroup* /*group2*/,
334 const DistanceOption& /*option*/,
335 DistanceResult* /*result*/)
336 {
337 static bool warned = false;
338 if (!warned)
339 {
340 dtwarn
341 << "[BulletCollisionDetector::distance] This collision detector does "
342 << "not support (signed) distance queries. Returning.\n";
343 warned = true;
344 }
345
346 return 0.0;
347 }
348
349 //==============================================================================
raycast(CollisionGroup * group,const Eigen::Vector3d & from,const Eigen::Vector3d & to,const RaycastOption & option,RaycastResult * result)350 bool BulletCollisionDetector::raycast(
351 CollisionGroup* group,
352 const Eigen::Vector3d& from,
353 const Eigen::Vector3d& to,
354 const RaycastOption& option,
355 RaycastResult* result)
356 {
357 if (result)
358 result->clear();
359
360 // Check if 'this' is the collision engine of 'group'.
361 if (!checkGroupValidity(this, group))
362 return false;
363
364 auto castedGroup = static_cast<BulletCollisionGroup*>(group);
365 auto collisionWorld = castedGroup->getBulletCollisionWorld();
366
367 const auto btFrom = convertVector3(from);
368 const auto btTo = convertVector3(to);
369
370 if (option.mEnableAllHits)
371 {
372 auto callback = btCollisionWorld::AllHitsRayResultCallback(btFrom, btTo);
373 castedGroup->updateEngineData();
374 collisionWorld->rayTest(btFrom, btTo, callback);
375
376 if (result == nullptr)
377 return callback.hasHit();
378
379 if (callback.hasHit())
380 {
381 reportRayHits(callback, option, *result);
382 return result->hasHit();
383 }
384 else
385 {
386 return false;
387 }
388 }
389 else
390 {
391 auto callback = btCollisionWorld::ClosestRayResultCallback(btFrom, btTo);
392 castedGroup->updateEngineData();
393 collisionWorld->rayTest(btFrom, btTo, callback);
394
395 if (result == nullptr)
396 return callback.hasHit();
397
398 if (callback.hasHit())
399 {
400 reportRayHits(callback, option, *result);
401 return result->hasHit();
402 }
403 else
404 {
405 return false;
406 }
407 }
408 }
409
410 //==============================================================================
BulletCollisionDetector()411 BulletCollisionDetector::BulletCollisionDetector() : CollisionDetector()
412 {
413 mCollisionObjectManager.reset(new ManagerForUnsharableCollisionObjects(this));
414 }
415
416 //==============================================================================
createCollisionObject(const dynamics::ShapeFrame * shapeFrame)417 std::unique_ptr<CollisionObject> BulletCollisionDetector::createCollisionObject(
418 const dynamics::ShapeFrame* shapeFrame)
419 {
420 auto bulletCollShape = claimBulletCollisionShape(shapeFrame->getShape());
421
422 return std::unique_ptr<BulletCollisionObject>(
423 new BulletCollisionObject(this, shapeFrame, bulletCollShape));
424 }
425
426 //==============================================================================
refreshCollisionObject(CollisionObject * object)427 void BulletCollisionDetector::refreshCollisionObject(CollisionObject* object)
428 {
429 BulletCollisionObject* bullet = static_cast<BulletCollisionObject*>(object);
430
431 bullet->mBulletCollisionShape = claimBulletCollisionShape(bullet->getShape());
432 bullet->mBulletCollisionObject->setCollisionShape(
433 bullet->mBulletCollisionShape->mCollisionShape.get());
434 }
435
436 //==============================================================================
notifyCollisionObjectDestroying(CollisionObject * object)437 void BulletCollisionDetector::notifyCollisionObjectDestroying(
438 CollisionObject* object)
439 {
440 reclaimBulletCollisionShape(object->getShape());
441 }
442
443 //==============================================================================
444 std::shared_ptr<BulletCollisionShape>
claimBulletCollisionShape(const dynamics::ConstShapePtr & shape)445 BulletCollisionDetector::claimBulletCollisionShape(
446 const dynamics::ConstShapePtr& shape)
447 {
448 const std::size_t currentVersion = shape->getVersion();
449
450 const auto search = mShapeMap.insert(std::make_pair(shape, ShapeInfo()));
451 const bool inserted = search.second;
452 ShapeInfo& info = search.first->second;
453
454 if (!inserted && currentVersion == info.mLastKnownVersion)
455 {
456 const auto& bulletCollShape = info.mShape.lock();
457 assert(bulletCollShape);
458
459 return bulletCollShape;
460 }
461
462 auto newBulletCollisionShape = std::shared_ptr<BulletCollisionShape>(
463 createBulletCollisionShape(shape).release(),
464 BulletCollisionShapeDeleter(this, shape));
465 info.mShape = newBulletCollisionShape;
466 info.mLastKnownVersion = currentVersion;
467
468 return newBulletCollisionShape;
469 }
470
471 //==============================================================================
reclaimBulletCollisionShape(const dynamics::ConstShapePtr & shape)472 void BulletCollisionDetector::reclaimBulletCollisionShape(
473 const dynamics::ConstShapePtr& shape)
474 {
475 const auto& search = mShapeMap.find(shape);
476 if (search == mShapeMap.end())
477 return;
478
479 const auto& bulletShape = search->second.mShape.lock();
480 if (!bulletShape || bulletShape.use_count() <= 2)
481 mShapeMap.erase(search);
482 }
483
484 //==============================================================================
485 std::unique_ptr<BulletCollisionShape>
createBulletCollisionShape(const dynamics::ConstShapePtr & shape)486 BulletCollisionDetector::createBulletCollisionShape(
487 const dynamics::ConstShapePtr& shape)
488 {
489 using dynamics::BoxShape;
490 using dynamics::CapsuleShape;
491 using dynamics::ConeShape;
492 using dynamics::CylinderShape;
493 using dynamics::EllipsoidShape;
494 using dynamics::HeightmapShaped;
495 using dynamics::HeightmapShapef;
496 using dynamics::MeshShape;
497 using dynamics::MultiSphereConvexHullShape;
498 using dynamics::PlaneShape;
499 using dynamics::Shape;
500 using dynamics::SoftMeshShape;
501 using dynamics::SphereShape;
502
503 if (shape->is<SphereShape>())
504 {
505 assert(dynamic_cast<const SphereShape*>(shape.get()));
506
507 const auto sphere = static_cast<const SphereShape*>(shape.get());
508 const auto radius = sphere->getRadius();
509
510 auto bulletCollisionShape = std::make_unique<btSphereShape>(radius);
511
512 return std::make_unique<BulletCollisionShape>(
513 std::move(bulletCollisionShape));
514 }
515 else if (shape->is<BoxShape>())
516 {
517 assert(dynamic_cast<const BoxShape*>(shape.get()));
518
519 const auto box = static_cast<const BoxShape*>(shape.get());
520 const Eigen::Vector3d& size = box->getSize();
521
522 auto bulletCollisionShape
523 = std::make_unique<btBoxShape>(convertVector3(size * 0.5));
524
525 return std::make_unique<BulletCollisionShape>(
526 std::move(bulletCollisionShape));
527 }
528 else if (shape->is<EllipsoidShape>())
529 {
530 assert(dynamic_cast<const EllipsoidShape*>(shape.get()));
531
532 const auto ellipsoid = static_cast<const EllipsoidShape*>(shape.get());
533 const Eigen::Vector3d& radii = ellipsoid->getRadii();
534
535 auto bulletCollisionShape = createBulletEllipsoidMesh(
536 radii[0] * 2.0, radii[1] * 2.0, radii[2] * 2.0);
537
538 return std::make_unique<BulletCollisionShape>(
539 std::move(bulletCollisionShape));
540 }
541 else if (shape->is<CylinderShape>())
542 {
543 assert(dynamic_cast<const CylinderShape*>(shape.get()));
544
545 const auto cylinder = static_cast<const CylinderShape*>(shape.get());
546 const auto radius = cylinder->getRadius();
547 const auto height = cylinder->getHeight();
548 const auto size = btVector3(radius, radius, height * 0.5);
549
550 auto bulletCollisionShape = std::make_unique<btCylinderShapeZ>(size);
551
552 return std::make_unique<BulletCollisionShape>(
553 std::move(bulletCollisionShape));
554 }
555 else if (shape->is<CapsuleShape>())
556 {
557 assert(dynamic_cast<const CapsuleShape*>(shape.get()));
558
559 const auto capsule = static_cast<const CapsuleShape*>(shape.get());
560 const auto radius = capsule->getRadius();
561 const auto height = capsule->getHeight();
562
563 auto bulletCollisionShape
564 = std::make_unique<btCapsuleShapeZ>(radius, height);
565
566 return std::make_unique<BulletCollisionShape>(
567 std::move(bulletCollisionShape));
568 }
569 else if (shape->is<ConeShape>())
570 {
571 assert(dynamic_cast<const ConeShape*>(shape.get()));
572
573 const auto cone = static_cast<const ConeShape*>(shape.get());
574 const auto radius = cone->getRadius();
575 const auto height = cone->getHeight();
576
577 auto bulletCollisionShape = std::make_unique<btConeShapeZ>(radius, height);
578 bulletCollisionShape->setMargin(0.0);
579 // TODO(JS): Bullet seems to use constant margin 0.4, however this could be
580 // dangerous when the cone is sufficiently small. We use zero margin here
581 // until find better solution even using zero margin is not recommended:
582 // https://www.sjbaker.org/wiki/index.php?title=Physics_-_Bullet_Collected_random_advice#Minimum_object_sizes_-_by_Erwin
583
584 return std::make_unique<BulletCollisionShape>(
585 std::move(bulletCollisionShape));
586 }
587 else if (shape->is<PlaneShape>())
588 {
589 assert(dynamic_cast<const PlaneShape*>(shape.get()));
590
591 const auto plane = static_cast<const PlaneShape*>(shape.get());
592 const Eigen::Vector3d normal = plane->getNormal();
593 const double offset = plane->getOffset();
594
595 auto bulletCollisionShape
596 = std::make_unique<btStaticPlaneShape>(convertVector3(normal), offset);
597
598 return std::make_unique<BulletCollisionShape>(
599 std::move(bulletCollisionShape));
600 }
601 else if (shape->is<MultiSphereConvexHullShape>())
602 {
603 assert(dynamic_cast<const MultiSphereConvexHullShape*>(shape.get()));
604
605 const auto multiSphere
606 = static_cast<const MultiSphereConvexHullShape*>(shape.get());
607 const auto numSpheres = multiSphere->getNumSpheres();
608 const auto& spheres = multiSphere->getSpheres();
609
610 std::vector<btVector3> bulletPositions(numSpheres);
611 std::vector<btScalar> bulletRadii(numSpheres);
612
613 for (auto i = 0u; i < numSpheres; ++i)
614 {
615 bulletRadii[i] = static_cast<btScalar>(spheres[i].first);
616 bulletPositions[i] = convertVector3(spheres[i].second);
617 }
618
619 auto bulletCollisionShape = std::make_unique<btMultiSphereShape>(
620 bulletPositions.data(), bulletRadii.data(), numSpheres);
621
622 return std::make_unique<BulletCollisionShape>(
623 std::move(bulletCollisionShape));
624 }
625 else if (shape->is<MeshShape>())
626 {
627 assert(dynamic_cast<const MeshShape*>(shape.get()));
628
629 const auto shapeMesh = static_cast<const MeshShape*>(shape.get());
630 const auto scale = shapeMesh->getScale();
631 const auto mesh = shapeMesh->getMesh();
632
633 auto bulletCollisionShape
634 = createBulletCollisionShapeFromAssimpScene(scale, mesh);
635
636 return std::make_unique<BulletCollisionShape>(
637 std::move(bulletCollisionShape));
638 }
639 else if (shape->is<SoftMeshShape>())
640 {
641 assert(dynamic_cast<const SoftMeshShape*>(shape.get()));
642
643 const auto softMeshShape = static_cast<const SoftMeshShape*>(shape.get());
644 const auto mesh = softMeshShape->getAssimpMesh();
645
646 auto bulletCollisionShape = createBulletCollisionShapeFromAssimpMesh(mesh);
647
648 return std::make_unique<BulletCollisionShape>(
649 std::move(bulletCollisionShape));
650 }
651 else if (shape->is<HeightmapShapef>())
652 {
653 assert(dynamic_cast<const HeightmapShapef*>(shape.get()));
654
655 const auto heightMap = static_cast<const HeightmapShapef*>(shape.get());
656
657 return createBulletCollisionShapeFromHeightmap(heightMap);
658 }
659 else if (shape->is<HeightmapShaped>())
660 {
661 assert(dynamic_cast<const HeightmapShaped*>(shape.get()));
662
663 dterr << "[BulletCollisionDetector::createBulletCollisionShape] "
664 << "Bullet does not support double height fields (shape type ["
665 << shape->getType() << "]). Creating a sphere with 0.1 radius "
666 << "instead.\n";
667
668 return std::make_unique<BulletCollisionShape>(
669 std::make_unique<btSphereShape>(0.1));
670
671 // take this back in as soon as bullet supports double in heightmaps
672 // const auto heightMap = static_cast<const HeightmapShaped*>(shape.get());
673 // return createBulletCollisionShapeFromHeightmap(heightMap);
674 }
675 else
676 {
677 dterr << "[BulletCollisionDetector::createBulletCollisionShape] "
678 << "Attempting to create an unsupported shape type ["
679 << shape->getType() << "] Creating a sphere with 0.1 radius "
680 << "instead.\n";
681
682 return std::make_unique<BulletCollisionShape>(
683 std::make_unique<btSphereShape>(0.1));
684 }
685 }
686
687 //==============================================================================
688 BulletCollisionDetector::BulletCollisionShapeDeleter ::
BulletCollisionShapeDeleter(BulletCollisionDetector * cd,const dynamics::ConstShapePtr & shape)689 BulletCollisionShapeDeleter(
690 BulletCollisionDetector* cd, const dynamics::ConstShapePtr& shape)
691 : mBulletCollisionDetector(cd), mShape(shape)
692 {
693 // Do nothing
694 }
695
696 //==============================================================================
operator ()(BulletCollisionShape * shape) const697 void BulletCollisionDetector::BulletCollisionShapeDeleter ::operator()(
698 BulletCollisionShape* shape) const
699 {
700 mBulletCollisionDetector->reclaimBulletCollisionShape(mShape);
701
702 delete shape;
703 }
704
705 namespace {
706
707 //==============================================================================
convertContact(const btManifoldPoint & bulletManifoldPoint,BulletCollisionObject * collObj1,BulletCollisionObject * collObj2)708 Contact convertContact(
709 const btManifoldPoint& bulletManifoldPoint,
710 BulletCollisionObject* collObj1,
711 BulletCollisionObject* collObj2)
712 {
713 assert(collObj1);
714 assert(collObj2);
715
716 Contact contact;
717
718 contact.point = convertVector3(bulletManifoldPoint.getPositionWorldOnA());
719 contact.normal = convertVector3(bulletManifoldPoint.m_normalWorldOnB);
720 contact.penetrationDepth = -bulletManifoldPoint.m_distance1;
721 contact.collisionObject1 = collObj1;
722 contact.collisionObject2 = collObj2;
723
724 return contact;
725 }
726
727 //==============================================================================
reportContacts(btCollisionWorld * world,const CollisionOption & option,CollisionResult & result)728 void reportContacts(
729 btCollisionWorld* world,
730 const CollisionOption& option,
731 CollisionResult& result)
732 {
733 assert(world);
734
735 auto dispatcher
736 = static_cast<detail::BulletCollisionDispatcher*>(world->getDispatcher());
737 assert(dispatcher);
738
739 const auto numManifolds = dispatcher->getNumManifolds();
740
741 for (auto i = 0; i < numManifolds; ++i)
742 {
743 const auto contactManifold = dispatcher->getManifoldByIndexInternal(i);
744
745 const auto body0 = contactManifold->getBody0();
746 const auto body1 = contactManifold->getBody1();
747
748 const auto userPointer0 = body0->getUserPointer();
749 const auto userPointer1 = body1->getUserPointer();
750
751 const auto collObj0 = static_cast<BulletCollisionObject*>(userPointer0);
752 const auto collObj1 = static_cast<BulletCollisionObject*>(userPointer1);
753
754 const auto numContacts = contactManifold->getNumContacts();
755
756 for (auto j = 0; j < numContacts; ++j)
757 {
758 const auto& cp = contactManifold->getContactPoint(j);
759
760 if (cp.m_normalWorldOnB.length2() < Contact::getNormalEpsilonSquared())
761 {
762 // Skip this contact. This is because we assume that a contact with
763 // zero-length normal is invalid.
764 continue;
765 }
766
767 result.addContact(convertContact(cp, collObj0, collObj1));
768
769 // No need to check further collisions
770 if (result.getNumContacts() >= option.maxNumContacts)
771 {
772 dispatcher->setDone(true);
773 return;
774 }
775 }
776 }
777 }
778
779 //==============================================================================
convertRayHit(const btCollisionObject * btCollObj,btVector3 hitPointWorld,btVector3 hitNormalWorld,btScalar closestHitFraction)780 RayHit convertRayHit(
781 const btCollisionObject* btCollObj,
782 btVector3 hitPointWorld,
783 btVector3 hitNormalWorld,
784 btScalar closestHitFraction)
785 {
786 RayHit rayHit;
787 assert(btCollObj);
788 const auto* userPointer = btCollObj->getUserPointer();
789 assert(userPointer);
790 const auto* collObj = static_cast<const BulletCollisionObject*>(userPointer);
791 assert(collObj);
792 rayHit.mCollisionObject = collObj;
793 rayHit.mPoint = convertVector3(hitPointWorld);
794 rayHit.mNormal = convertVector3(hitNormalWorld);
795 rayHit.mFraction = static_cast<double>(closestHitFraction);
796
797 return rayHit;
798 }
799
800 //==============================================================================
reportRayHits(const btCollisionWorld::ClosestRayResultCallback callback,const RaycastOption &,RaycastResult & result)801 void reportRayHits(
802 const btCollisionWorld::ClosestRayResultCallback callback,
803 const RaycastOption& /*option*/,
804 RaycastResult& result)
805 {
806 // This function shouldn't be called if callback has not ray hit.
807 assert(callback.hasHit());
808
809 const auto rayHit = convertRayHit(
810 callback.m_collisionObject,
811 callback.m_hitPointWorld,
812 callback.m_hitNormalWorld,
813 callback.m_closestHitFraction);
814
815 result.mRayHits.clear();
816 result.mRayHits.reserve(1);
817 result.mRayHits.emplace_back(rayHit);
818 }
819
820 //==============================================================================
821 struct FractionLess
822 {
operator ()dart::collision::__anon0b06599f0311::FractionLess823 bool operator()(const RayHit& a, const RayHit& b)
824 {
825 return a.mFraction < b.mFraction;
826 }
827 };
828
829 //==============================================================================
reportRayHits(const btCollisionWorld::AllHitsRayResultCallback callback,const RaycastOption & option,RaycastResult & result)830 void reportRayHits(
831 const btCollisionWorld::AllHitsRayResultCallback callback,
832 const RaycastOption& option,
833 RaycastResult& result)
834 {
835 result.mRayHits.clear();
836 result.mRayHits.reserve(
837 static_cast<std::size_t>(callback.m_hitPointWorld.size()));
838
839 for (auto i = 0; i < callback.m_hitPointWorld.size(); ++i)
840 {
841 const auto rayHit = convertRayHit(
842 callback.m_collisionObjects[i],
843 callback.m_hitPointWorld[i],
844 callback.m_hitNormalWorld[i],
845 callback.m_hitFractions[i]);
846 result.mRayHits.emplace_back(rayHit);
847 }
848
849 if (option.mSortByClosest)
850 std::sort(result.mRayHits.begin(), result.mRayHits.end(), FractionLess());
851 }
852
853 //==============================================================================
createBulletEllipsoidMesh(float sizeX,float sizeY,float sizeZ)854 std::unique_ptr<btCollisionShape> createBulletEllipsoidMesh(
855 float sizeX, float sizeY, float sizeZ)
856 {
857 float v[59][3] = {{0, 0, 0},
858 {0.135299, -0.461940, -0.135299},
859 {0.000000, -0.461940, -0.191342},
860 {-0.135299, -0.461940, -0.135299},
861 {-0.191342, -0.461940, 0.000000},
862 {-0.135299, -0.461940, 0.135299},
863 {0.000000, -0.461940, 0.191342},
864 {0.135299, -0.461940, 0.135299},
865 {0.191342, -0.461940, 0.000000},
866 {0.250000, -0.353553, -0.250000},
867 {0.000000, -0.353553, -0.353553},
868 {-0.250000, -0.353553, -0.250000},
869 {-0.353553, -0.353553, 0.000000},
870 {-0.250000, -0.353553, 0.250000},
871 {0.000000, -0.353553, 0.353553},
872 {0.250000, -0.353553, 0.250000},
873 {0.353553, -0.353553, 0.000000},
874 {0.326641, -0.191342, -0.326641},
875 {0.000000, -0.191342, -0.461940},
876 {-0.326641, -0.191342, -0.326641},
877 {-0.461940, -0.191342, 0.000000},
878 {-0.326641, -0.191342, 0.326641},
879 {0.000000, -0.191342, 0.461940},
880 {0.326641, -0.191342, 0.326641},
881 {0.461940, -0.191342, 0.000000},
882 {0.353553, 0.000000, -0.353553},
883 {0.000000, 0.000000, -0.500000},
884 {-0.353553, 0.000000, -0.353553},
885 {-0.500000, 0.000000, 0.000000},
886 {-0.353553, 0.000000, 0.353553},
887 {0.000000, 0.000000, 0.500000},
888 {0.353553, 0.000000, 0.353553},
889 {0.500000, 0.000000, 0.000000},
890 {0.326641, 0.191342, -0.326641},
891 {0.000000, 0.191342, -0.461940},
892 {-0.326641, 0.191342, -0.326641},
893 {-0.461940, 0.191342, 0.000000},
894 {-0.326641, 0.191342, 0.326641},
895 {0.000000, 0.191342, 0.461940},
896 {0.326641, 0.191342, 0.326641},
897 {0.461940, 0.191342, 0.000000},
898 {0.250000, 0.353553, -0.250000},
899 {0.000000, 0.353553, -0.353553},
900 {-0.250000, 0.353553, -0.250000},
901 {-0.353553, 0.353553, 0.000000},
902 {-0.250000, 0.353553, 0.250000},
903 {0.000000, 0.353553, 0.353553},
904 {0.250000, 0.353553, 0.250000},
905 {0.353553, 0.353553, 0.000000},
906 {0.135299, 0.461940, -0.135299},
907 {0.000000, 0.461940, -0.191342},
908 {-0.135299, 0.461940, -0.135299},
909 {-0.191342, 0.461940, 0.000000},
910 {-0.135299, 0.461940, 0.135299},
911 {0.000000, 0.461940, 0.191342},
912 {0.135299, 0.461940, 0.135299},
913 {0.191342, 0.461940, 0.000000},
914 {0.000000, -0.500000, 0.000000},
915 {0.000000, 0.500000, 0.000000}};
916
917 int f[112][3]
918 = {{1, 2, 9}, {9, 2, 10}, {2, 3, 10}, {10, 3, 11}, {3, 4, 11},
919 {11, 4, 12}, {4, 5, 12}, {12, 5, 13}, {5, 6, 13}, {13, 6, 14},
920 {6, 7, 14}, {14, 7, 15}, {7, 8, 15}, {15, 8, 16}, {8, 1, 16},
921 {16, 1, 9}, {9, 10, 17}, {17, 10, 18}, {10, 11, 18}, {18, 11, 19},
922 {11, 12, 19}, {19, 12, 20}, {12, 13, 20}, {20, 13, 21}, {13, 14, 21},
923 {21, 14, 22}, {14, 15, 22}, {22, 15, 23}, {15, 16, 23}, {23, 16, 24},
924 {16, 9, 24}, {24, 9, 17}, {17, 18, 25}, {25, 18, 26}, {18, 19, 26},
925 {26, 19, 27}, {19, 20, 27}, {27, 20, 28}, {20, 21, 28}, {28, 21, 29},
926 {21, 22, 29}, {29, 22, 30}, {22, 23, 30}, {30, 23, 31}, {23, 24, 31},
927 {31, 24, 32}, {24, 17, 32}, {32, 17, 25}, {25, 26, 33}, {33, 26, 34},
928 {26, 27, 34}, {34, 27, 35}, {27, 28, 35}, {35, 28, 36}, {28, 29, 36},
929 {36, 29, 37}, {29, 30, 37}, {37, 30, 38}, {30, 31, 38}, {38, 31, 39},
930 {31, 32, 39}, {39, 32, 40}, {32, 25, 40}, {40, 25, 33}, {33, 34, 41},
931 {41, 34, 42}, {34, 35, 42}, {42, 35, 43}, {35, 36, 43}, {43, 36, 44},
932 {36, 37, 44}, {44, 37, 45}, {37, 38, 45}, {45, 38, 46}, {38, 39, 46},
933 {46, 39, 47}, {39, 40, 47}, {47, 40, 48}, {40, 33, 48}, {48, 33, 41},
934 {41, 42, 49}, {49, 42, 50}, {42, 43, 50}, {50, 43, 51}, {43, 44, 51},
935 {51, 44, 52}, {44, 45, 52}, {52, 45, 53}, {45, 46, 53}, {53, 46, 54},
936 {46, 47, 54}, {54, 47, 55}, {47, 48, 55}, {55, 48, 56}, {48, 41, 56},
937 {56, 41, 49}, {2, 1, 57}, {3, 2, 57}, {4, 3, 57}, {5, 4, 57},
938 {6, 5, 57}, {7, 6, 57}, {8, 7, 57}, {1, 8, 57}, {49, 50, 58},
939 {50, 51, 58}, {51, 52, 58}, {52, 53, 58}, {53, 54, 58}, {54, 55, 58},
940 {55, 56, 58}, {56, 49, 58}};
941
942 auto triMesh = new btTriangleMesh();
943
944 for (auto i = 0u; i < 112; ++i)
945 {
946 btVector3 vertices[3];
947
948 const auto& index0 = f[i][0];
949 const auto& index1 = f[i][1];
950 const auto& index2 = f[i][2];
951
952 const auto& p0 = v[index0];
953 const auto& p1 = v[index1];
954 const auto& p2 = v[index2];
955
956 vertices[0] = btVector3(p0[0] * sizeX, p0[1] * sizeY, p0[2] * sizeZ);
957 vertices[1] = btVector3(p1[0] * sizeX, p1[1] * sizeY, p1[2] * sizeZ);
958 vertices[2] = btVector3(p2[0] * sizeX, p2[1] * sizeY, p2[2] * sizeZ);
959
960 triMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
961 }
962
963 auto gimpactMeshShape = std::make_unique<btGImpactMeshShape>(triMesh);
964 gimpactMeshShape->updateBound();
965
966 return gimpactMeshShape;
967 }
968
969 //==============================================================================
createBulletCollisionShapeFromAssimpScene(const Eigen::Vector3d & scale,const aiScene * scene)970 std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpScene(
971 const Eigen::Vector3d& scale, const aiScene* scene)
972 {
973 auto triMesh = new btTriangleMesh();
974
975 for (auto i = 0u; i < scene->mNumMeshes; ++i)
976 {
977 for (auto j = 0u; j < scene->mMeshes[i]->mNumFaces; ++j)
978 {
979 btVector3 vertices[3];
980 for (auto k = 0u; k < 3; ++k)
981 {
982 const aiVector3D& vertex
983 = scene->mMeshes[i]
984 ->mVertices[scene->mMeshes[i]->mFaces[j].mIndices[k]];
985 vertices[k] = btVector3(
986 vertex.x * scale[0], vertex.y * scale[1], vertex.z * scale[2]);
987 }
988 triMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
989 }
990 }
991
992 auto gimpactMeshShape = std::make_unique<btGImpactMeshShape>(triMesh);
993 gimpactMeshShape->updateBound();
994 gimpactMeshShape->setUserPointer(triMesh);
995
996 return gimpactMeshShape;
997 }
998
999 //==============================================================================
createBulletCollisionShapeFromAssimpMesh(const aiMesh * mesh)1000 std::unique_ptr<btCollisionShape> createBulletCollisionShapeFromAssimpMesh(
1001 const aiMesh* mesh)
1002 {
1003 auto triMesh = new btTriangleMesh();
1004
1005 for (auto i = 0u; i < mesh->mNumFaces; ++i)
1006 {
1007 btVector3 vertices[3];
1008 for (auto j = 0u; j < 3; ++j)
1009 {
1010 const aiVector3D& vertex = mesh->mVertices[mesh->mFaces[i].mIndices[j]];
1011 vertices[j] = btVector3(vertex.x, vertex.y, vertex.z);
1012 }
1013 triMesh->addTriangle(vertices[0], vertices[1], vertices[2]);
1014 }
1015
1016 auto gimpactMeshShape = std::make_unique<btGImpactMeshShape>(triMesh);
1017 gimpactMeshShape->updateBound();
1018
1019 return gimpactMeshShape;
1020 }
1021
1022 //==============================================================================
1023 template <typename HeightmapShapeT>
createBulletCollisionShapeFromHeightmap(const HeightmapShapeT * heightMap)1024 std::unique_ptr<BulletCollisionShape> createBulletCollisionShapeFromHeightmap(
1025 const HeightmapShapeT* heightMap)
1026 {
1027 // get the heightmap parameters
1028 const auto& scale = heightMap->getScale();
1029 const auto minHeight = heightMap->getMinHeight();
1030 const auto maxHeight = heightMap->getMaxHeight();
1031
1032 // determine which data type (float or double) is to be used for the field
1033 PHY_ScalarType scalarType = PHY_FLOAT;
1034 if (std::is_same<typename HeightmapShapeT::S, double>::value)
1035 {
1036 dterr << "Bullet does not support DOUBLE as heightmap field yet.\n";
1037 return nullptr;
1038 // take this back in as soon as it is supported
1039 // scalarType = PHY_DOUBLE;
1040 }
1041
1042 // the y-values in the height field need to be flipped
1043 heightMap->flipY();
1044
1045 const auto& heights = heightMap->getHeightField();
1046
1047 // create the height field
1048 const btVector3 localScaling(scale.x(), scale.y(), scale.z());
1049 const bool flipQuadEdges = false;
1050 auto heightFieldShape = std::make_unique<btHeightfieldTerrainShape>(
1051 heightMap->getWidth(), // Width of height field
1052 heightMap->getDepth(), // Depth of height field
1053 heights.data(), // Height values
1054 1, // Height scaling
1055 minHeight, // Min height
1056 maxHeight, // Max height
1057 2, // Up axis
1058 scalarType, // Float or double field
1059 flipQuadEdges); // Flip quad edges
1060 heightFieldShape->setLocalScaling(localScaling);
1061 heightFieldShape->setUseZigzagSubdivision(true);
1062
1063 // change the relative transform of the height field so that the minimum
1064 // height is at the same z coordinate. Bullet shifts the height map such
1065 // that its center is the AABB center.
1066 const btVector3 trans(
1067 0, 0, ((maxHeight - minHeight) * 0.5 + minHeight) * scale.z());
1068 btTransform relativeShapeTransform(btMatrix3x3::getIdentity(), trans);
1069
1070 // bullet places the heightfield such that the origin is in the
1071 // middle of the AABB. We want however that the minimum height value
1072 // is on x/y plane.
1073 btVector3 min;
1074 btVector3 max;
1075 heightFieldShape->getAabb(btTransform::getIdentity(), min, max);
1076 dtdbg << "DART Bullet heightfield AABB: min = {" << min.x() << ", " << min.y()
1077 << ", " << min.z() << "}, max = {" << max.x() << ", " << max.y() << ", "
1078 << max.z() << "}"
1079 << " (will be translated by z=" << trans.z() << ")" << std::endl;
1080
1081 return std::make_unique<BulletCollisionShape>(
1082 std::move(heightFieldShape), relativeShapeTransform);
1083 }
1084
1085 } // anonymous namespace
1086
1087 } // namespace collision
1088 } // namespace dart
1089