erincatto / box2d

Box2D is a 2D physics engine for games
https://box2d.org
MIT License
7.41k stars 1.47k forks source link

Collision Detection Problems with b2ContactEdge and b2Distance #766

Open nocholasrift opened 1 month ago

nocholasrift commented 1 month ago

Hi there! I am currently working with the author of mvsim: https://github.com/MRPT/mvsim, which is a robot simulator which uses Box2D for collision detection. Box2D works great for preventing objects from penetrating each other. However, when we try to set a flag to true to expose this collision to the user, the behavior is inconsistent. Specifically, the problem seems to happen mostly when two edges are face to face (parallel), but occasionally happens for corner edges.

Current there are two techniques used to detect collision:

  1. b2ContactEdge->contact->isTouching()
  2. b2Distance() with the fixtures -- it works great sometimes, others it returns a large distance, even if the two bodies are face to face with two edges colliding.

Here is a link to the issue in which we are discussing this problem: https://github.com/MRPT/mvsim/issues/42

Any help would be greatly appreciated! Is there a better way to detect collisions than this method?

Attached below is the dump file as requested as a follow up from my Discord post, where the robot crashed head on into an obstacle, and using both 1 and 2 failed to detect the collision: box2d_dump.zip

erincatto commented 1 month ago

Are calling b2Distance directly? I need the data you are sending to that specific function. The world dump doesn't give me the data you send to that function. You'll have to print the input yourself somehow.

I'm guessing you increased b2_maxPolygonVertices? But looking at your geometry it doesn't seem necessary. Also you are creating 514 joints between static bodies.

image

nocholasrift commented 1 month ago

Is there some cleaner way to accomplish this printout of the data other than going through the docs and printing every member of every struct and class used? This is the code being used to try and detect the collisions, would I be trying to print all the fields relevant to dO, dc, and di?

// Instantaneous collision flag:
isInCollision_ = false;
if (b2ContactEdge* cl = b2dBody_->GetContactList(); cl != nullptr)
{
    for (auto contact = cl->contact; contact != nullptr;
         contact = contact->GetNext())
    {
        // We may store with which other bodies it's in collision?
        const auto shA = cl->contact->GetFixtureA()->GetShape();
        const auto shB = cl->contact->GetFixtureB()->GetShape();
        if (cl->contact->GetFixtureA()->IsSensor()) continue;
        if (cl->contact->GetFixtureB()->IsSensor()) continue;

        b2DistanceInput di;
        di.proxyA.Set(shA, 0 /*index for chains*/);
        di.proxyB.Set(shB, 0 /*index for chains*/);
        di.transformA =
            cl->contact->GetFixtureA()->GetBody()->GetTransform();
        di.transformB =
            cl->contact->GetFixtureB()->GetBody()->GetTransform();
        di.useRadii = true;

        b2SimplexCache dc;
        dc.count = 0;
        b2DistanceOutput dO;
        b2Distance(&dO, &dc, &di);

        if (dO.distance < simulable_parent_->collisionThreshold() ||
            cl->contact->IsTouching())
            isInCollision_ = true;
    }
}
erincatto commented 1 month ago

Set a breakpoint before the call to b2Distance, then in the watch window put in di, expand it and then copy the values. You should also list the proxy vertices.

Here is some example data I captured in Visual Studio.

-       distanceInput   {proxyA={m_buffer=0x000000eb168fee90 {{...}, {...}} m_vertices=0x000000eb168ff1d0 {x=-4.00000000 y=1.00000000 } ...} ...}   b2DistanceInput
-       proxyA  {m_buffer=0x000000eb168fee90 {{x=-4.00000000 y=1.00000000 }, {x=-5.00000000 y=5.00000000 }} m_vertices=...} b2DistanceProxy
+       m_buffer    0x000000eb168fee90 {{x=-4.00000000 y=1.00000000 }, {x=-5.00000000 y=5.00000000 }}   b2Vec2[2]
+       m_vertices  0x000000eb168ff1d0 {x=-4.00000000 y=1.00000000 }    const b2Vec2 *
        m_count 2   int
        m_radius    0.00999999978   float
-       proxyB  {m_buffer=0x000000eb168feeb0 {{x=-107374176. y=-107374176. }, {x=-107374176. y=-107374176. }} m_vertices=...}   b2DistanceProxy
+       m_buffer    0x000000eb168feeb0 {{x=-107374176. y=-107374176. }, {x=-107374176. y=-107374176. }} b2Vec2[2]
+       m_vertices  0x000002a0fdb64c70 {x=0.00000000 y=0.00000000 } const b2Vec2 *
        m_count 1   int
        m_radius    1.00000000  float
-       transformA  {p={x=0.00000000 y=0.00000000 } q={s=0.00000000 c=1.00000000 } }    b2Transform
+       p   {x=0.00000000 y=0.00000000 }    b2Vec2
+       q   {s=0.00000000 c=1.00000000 }    b2Rot
-       transformB  {p={x=-3.27151895 y=6.73861551 } q={s=0.00000000 c=1.00000000 } }   b2Transform
+       p   {x=-3.27151895 y=6.73861551 }   b2Vec2
+       q   {s=0.00000000 c=1.00000000 }    b2Rot
        useRadii    false   bool
-       distanceInput.proxyA.m_vertices,2   0x000000eb168ff1d0 {{x=-4.00000000 y=1.00000000 }, {x=-5.00000000 y=5.00000000 }}   const b2Vec2[2]
+       [0] {x=-4.00000000 y=1.00000000 }   const b2Vec2
+       [1] {x=-5.00000000 y=5.00000000 }   const b2Vec2
-       distanceInput.proxyB.m_vertices,1   0x000002a0fdb64c70 {{x=0.00000000 y=0.00000000 }}   const b2Vec2[1]
+       [0] {x=0.00000000 y=0.00000000 }    const b2Vec2
nocholasrift commented 5 days ago

@erincatto Apologies for the delay, here is a a dump of what was contained in di right before the function call. Is the nan value I'm seeing in the proxyA m_buffer to blame? The setup is same as before, there is a head on collision between the robot and the obstacle, but the current way the box2D api is being used isn't detecting any collision (last if statement in code provided above is false).

    Locals      
        angle   0   float
        cl  @0x555555c7f8e0 b2ContactEdge
        contact @0x555555c7f8a0 b2Contact
        context @0x7fffffffdf60 mvsim::TSimulContext &
        dO  @0x7fffffffddd0 b2DistanceOutput
        dc  @0x7fffffffddf4 b2SimplexCache
        di  @0x7fffffffde00 b2DistanceInput
            proxyA  @0x7fffffffde00 b2DistanceProxy
                m_buffer    @0x7fffffffde00 b2Vec2[2]
                    [0] @0x7fffffffde00 b2Vec2
                        x   nan float
                        y   4.591634678053128e-41   float
                    [1] @0x7fffffffde08 b2Vec2
                        x   34816179306496.0    float
                        y   3.061136495317563e-41   float
                m_count 4   int32
                m_radius    0.00999999978   float
                m_vertices  @0x555555d56b08 b2Vec2
                    x   0.0299999937    float
                    y   0.144999996 float
            proxyB  @0x7fffffffde20 b2DistanceProxy
                m_buffer    @0x7fffffffde20 b2Vec2[2]
                    [0] @0x7fffffffde20 b2Vec2
                    [1] @0x7fffffffde28 b2Vec2
                m_count 14  int32
                m_radius    0.00249999994   float
                m_vertices  @0x555555fb9938 b2Vec2
                    x   0.0769287646    float
                    y   0   float
            transformA  @0x7fffffffde40 b2Transform
                p   @0x7fffffffde40 b2Vec2
                    x   -3.195467   float
                    y   5.40175343  float
                q   @0x7fffffffde48 b2Rot
                    c   -0.851679325    float
                    s   0.524063289 float
            transformB  @0x7fffffffde50 b2Transform
                p   @0x7fffffffde50 b2Vec2
                    x   -3.67499995 float
                    y   5.4749999   float
                q   @0x7fffffffde58 b2Rot
                    c   1   float
                    s   0   float
            useRadii    true    bool
        lck @0x7fffffffdd80 std::unique_lock<std::shared_mutex>
        pos @0x555555f45f2c b2Vec2 &
        shA @0x555555d56af0 b2Shape
        shB @0x555555fb9920 b2Shape
        this    @0x555555fd76d0 mvsim::Block
        vel @0x555555f45f60 b2Vec2 &
        w   0   float
    Inspector       
    Expressions     
    Return Value        
    Tooltip