Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add blacklist to BodyNodeCollisionFilter #911

Merged
merged 21 commits into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
af9d36e
Add blacklist to BodyNodeCollisionFilter
jslee02 Aug 28, 2017
a3f01ff
Add more test for blacklist of collision filter
jslee02 Aug 28, 2017
43820d5
Update collision filter test to use world
jslee02 Aug 28, 2017
68373c6
Fix collision filter test to use the given collision detector
jslee02 Aug 28, 2017
7e5ed18
Merge branch 'release-6.3' into feature/collision_filter
jslee02 Aug 29, 2017
305f47e
Using insert instead of manual logic
mxgrey Aug 29, 2017
2418f76
Language tweaks in comments
mxgrey Aug 29, 2017
33f46fa
Rename needsCollisionCheck to ignoreCollision per this comment: https…
jslee02 Aug 30, 2017
8cc365a
Use std::unordered_set instead of std::vector per this comment: https…
jslee02 Aug 30, 2017
78b7e42
Rename existsBodyNodePairInBlacklist to hasBodyNodePairInBlacklist pe…
jslee02 Aug 30, 2017
e6f2f00
Use std::unordered_set instead of std::set per this comment: https://…
jslee02 Aug 30, 2017
96596fd
Use more informative variable name: key -> associatedRights
jslee02 Aug 30, 2017
72a2b28
Remove redundant operations per this comment: https://github.com/dart…
jslee02 Aug 30, 2017
6a8e5ac
Assert when BodyNodeCollisionFilter is used for non-ShapeNodes per th…
jslee02 Aug 30, 2017
866947c
Remove unnecessary header inclusion
jslee02 Aug 30, 2017
f46337b
Use more informative variable name: key -> associatedRights
jslee02 Aug 31, 2017
8c7611a
Remove unnecessary operations
jslee02 Aug 31, 2017
b5879bc
Allow BodyNodeCollisionFilter is used for non-ShapeNode; return false…
jslee02 Aug 31, 2017
23f10bd
Generalize blacklist functionality from BodyNodeCollisionFilter
jslee02 Aug 31, 2017
42ad25e
Clean up code
jslee02 Aug 31, 2017
3ced456
Merge branch 'release-6.3' into feature/collision_filter
jslee02 Sep 12, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 158 additions & 12 deletions dart/collision/CollisionFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,44 +38,164 @@ namespace dart {
namespace collision {

//==============================================================================
bool BodyNodeCollisionFilter::needCollision(
bool CollisionFilter::needCollision(
const CollisionObject* object1, const CollisionObject* object2) const
{
return !ignoresCollision(object1, object2);
}

//==============================================================================
void CompositeCollisionFilter::addCollisionFilter(const CollisionFilter* filter)
{
if (!filter)
return;

const auto result = std::find(mFilters.begin(), mFilters.end(), filter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have two thoughts on this where the second thought makes the first through irrelevant, but I'll mention it anyway, just in case you're able to correct me:

  1. I'm not sure if std::find will take advantage of std::unordered_set's useful hashing properties. It's entirely plausible that it could (using either template specialization or function overloading), and I wouldn't be very surprised if it did, but I can't find any indication online that this is the case. So this might be performing an O(N) lookup even though our container could do a O(1) lookup. Please let me know if you're aware of any specialized optimizations that std::find performs, because I'd be very interested in knowing.

  2. Calling mFilters.insert(filter) will insert filter if it wasn't already in the set; otherwise it will do nothing. So checking to see if filter is already in the set doesn't do anything for us here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I believe there are no specialized optimizations or that std::find performs, and also believe that there is no advantage of using std::find over std::unordered_set::find.

  2. This is just an oversight: This is a legacy code that was for when the type of mFilters was std::vector, which should be removed as std::unordered_set is used now.

const bool found = (result != mFilters.end());
if (found)
return;

mFilters.insert(filter);
}

//==============================================================================
void CompositeCollisionFilter::removeCollisionFilter(
const CollisionFilter* filter)
{
mFilters.erase(filter);
}

//==============================================================================
void CompositeCollisionFilter::removeAllCollisionFilters()
{
mFilters.clear();
}

//==============================================================================
bool CompositeCollisionFilter::ignoresCollision(
const CollisionObject* object1, const CollisionObject* object2) const
{
for (const auto* filter : mFilters)
{
const bool collisionIgnored = filter->ignoresCollision(object1, object2);
if (collisionIgnored)
return true;
}

return false;
}

//==============================================================================
void BodyNodeCollisionFilter::addBodyNodePairToBlackList(
const dynamics::BodyNode* bodyNode1, const dynamics::BodyNode* bodyNode2)
{
if (!bodyNode1 || !bodyNode2)
return;

const auto* bodyNodeLess = bodyNode1;
const auto* bodyNodeGreater = bodyNode2;

if (bodyNodeLess > bodyNodeGreater)
std::swap(bodyNodeLess, bodyNodeGreater);

// Call insert in case an entry for bodyNodeLess doesn't exist. If it doesn't
// exist, it will be initialized with an empty set. If it does already exist,
// we will just get an iterator to the existing entry.
const auto itLess = mBlackList.insert(
std::make_pair(bodyNodeLess,
std::unordered_set<const dynamics::BodyNode*>())).first;

// Insert bodyNodeGreater into the set corresponding to bodyNodeLess. If the
// pair already existed, this will do nothing.
itLess->second.insert(bodyNodeGreater);
}

//==============================================================================
void BodyNodeCollisionFilter::removeBodyNodePairFromBlackList(
const dynamics::BodyNode* bodyNode1, const dynamics::BodyNode* bodyNode2)
{
if (!bodyNode1 || !bodyNode2)
return;

const auto* bodyNodeLess = bodyNode1;
const auto* bodyNodeGreater = bodyNode2;

if (bodyNodeLess > bodyNodeGreater)
std::swap(bodyNodeLess, bodyNodeGreater);

// Remove the pair only when it already exists
const auto resultLeft = mBlackList.find(bodyNodeLess);
const bool foundLeft = (resultLeft != mBlackList.end());
if (foundLeft)
{
auto& associatedRights = resultLeft->second;
associatedRights.erase(bodyNodeGreater);

if (associatedRights.empty())
mBlackList.erase(resultLeft);
}
}

//==============================================================================
void BodyNodeCollisionFilter::removeAllBodyNodePairsFromBlackList()
{
mBlackList.clear();
}

//==============================================================================
bool BodyNodeCollisionFilter::ignoresCollision(
const collision::CollisionObject* object1,
const collision::CollisionObject* object2) const
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is really for line 171, but Github won't let me comment there:

Why do we assume that non-ShapeNodes are always checked for collisions? Is it even possible for a non-ShapeNode to provide a CollisionObject? Rather than short-circuiting if one of them is not a ShapeNode, I would expect to assert that both are, in fact, ShapeNodes.

if (object1 == object2)
return false;
return true;

auto shapeNode1 = object1->getShapeFrame()->asShapeNode();
auto shapeNode2 = object2->getShapeFrame()->asShapeNode();

if (!shapeNode1 || !shapeNode2)
return true;
// We assume that non-ShapeNode is always being checked collision.
if (!shapeNode1)
{
dterr << "[BodyNodeCollisionFilter::ignoresCollision] Inappropriate "
<< "collision filter is used for shapeNode1. This collision filter "
<< "shouldn't be for non-ShapeNodes.\n";
assert(false);
}

if (!shapeNode2)
{
dterr << "[BodyNodeCollisionFilter::ignoresCollision] Inappropriate "
<< "collision filter is used for shapeNode2. This collision filter "
<< "shouldn't be for non-ShapeNodes.\n";
assert(false);
}

auto bodyNode1 = shapeNode1->getBodyNodePtr();
auto bodyNode2 = shapeNode2->getBodyNodePtr();

if (bodyNode1 == bodyNode2)
return false;
return true;

if (!bodyNode1->isCollidable() || !bodyNode2->isCollidable())
return false;
return true;

if (bodyNode1->getSkeleton() == bodyNode2->getSkeleton())
{
auto skeleton = bodyNode1->getSkeleton();

if (!skeleton->isEnabledSelfCollisionCheck())
return false;
return true;

if (!skeleton->isEnabledAdjacentBodyCheck())
{
if (areAdjacentBodies(bodyNode1, bodyNode2))
return false;
return true;
}
}

return true;
if (hasBodyNodePairInBlacklist(bodyNode1, bodyNode2))
return true;

return false;
}

//==============================================================================
Expand All @@ -93,5 +213,31 @@ bool BodyNodeCollisionFilter::areAdjacentBodies(
return false;
}

} // namespace collision
} // namespace dart
//==============================================================================
bool BodyNodeCollisionFilter::hasBodyNodePairInBlacklist(
const dynamics::BodyNode* bodyNode1,
const dynamics::BodyNode* bodyNode2) const
{
const auto* bodyNodeLess = bodyNode1;
const auto* bodyNodeGreater = bodyNode2;

if (bodyNodeLess > bodyNodeGreater)
std::swap(bodyNodeLess, bodyNodeGreater);

const auto resultLeft = mBlackList.find(bodyNodeLess);
const bool foundLeft = (resultLeft != mBlackList.end());
if (foundLeft)
{
auto& key = resultLeft->second;

const auto resultRight = key.find(bodyNodeGreater);
const bool foundRight = (resultRight != key.end());
if (foundRight)
return true;
}

return false;
}

} // namespace collision
} // namespace dart
87 changes: 79 additions & 8 deletions dart/collision/CollisionFilter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
#ifndef DART_COLLISION_COLLISIONFILTER_HPP_
#define DART_COLLISION_COLLISIONFILTER_HPP_

#include <unordered_map>
#include <unordered_set>

#include "dart/common/Deprecated.hpp"

namespace dart {

namespace dynamics {
Expand All @@ -42,22 +47,88 @@ namespace collision {

class CollisionObject;

struct CollisionFilter
class CollisionFilter
{
virtual bool needCollision(const CollisionObject* object1,
const CollisionObject* object2) const = 0;
public:
/// Returns true if the given two CollisionObjects should be checked by the
/// collision detector, false otherwise.
/// \deprecated Deprecated in 6.3.0. Please use ignoreCollision instead. Note
/// that ignoreCollision returns logically opposite to what needCollision
/// returns.
DART_DEPRECATED(6.3)
bool needCollision(
const CollisionObject* object1, const CollisionObject* object2) const;

/// Returns true if the given two CollisionObjects should be checked by the
/// collision detector, false otherwise.
virtual bool ignoresCollision(
const CollisionObject* object1, const CollisionObject* object2) const = 0;
};

struct BodyNodeCollisionFilter : CollisionFilter
class CompositeCollisionFilter : public CollisionFilter
{
bool needCollision(const CollisionObject* object1,
const CollisionObject* object2) const override;
public:
/// Adds a collision filter to this CompositeCollisionFilter.
void addCollisionFilter(const CollisionFilter* filter);

/// Removes a collision filter from this CompositeCollisionFilter.
void removeCollisionFilter(const CollisionFilter* filter);

/// Removes all the collision filters from this CompositeCollisionFilter.
void removeAllCollisionFilters();

// Documentation inherited
bool ignoresCollision(
const CollisionObject* object1,
const CollisionObject* object2) const override;

protected:
/// Collision filters
std::unordered_set<const CollisionFilter*> mFilters;
};

class BodyNodeCollisionFilter : public CollisionFilter
{
public:
/// Add a BodyNode pair to the blacklist.
void addBodyNodePairToBlackList(
const dynamics::BodyNode* bodyNode1,
const dynamics::BodyNode* bodyNode2);

/// Remove a BodyNode pair from the blacklist.
void removeBodyNodePairFromBlackList(
const dynamics::BodyNode* bodyNode1,
const dynamics::BodyNode* bodyNode2);

/// Remove all the BodyNode pairs from the blacklist.
void removeAllBodyNodePairsFromBlackList();

// Documentation inherited
bool ignoresCollision(
const CollisionObject* object1,
const CollisionObject* object2) const override;

private:
/// Returns true if the two BodyNodes are adjacent BodyNodes (i.e., the two
/// BodyNodes are connected by a Joint).
bool areAdjacentBodies(const dynamics::BodyNode* bodyNode1,
const dynamics::BodyNode* bodyNode2) const;

/// Returns true if the BodyNode pair is in the blacklist.
bool hasBodyNodePairInBlacklist(
const dynamics::BodyNode* bodyNode1,
const dynamics::BodyNode* bodyNode2) const;

/// List of pairs to be excluded in the collision detection.
///
/// Each pair is stored so that the key of the std::unordered_map always has
/// a value less than every element in the set that is associated with it.
std::unordered_map<
const dynamics::BodyNode*,
std::unordered_set<const dynamics::BodyNode*>> mBlackList;
};

} // namespace collision
} // namespace dart
} // namespace collision
} // namespace dart

#endif // DART_COLLISION_COLLISIONFILTER_HPP_
2 changes: 1 addition & 1 deletion dart/collision/bullet/BulletCollisionDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ void filterOutCollisions(btCollisionWorld* world)
const auto collObj0 = static_cast<BulletCollisionObject*>(userPtr0);
const auto collObj1 = static_cast<BulletCollisionObject*>(userPtr1);

if (!filter->needCollision(collObj0, collObj1))
if (filter->ignoresCollision(collObj0, collObj1))
manifoldsToRelease.push_back(contactManifold);
}

Expand Down
2 changes: 1 addition & 1 deletion dart/collision/bullet/detail/BulletCollisionDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ bool BulletCollisionDispatcher::needsCollision(
const auto collObj1
= static_cast<BulletCollisionObject*>(body1->getUserPointer());

if (mFilter && !mFilter->needCollision(collObj0, collObj1))
if (mFilter && mFilter->ignoresCollision(collObj0, collObj1))
return false;

return btCollisionDispatcher::needsCollision(body0, body1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ bool BulletOverlapFilterCallback::needBroadphaseCollision(
const auto collObj0 = static_cast<BulletCollisionObject*>(userPtr0);
const auto collObj1 = static_cast<BulletCollisionObject*>(userPtr1);

return filter->needCollision(collObj0, collObj1);
return !filter->ignoresCollision(collObj0, collObj1);
}

return collide;
Expand Down
4 changes: 2 additions & 2 deletions dart/collision/dart/DARTCollisionDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ bool DARTCollisionDetector::collide(
{
auto* collObj2 = objects[j];

if (filter && !filter->needCollision(collObj1, collObj2))
if (filter && filter->ignoresCollision(collObj1, collObj2))
continue;

collisionFound = checkPair(collObj1, collObj2, option, result);
Expand Down Expand Up @@ -207,7 +207,7 @@ bool DARTCollisionDetector::collide(
{
auto* collObj2 = objects2[j];

if (filter && !filter->needCollision(collObj1, collObj2))
if (filter && filter->ignoresCollision(collObj1, collObj2))
continue;

collisionFound = checkPair(collObj1, collObj2, option, result);
Expand Down
2 changes: 1 addition & 1 deletion dart/collision/fcl/FCLCollisionDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,7 @@ bool collisionCallback(
assert(collisionObject1);
assert(collisionObject2);

if (!filter->needCollision(collisionObject2, collisionObject1))
if (filter->ignoresCollision(collisionObject2, collisionObject1))
return collData->done;
}

Expand Down
2 changes: 1 addition & 1 deletion dart/collision/ode/OdeCollisionDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ void CollisionCallback(void* data, dGeomID o1, dGeomID o2)
assert(collObj1);
assert(collObj2);

if (filter && !filter->needCollision(collObj1, collObj2))
if (filter && filter->ignoresCollision(collObj1, collObj2))
return;

// Perform narrow-phase collision detection
Expand Down
12 changes: 12 additions & 0 deletions dart/constraint/ConstraintSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,18 @@ collision::ConstCollisionGroupPtr ConstraintSolver::getCollisionGroup() const
return mCollisionGroup;
}

//==============================================================================
collision::CollisionOption& ConstraintSolver::getCollisionOption()
{
return mCollisionOption;
}

//==============================================================================
const collision::CollisionOption& ConstraintSolver::getCollisionOption() const
{
return mCollisionOption;
}

//==============================================================================
collision::CollisionResult& ConstraintSolver::getLastCollisionResult()
{
Expand Down
Loading