ConvexCast not setting contact body

Report any bugs here and we'll post fixes

Moderators: Sascha Willems, Thomas

ConvexCast not setting contact body

Postby Esharc » Tue May 09, 2023 6:54 am

Hi Julio,

When I perform a ConvexCast, the bodies of m_contacts of the cast calback does not contain the data for the body that is hit.

I then noticed that if I set the bodies in
Code: Select all
ConvexToConvexContactsContinue()
it works correctly.

Code: Select all
if ((num <= ndFloat32(1.0e-5f)) && (tacc <= timestep))
      {
         // bodies collide at time tacc, but we do not set it yet
         ndVector step(relVeloc.Scale(tacc));
         m_timestep = tacc;
         closestPoint1 = m_closestPoint1 + step;
         m_separatingVector = m_separatingVector * ndVector::m_negOne;
         ndFloat32 penetration = ndMax(num * ndFloat32(-1.0f) + D_PENETRATION_TOL, ndFloat32(0.0f));
         if (m_contactBuffer && !m_intersectionTestOnly)
         {
            if (ndInt8(m_instance0.GetCollisionMode()) & ndInt8(m_instance1.GetCollisionMode()))
            {
               count = CalculateContacts(m_closestPoint0, m_closestPoint1, m_separatingVector);
               if (count)
               {
                  count = ndMin(m_maxCount, count);
                  ndContactPoint* const contactOut = m_contactBuffer;

                  for (int i = 0; i < count; ++i)
                  {
                     contactOut[i].m_point = m_hullDiff[i] + step;
                     contactOut[i].m_normal = m_separatingVector;
                     contactOut[i].m_penetration = penetration;
                     contactOut[i].m_body0 = body0;
                     contactOut[i].m_body1 = body1;
                  }
               }
            }
         }
         break;
      }


I tried verifying this in the demo sandbox, but it seems that you only use the convex cast when placing objects on the floor. When I tried loading the basic stack demo, I get an assert in
Code: Select all
ndPolygonMeshDesc::ndPolygonMeshDesc()
   :ndFastAabb()
   ,m_boxDistanceTravelInMeshSpace(ndVector::m_zero)
   ,m_maxT(ndFloat32(1.0f))
   ,m_threadId(0)
   ,m_ownTempBuffers(false)
   ,m_doContinueCollisionTest(false)
{
    ndAssert(0);
}


My test is basically a sphere shape cast from the terrain, I then let a cube fall to the terrain and when the ConvexCast returns true I create a ndKinematicController to "catch" the falling cube.
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: ConvexCast not setting contact body

Postby Julio Jerez » Tue May 09, 2023 9:40 am

the assert was not necessary, was just to see where the call came from, I removed it.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: ConvexCast not setting contact body

Postby Julio Jerez » Tue May 09, 2023 5:53 pm

ok, on this.
I was not the happy with the fix for this, because it was creation, and vector on each call.
That problem was already there, and for that reason I made class ndPolygonMeshDesc

but that is not a solution either because the vector container is not allocated, be the memory buffer is. and that could lead pattern to bad memory behavior.
the idea is to make the class use the buffers that in in scene. which are on per thread.

that easy for the engine, but for the collisions, a way to make it aware of the scene, is by invoking like this.

Code: Select all
         ndContactSolver solver;
         ndContactNotify notification(world->GetScene());
         ndFixSizeArray<ndContactPoint, 16> contactOut;
         ndBodyKinematic* bodyA = contact->GetBody0();
         ndBodyKinematic* bodyB = contact->GetBody1();
         const ndShapeInstance& shapeA = bodyA->GetCollisionShape();
         const ndShapeInstance& shapeB = bodyB->GetCollisionShape();

         solver.CalculateContacts(
            &shapeA, bodyA->GetMatrix(), bodyA->GetVelocity(),
            &shapeB, bodyB->GetMatrix(), bodyB->GetVelocity(),
            contactOut, &notification);


you may have to make that change
ndContactNotify notification(world->GetScene());

the other solution works, and will be ideal, but is not practical for real time,
specially for user like you that use high dense meshes.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: ConvexCast not setting contact body

Postby Esharc » Wed May 10, 2023 6:03 am

This one was quite hard for me to reproduce. I am not using the contact solver in this instance, but calling ConvexCast from ndWorld. Here is code that reproduces the problem:

Code: Select all
#include "ndSandboxStdafx.h"
#include "ndSkyBox.h"
#include "ndDemoMesh.h"
#include "ndDemoCamera.h"
#include "ndPhysicsUtils.h"
#include "ndPhysicsWorld.h"
#include "ndMakeStaticMap.h"
#include "ndDemoEntityManager.h"
#include "ndDemoInstanceEntity.h"
#include "ndJointKinematicController.h"
#include "ndDemoDebugMesh.h"

class CConvexCaster : public ndModel
{
public:
   class CConvexCastCallBack : public ndConvexCastNotify
   {
   public:
      CConvexCastCallBack()
         : ndConvexCastNotify()
      {}
      virtual ndUnsigned32 OnRayPrecastAction(const ndBody* const pBody, const ndShapeInstance* const pShapeInstance) override
      {
         return 1;
      }
      virtual ndFloat32 OnRayCastAction(const ndContactPoint& contact, ndFloat32 intersectParam) override
      {
         return 0;
      }
   };

   CConvexCaster(ndDemoEntityManager* const pScene, ndBodyKinematic* const pParentBody)
      : ndModel()
      , m_CastShape(new ndShapeSphere(5.0))
      , m_pKinJoint(nullptr)
      , m_pScene(pScene)
      , m_pParentBody(pParentBody)
   {
   }
   
   virtual void OnAddToWorld() override {}
   virtual void OnRemoveFromToWorld() override {}

   virtual void Update(ndWorld* const world, ndFloat32 timestep) override
   {
      CConvexCastCallBack castCallback;

      if (m_world->ConvexCast(castCallback, m_CastShape, ndGetIdentityMatrix(), ndVector(0.0, 0.001, 0.0, 1.0)))
      {

         if (castCallback.m_contacts.GetCount() > 0)
         {
            if (castCallback.m_contacts[0].m_body1)
            {
               auto pHitBody = castCallback.m_contacts[0].m_body1->GetNotifyCallback()->GetBody()->GetAsBodyKinematic();
               auto pUserData = static_cast<ndDemoEntity*>(castCallback.m_contacts[0].m_body1->GetNotifyCallback()->GetUserData());

               ndAssert(pUserData->GetName() == "My Collision Object");

               if (!m_pKinJoint)
               {
                  m_pKinJoint = new ndJointKinematicController(pHitBody, m_pParentBody, castCallback.m_contacts[0].m_point);
                  m_pKinJoint->SetMaxAngularFriction(1000);
                  m_pKinJoint->SetMaxLinearFriction(1000);
                  ndSharedPtr<ndJointBilateralConstraint> jointPtr(m_pKinJoint);
                  m_world->AddJoint(jointPtr);
               }
            }
         }
      }
   }

   virtual void Debug(ndConstraintDebugCallback& context) const override
   {
      ndWireFrameDebugMesh sharedEdgeMesh = ndWireFrameDebugMesh(m_pScene->GetShaderCache(), &m_CastShape);
      const ndVector color(0.5f, 0.5f, 0.5f, 1.0f);
      sharedEdgeMesh.SetColor(color);
      sharedEdgeMesh.Render(m_pScene, ndGetIdentityMatrix());
   }

private:
   ndShapeInstance m_CastShape;
   ndBodyKinematic* m_pParentBody;
   ndJointKinematicController* m_pKinJoint;
   ndDemoEntityManager* m_pScene;
};



ndBodyKinematic* BuildHeightField(ndDemoEntityManager* const scene)
{
    int iDim = 64;
    ndFloat32 dSize = 128, dMaxHeight = 0.0;
    std::vector<ndFloat32> aData; aData.resize(iDim * iDim);
   ndFloat32 fHorizontalScale = ndFloat32(128.0 / (iDim - 1));
   ndShapeInstance shape(new ndShapeHeightfield(iDim, iDim, ndShapeHeightfield::m_normalDiagonals, fHorizontalScale, fHorizontalScale));
   ndMatrix mLocal(ndGetIdentityMatrix());
   mLocal.m_posit = ndVector(-(dSize * 0.5), 0.0, -(dSize * 0.5), 1.0);
   shape.SetLocalMatrix(mLocal);
   auto pShapeHeightField = shape.GetShape()->GetAsShapeHeightfield();

   for (int i = 0; i < iDim * iDim; ++i)
      pShapeHeightField->GetElevationMap()[i] = rand() * 2.0 * dMaxHeight / RAND_MAX;

   pShapeHeightField->UpdateElevationMapAabb();
   ndMatrix uvMatrix(ndGetIdentityMatrix());
   uvMatrix[0][0] *= 0.025f;
   uvMatrix[1][1] *= 0.025f;
   uvMatrix[2][2] *= 0.025f;

   ndSharedPtr<ndDemoMeshInterface>geometry(new ndDemoMesh("box", scene->GetShaderCache(), &shape, "marbleCheckBoard.tga", "marbleCheckBoard.tga", "marbleCheckBoard.tga", 1.0f, uvMatrix, false));
   ndMatrix location(ndGetIdentityMatrix());
   ndDemoEntity* const entity = new ndDemoEntity(location, nullptr);
   entity->SetMesh(geometry);

   ndBodyKinematic* const body = new ndBodyDynamic();
   body->SetMatrix(location);
   body->SetCollisionShape(shape);
   ndSharedPtr<ndBody> bodyPtr(body);
   scene->GetWorld()->AddBody(bodyPtr);
   scene->AddEntity(entity);
   return body;
}

void AddBody(ndDemoEntityManager* const scene)
{
   ndVector vStart(5.0, 10.0, 0.0, 1.0);
   ndMatrix location(ndGetIdentityMatrix());
   location.m_posit = vStart;
   auto pBody = AddBox(scene, location, 1.0f, 1.0f, 1.0f, 1.0f);
   auto pUserData = static_cast<ndDemoEntity*>(pBody->GetNotifyCallback()->GetUserData());
   pUserData->SetName("My Collision Object");
}

void ndConvexCastTest(ndDemoEntityManager* const scene)
{
    // build the height field
    auto pFloorBody = BuildHeightField(scene);
   AddBody(scene);
   ndSharedPtr<ndModel> convexCaster(new CConvexCaster(scene, pFloorBody));
   scene->GetWorld()->AddModel(convexCaster);

    ndQuaternion rot;
    ndVector origin(-10.0f, 5.0f, 0.0f, 1.0f);
    scene->SetCameraMatrix(rot, origin);
}


If you enable "show models debug info" the sphere that I am casting will be drawn. The body1 that is returned is always null, so the box falls through the casting sphere.

The idea is that as the box falls and a convex cast returns true, a kinematic controller is created to hold the box at the point where the convex cast is hit
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: ConvexCast not setting contact body

Postby Julio Jerez » Wed May 10, 2023 10:06 am

I masted in the sandbox, and for what I can see, that is not going to work.

the start position is: p0(0, 0, 0)
the end position is p1(0, 0.001, 0)

that's like using that cast as a collision, but I will fail, because the small box is already penetration the sphere, plus it is at a location that even id it touches the sphere, it will no product a contact because the direction of motion is parallel to the contact normal.

here is a cross section.
Untitled.png
Untitled.png (3.02 KiB) Viewed 7410 times


if you set a break point on thsi funtion

Code: Select all
      virtual ndUnsigned32 OnRayPrecastAction(const ndBody* const body, const ndShapeInstance* const) override
      {
         return 1;
      }


you will see that it is call twice, one for the floor, and one for the box,
since is return 1, it proceeds to calculate contacts.
but both low level routine return no contacts.
the terrain because the ball is moving away.
the box because it is moving tangent to the contact point.

I assume you want to just check the contact with teh small box,

if that is the case, you have to filer the object you are not interested in, like the terrain but using some tag, and base on that return 1 or 0. for thsi case check the mass, would be fine.

for the Box, I assuming the dest p(0, 0.001, 0) was a mistake and you meant

p1 (0.001, 0, 0)


anyway, I set that and did no get a contact either, and I do not know why, I will check it tonight.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: ConvexCast not setting contact body

Postby Julio Jerez » Wed May 10, 2023 2:23 pm

al right, did some more check and at first I though the cast direction was the case of failing to report a hit. so I changed and still failed, then I saw the change was also incorrect son eth contact was perpendicular to the direction of travel.
after making this change
Code: Select all
      //if (m_world->ConvexCast(castCallback, m_CastShape, ndGetIdentityMatrix(), ndVector(0.0, 0.001, 0.0, 1.0)))
      //if (m_world->ConvexCast(castCallback, m_CastShape, ndGetIdentityMatrix(), ndVector(0.0, 0.0, 0.001, 1.0)))
      if (m_world->ConvexCast(castCallback, m_CastShape, ndGetIdentityMatrix(), ndVector(0.001, 0.0, 0.0, 1.0)))


the querie does report a hit and get one contact, the Ig tpe whet actual bog that you found,

it failed to populate the contact array with the hit body. so that is the actual bug.

It should be easy to fix. so I will check that later.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: ConvexCast not setting contact body

Postby Julio Jerez » Wed May 10, 2023 4:24 pm

ok, this i now fixed.

notice that that I simplify your code, particularly the call bellow
Code: Select all
if (m_world->ConvexCast(castCallback, m_CastShape, ndGetIdentityMatrix(), ndVector(0.001, 0.0, 0.0, 1.0)))


tracing perpendicular to the contact normal will genera a faulty result.
this is because contact are considered valid, only when the projection of the normal over the velocity at the contact point is negative.
so in this case because is a manually setup there are truly perpendicular and by shear rounding error you may get a false positive.

I also noticed you used the ndModel to set that simple test case. 8) :o
I hope you realized that the same setup can be more complex when only using Bodies and Joints.

again, ndModels are like a catalyst in a chemical reaction, they are not part of the chemistry, but can modulate it.

anyway, if you sync, this should be good now.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: ConvexCast not setting contact body

Postby Esharc » Thu May 11, 2023 1:17 am

Thank you.

The contacts are working on my side now. But I noticed that the boxes first collision points does not hit on the sphere, but it looks as if it is on the AABB bounds of the sphere. I noticed that it is the same in the one I sent you that works in the demo sandbox.

Should I be checking if the castCallback.m_param value is within a limit?

Yes the ndModel works very nice. I will be looking at adding it into our system in the near future
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa

Re: ConvexCast not setting contact body

Postby Julio Jerez » Thu May 11, 2023 9:04 am

the objects are in deep penetration, when that happens, the collision routine finds the points of deepest penetration that when translated along the normal, will clear the penetration.
That is, there will be two points, one of shape A and one on shape B.
when the collision is normal contacts, the points are close, so any point along the line defined by those two is a valid collision point.
the newton routine heuristically takes the average of the two points.

In the example, we know all the parameters, so the two points will be:
one on the surface of the box p (4.5, 0.001, 0.0)
and one on the surface of the sphere q (5.0, 0.001, 0.0)
the expected average will be around d (4.75, 0.001, 0.0)

and if you set a break point on the demo after in fine the collisions, debugger will show this
castCallback.m_contacts.m_array[0].m_point {4.74951172, 0.00103048631, 0.00000000, 1.00000000}


if you want the normal and points on the surface of each shape, that will be parameters.
m_normal, m_closestPoint0, m_closestPoint1 of class ndConvexCastNotify

and for the param, yes you should check it,
m_param represents the parametric fraction of the length from
position of shape A to position of shape B,
along the unit vector normal = normalized (PositA - PositB)

since the shape are in penetration, it is expected to be 0 in this test.
Julio Jerez
Moderator
Moderator
 
Posts: 12249
Joined: Sun Sep 14, 2003 2:18 pm
Location: Los Angeles

Re: ConvexCast not setting contact body

Postby Esharc » Thu May 11, 2023 10:03 am

I see, thank you. I will add the check for the param.
Esharc
 
Posts: 120
Joined: Tue Jan 10, 2017 5:23 am
Location: South Africa


Return to Bugs and Fixes

Who is online

Users browsing this forum: No registered users and 28 guests