How to find if vertex of collisionshape inside another mesh

jchen114
Posts: 12
Joined: Wed Jun 08, 2016 1:39 am

How to find if vertex of collisionshape inside another mesh

Post by jchen114 »

Hi all,
I have a specific set of vertices I'd like to keep track of on a collision shape. I was wondering if there was a way for me to find out if any of these vertices intersect another collisionshape efficiently.
Thanks.
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: How to find if vertex of collisionshape inside another m

Post by drleviathan »

There is probably more than one way to do it. The best way depends on the known details of the two objects and what you really want to know: are the vertices inside another object or do you also want to know how deep and in which direction?

You should read this wiki page about collision triggers and callbacks.

If you have a small list of vertices on ObjectA and you want to test them against a particular other ObjectB then you could perform a contactPairTest for each point against ObjectB. In theory (I haven't done it myself), you would for each vertex: (a) create a tiny proxy btCollisionObject, (b) put it at the vertex location, and then (c) call btCollisionWorld::contactPairTest(ObjectA, ObjectB, callback) where (d) you have written a custom callback to handle the collision results as they are computed.

Alternatively, if you know ObjectA and ObjectB are convex then you could also probably do ray tests on ObjectB from the center of ObjectA toward the vertices, or use a ray from behind the vertex toward the center of ObjectB. Look for information about btCollisionWorld::rayTestSingle().

If you want to know: "Are any of the special vertices are inside any nearby object?" then maybe you could slave little btCollisionObjects to the world-frame position of the vertices (kinematically), disable collision response on each proxy object, and give them a custom collision callback to harvest the information you want. If so, carefully read the Contact Callbacks section of the aforementioned wiki page.
jchen114
Posts: 12
Joined: Wed Jun 08, 2016 1:39 am

Re: How to find if vertex of collisionshape inside another m

Post by jchen114 »

For the last option, I have read that kinematic objects do not collide with other kinematic or static objects. Would the Callback still work with this? Would I be able to use a ghost object instead?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: How to find if vertex of collisionshape inside another m

Post by drleviathan »

Yes, kinematic objects don't "collide" with other kinematic objects because the effect of the collision is meaningless -- they're kinematic and move according to external logic. However, you could make your proxy objects dynamic (remember they would have no collision response) but manually slave them as if the were kinematic (maybe use an Action to do it). Hrm... maybe this idea won't work... the Action update is probably on the wrong side of the collision callback events in the loop.

I guess you could perform a manual per-vertex collision test with the world or... better yet, follow ObjectA around with a btPairCachingGhostObject and do a per-vertex collision test against its cache of overlapping objects.
jchen114
Posts: 12
Joined: Wed Jun 08, 2016 1:39 am

Re: How to find if vertex of collisionshape inside another m

Post by jchen114 »

I have a btPairCachingGhostObject for each vertex now.. but it looks like it is overlapping with the object that it is following which is good. But it doesn't collide with any static objects like my ground. I've played with different combinations of the flags but it still doesn't seem to work.
I've implemented it like this:

Code: Select all

sphere = std::unique_ptr<btSphereShape>(new btSphereShape(0.05)); 
btTransform xform;
//xform.setRotation(btQuaternion(0, 0, 0, 1));
xform.setOrigin(m_vertexPos);
//xform.setBasis(btMatrix3x3(btQuaternion(btVector3(0, 0, 1), 0.0)));
m_ghostVertex = std::unique_ptr<btPairCachingGhostObject>(new btPairCachingGhostObject());
m_ghostVertex->setCollisionShape(sphere.get());
m_ghostVertex->setWorldTransform(xform);
m_ghostVertex->setUserPointer(object);
m_ghostVertex->setCollisionFlags(btCollisionObject::CF_NO_CONTACT_RESPONSE);
ContactLearningApp::GetInstance()->GetWorld()->addCollisionObject(m_ghostVertex.get(), btBroadphaseProxy::AllFilter, btBroadphaseProxy::AllFilter);
On a side note, would I be able to find the normal vector of the edge that the vertex collided with?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: How to find if vertex of collisionshape inside another m

Post by drleviathan »

No, you only need one btPairCachingGhostObject. You give it the same shape as ObjectA if it's convex or the convex hull of ObjectA if it's not. Every frame you move the ghost so that it follows the ObjectA around (in an Action callback or something). The ghost won't actually collide with anything but maintains a short list of all objects near ObjectA (it get's the list from broadphase overlaps), and it provides an API that allows you to make manual sweep queries against only the objects in that subset. In other words: the only advantage of the ghost is efficiency.

BTW, I realize now that I look into it that the ghost API provides for efficient rayTest and convexSweepTest, not single collision query.

So, using ghost object or not, the idea was to do the following:

(1) Derive a class from ClosestConvexResultCallback like this:

Code: Select all

class VertexSweepResult : public btCollisionWorld::ClosestConvexResultCallback {
public:
    CharacterSweepResult(const btCollisionObject* objectA, const btCollisionObject* ghost);
        :   btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)),
            m_ObjectA(objectA), m_ghost(ghost)
    {
        assert(m_objectA);
        // set collision group and mask to match m_objectA
        // NOTE: if the collision groups are properly setup you might be able to get away without caching m_objectA and m_ghost in this class.
        m_objectA->getCollisionGroupAndMask(m_collisionFilterGroup, m_collisionFilterMask);
    }
    virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool useWorldFrame) override {
        // skip objects that we shouldn't collide with
        if (!convexResult.m_hitCollisionObject->hasContactResponse()
            || convexResult.m_hitCollisionObject == m_objectA
            || convexResult.m_hitCollisionObject == m_ghost) {
            return btScalar(1.0);
        }
        return ClosestConvexResultCallback::addSingleResult(convexResult, useWorldFrame);
    }
protected:
    const CharacterGhostObject* m_objectA;
    const CharacterGhostObject* m_ghost;

    // Public data members inherited from ClosestConvexResultCallback:
    //
    //  btVector3   m_convexFromWorld; // unused except by btClosestNotMeConvexResultCallback
    //  btVector3   m_convexToWorld;   // unused except by btClosestNotMeConvexResultCallback
    //  btVector3   m_hitNormalWorld;
    //  btVector3   m_hitPointWorld;
    //  const btCollisionObject*    m_hitCollisionObject;
    //
    // Public data members inherited from ConvexResultCallback:
    //
    //  btScalar    m_closestHitFraction;
    //  short int   m_collisionFilterGroup;
    //  short int   m_collisionFilterMask;
};
Then for each vertex every frame:

(2) Remember vertex's world transform from last frame.

(3) Compute vertex's new world transform.

(4) Sweep test:

Code: Select all

btScalar allowedPenetration = collisionWorld->getDispatchInfo().m_allowedCcdPenetration;
VertexSweepResult result(objectA, ghost);
ghost->convexSweepTest(vertexShape, oldTransform, newTransform, result, allowedPenetration);
(5) Check result.

I dunno it actually works and don't know if the code above compiles (I did not test). In theory you could do something similar without a ghost: use btCollisionWorld::convexSweepTest() instead.