5050#include " dart/dynamics/PyramidShape.hpp"
5151#include " dart/dynamics/Shape.hpp"
5252#include " dart/dynamics/ShapeFrame.hpp"
53+ #include " dart/dynamics/ShapeNode.hpp"
5354#include " dart/dynamics/SoftMeshShape.hpp"
5455#include " dart/dynamics/SphereShape.hpp"
5556#include " dart/dynamics/VoxelGridShape.hpp"
5657
5758#include < assimp/scene.h>
5859
60+ #include < algorithm>
5961#include < limits>
62+ #include < string>
63+
64+ #include < cstdint>
6065
6166namespace dart {
6267namespace collision {
6368
6469namespace {
6570
71+ std::string collisionObjectKey (const FCLCollisionObject* object)
72+ {
73+ if (!object)
74+ return " " ;
75+
76+ return object->getKey ();
77+ }
78+
6679bool collisionCallback (
6780 fcl::CollisionObject* o1, fcl::CollisionObject* o2, void * cdata);
6881
@@ -146,6 +159,11 @@ Contact convertContact(
146159 fcl::CollisionObject* o2,
147160 const CollisionOption& option);
148161
162+ bool shouldSwapDeterministically (
163+ const FCLCollisionObject* fclObj1, const FCLCollisionObject* fclObj2);
164+
165+ void applyDeterministicSwap (Contact& contact);
166+
149167// / Collision data stores the collision request and the result given by
150168// / collision algorithm.
151169struct FCLCollisionCallbackData
@@ -852,6 +870,7 @@ void FCLCollisionDetector::refreshCollisionObject(CollisionObject* const object)
852870
853871 fcl->mFCLCollisionObject = std::unique_ptr<fcl::CollisionObject>(
854872 new fcl::CollisionObject (claimFCLCollisionGeometry (object->getShape ())));
873+ fcl->mFCLCollisionObject ->setUserData (fcl);
855874}
856875
857876// ==============================================================================
@@ -1082,16 +1101,16 @@ bool collisionCallback(
10821101 const auto & option = collData->option ;
10831102 const auto & filter = option.collisionFilter ;
10841103
1085- // Filtering
1086- if (filter) {
1087- auto collisionObject1 = static_cast <FCLCollisionObject*>(o1-> getUserData () );
1088- auto collisionObject2 = static_cast <FCLCollisionObject*>(o2-> getUserData () );
1089- DART_ASSERT ( collisionObject1);
1090- DART_ASSERT (collisionObject2) ;
1104+ auto collisionObject1 = static_cast <FCLCollisionObject*>(o1-> getUserData ());
1105+ auto collisionObject2 = static_cast <FCLCollisionObject*>(o2-> getUserData ());
1106+ DART_ASSERT (collisionObject1 );
1107+ DART_ASSERT (collisionObject2 );
1108+ if (! collisionObject1 || !collisionObject2)
1109+ return collData-> done ;
10911110
1092- if (filter-> ignoresCollision (collisionObject2, collisionObject1))
1093- return collData-> done ;
1094- }
1111+ // Filtering
1112+ if (filter && filter-> ignoresCollision (collisionObject2, collisionObject1))
1113+ return collData-> done ;
10951114
10961115 // Clear previous results
10971116 fclResult.clear ();
@@ -1412,6 +1431,15 @@ void postProcessDART(
14121431 } else {
14131432 numContacts++;
14141433 }
1434+
1435+ const auto * fclObj1 = static_cast <FCLCollisionObject*>(o1->getUserData ());
1436+ const auto * fclObj2 = static_cast <FCLCollisionObject*>(o2->getUserData ());
1437+ if (shouldSwapDeterministically (fclObj1, fclObj2)) {
1438+ if (option.enableContact )
1439+ std::swap (pair1.point , pair2.point );
1440+ applyDeterministicSwap (pair1);
1441+ applyDeterministicSwap (pair2);
1442+ }
14151443 }
14161444
14171445 // For binary check, return after adding the first contact point to the
@@ -1624,10 +1652,8 @@ Contact convertContact(
16241652{
16251653 Contact contact;
16261654
1627- contact.collisionObject1
1628- = static_cast <FCLCollisionObject*>(o1->getUserData ());
1629- contact.collisionObject2
1630- = static_cast <FCLCollisionObject*>(o2->getUserData ());
1655+ contact.collisionObject1 = static_cast <CollisionObject*>(o1->getUserData ());
1656+ contact.collisionObject2 = static_cast <CollisionObject*>(o2->getUserData ());
16311657
16321658 if (option.enableContact ) {
16331659 contact.point = FCLTypes::convertVector3 (fclContact.pos );
@@ -1637,9 +1663,40 @@ Contact convertContact(
16371663 contact.triID2 = fclContact.b2 ;
16381664 }
16391665
1666+ // Enforce deterministic ordering across runs and clones. Tie-break on the
1667+ // underlying FCL object address if keys are identical.
1668+ const auto fclObj1
1669+ = static_cast <FCLCollisionObject*>(contact.collisionObject1 );
1670+ const auto fclObj2
1671+ = static_cast <FCLCollisionObject*>(contact.collisionObject2 );
1672+ if (shouldSwapDeterministically (fclObj1, fclObj2))
1673+ applyDeterministicSwap (contact);
1674+
16401675 return contact;
16411676}
16421677
1678+ // ==============================================================================
1679+ bool shouldSwapDeterministically (
1680+ const FCLCollisionObject* fclObj1, const FCLCollisionObject* fclObj2)
1681+ {
1682+ const auto key1 = collisionObjectKey (fclObj1);
1683+ const auto key2 = collisionObjectKey (fclObj2);
1684+
1685+ const auto addr1 = reinterpret_cast <std::uintptr_t >(fclObj1);
1686+ const auto addr2 = reinterpret_cast <std::uintptr_t >(fclObj2);
1687+
1688+ return (key2 < key1) || (key1 == key2 && addr2 < addr1 && addr2 != 0u );
1689+ }
1690+
1691+ // ==============================================================================
1692+ void applyDeterministicSwap (Contact& contact)
1693+ {
1694+ std::swap (contact.collisionObject1 , contact.collisionObject2 );
1695+ std::swap (contact.triID1 , contact.triID2 );
1696+
1697+ contact.normal = -contact.normal ;
1698+ }
1699+
16431700} // anonymous namespace
16441701
16451702} // namespace collision
0 commit comments