NVIDIA-Omniverse / PhysX

NVIDIA PhysX SDK
BSD 3-Clause "New" or "Revised" License
2.42k stars 328 forks source link

TGS solver does not support PxPairFlag::eNOTIFY_THRESHOLD_FORCE_... flags #271

Open goncalo opened 3 months ago

goncalo commented 3 months ago

PhysX v5.3.1 OS: Windows 10

Steps to Trigger Behavior

  1. Run the snippet

Code Snippet to Reproduce Behavior

Snippet with the problem (small changes to SnippetContactReport.cpp)

#include <vector>
#include "PxPhysicsAPI.h"
#include "../snippetutils/SnippetUtils.h"
#include "../snippetcommon/SnippetPrint.h"
#include "../snippetcommon/SnippetPVD.h"

using namespace physx;

static PxDefaultAllocator       gAllocator;
static PxDefaultErrorCallback   gErrorCallback;
static PxFoundation*            gFoundation = NULL;
static PxPhysics*               gPhysics    = NULL;
static PxDefaultCpuDispatcher*  gDispatcher = NULL;
static PxScene*                 gScene      = NULL;
static PxMaterial*              gMaterial   = NULL;
static PxPvd*                   gPvd        = NULL;

std::vector<PxVec3> gContactPositions;
std::vector<PxVec3> gContactImpulses;

static PxFilterFlags contactReportFilterShader( PxFilterObjectAttributes attributes0, PxFilterData filterData0, 
                                                PxFilterObjectAttributes attributes1, PxFilterData filterData1,
                                                PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
{
    PX_UNUSED(attributes0);
    PX_UNUSED(attributes1);
    PX_UNUSED(filterData0);
    PX_UNUSED(filterData1);
    PX_UNUSED(constantBlockSize);
    PX_UNUSED(constantBlock);

    // all initial and persisting reports for everything, with per-point data
    pairFlags = PxPairFlag::eSOLVE_CONTACT | PxPairFlag::eDETECT_DISCRETE_CONTACT
              | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND
              | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS
              | PxPairFlag::eNOTIFY_CONTACT_POINTS;
    return PxFilterFlag::eDEFAULT;
}

class ContactReportCallback: public PxSimulationEventCallback
{
    void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count)  { PX_UNUSED(constraints); PX_UNUSED(count); }
    void onWake(PxActor** actors, PxU32 count)                          { PX_UNUSED(actors); PX_UNUSED(count); }
    void onSleep(PxActor** actors, PxU32 count)                         { PX_UNUSED(actors); PX_UNUSED(count); }
    void onTrigger(PxTriggerPair* pairs, PxU32 count)                   { PX_UNUSED(pairs); PX_UNUSED(count); }
    void onAdvance(const PxRigidBody*const*, const PxTransform*, const PxU32) {}
    void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs) 
    {
        PX_UNUSED((pairHeader));
        std::vector<PxContactPairPoint> contactPoints;

        for(PxU32 i=0;i<nbPairs;i++)
        {
            PxU32 contactCount = pairs[i].contactCount;
            if(contactCount)
            {
                contactPoints.resize(contactCount);
                pairs[i].extractContacts(&contactPoints[0], contactCount);

                for(PxU32 j=0;j<contactCount;j++)
                {
                    gContactPositions.push_back(contactPoints[j].position);
                    gContactImpulses.push_back(contactPoints[j].impulse);
                }
            }
        }
    }
};

ContactReportCallback gContactReportCallback;

static void createStack(const PxTransform& t, PxU32 size, PxReal halfExtent)
{
    PxShape* shape = gPhysics->createShape(PxBoxGeometry(halfExtent, halfExtent, halfExtent), *gMaterial);
    for(PxU32 i=0; i<size;i++)
    {
        for(PxU32 j=0;j<size-i;j++)
        {
            PxTransform localTm(PxVec3(PxReal(j*2) - PxReal(size-i), PxReal(i*2+1), 0) * halfExtent);
            PxRigidDynamic* body = gPhysics->createRigidDynamic(t.transform(localTm));
            body->attachShape(*shape);
            body->setContactReportThreshold(0.01);
            PxRigidBodyExt::updateMassAndInertia(*body, 10.0f);
            gScene->addActor(*body);
        }
    }
    shape->release();
}

void initPhysics(bool /*interactive*/)
{
    gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback);
    gPvd = PxCreatePvd(*gFoundation);
    PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(PVD_HOST, 5425, 10);
    gPvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
    gPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(),true,gPvd);
    PxInitExtensions(*gPhysics,gPvd);
    PxU32 numCores = SnippetUtils::getNbPhysicalCores();
    gDispatcher = PxDefaultCpuDispatcherCreate(numCores == 0 ? 0 : numCores - 1);
    PxSceneDesc sceneDesc(gPhysics->getTolerancesScale());
    sceneDesc.cpuDispatcher = gDispatcher;
    sceneDesc.gravity = PxVec3(0, -9.81f, 0);
    sceneDesc.filterShader  = contactReportFilterShader;            
    sceneDesc.simulationEventCallback = &gContactReportCallback;    
    sceneDesc.solverType = PxSolverType::eTGS;
    gScene = gPhysics->createScene(sceneDesc);

    PxPvdSceneClient* pvdClient = gScene->getScenePvdClient();
    if(pvdClient)
    {
        pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true);
    }
    gMaterial = gPhysics->createMaterial(0.5f, 0.5f, 0.6f);

    PxRigidStatic* groundPlane = PxCreatePlane(*gPhysics, PxPlane(0,1,0,0), *gMaterial);
    gScene->addActor(*groundPlane);
    createStack(PxTransform(PxVec3(0,3.0f,10.0f)), 5, 2.0f);
}

void stepPhysics(bool /*interactive*/)
{
    gContactPositions.clear();
    gContactImpulses.clear();

    gScene->simulate(1.0f/60.0f);
    gScene->fetchResults(true);
    printf("%d contact reports\n", PxU32(gContactPositions.size()));
}

void cleanupPhysics(bool /*interactive*/)
{
    PX_RELEASE(gScene);
    PX_RELEASE(gDispatcher);
    PxCloseExtensions();
    PX_RELEASE(gPhysics);
    if(gPvd)
    {
        PxPvdTransport* transport = gPvd->getTransport();
        gPvd->release();    gPvd = NULL;
        PX_RELEASE(transport);
    }
    PX_RELEASE(gFoundation);

    printf("SnippetContactReport done.\n");
}

int snippetMain(int, const char*const*)
{
#ifdef RENDER_SNIPPET
    extern void renderLoop();
    renderLoop();
#else
    initPhysics(false);
    for(PxU32 i=0; i<250; i++)
        stepPhysics(false);
    cleanupPhysics(false);
#endif

    return 0;
}

Snippet without problem (just changed solver type to ePGS)

#include <vector>
#include "PxPhysicsAPI.h"
#include "../snippetutils/SnippetUtils.h"
#include "../snippetcommon/SnippetPrint.h"
#include "../snippetcommon/SnippetPVD.h"

using namespace physx;

static PxDefaultAllocator       gAllocator;
static PxDefaultErrorCallback   gErrorCallback;
static PxFoundation*            gFoundation = NULL;
static PxPhysics*               gPhysics    = NULL;
static PxDefaultCpuDispatcher*  gDispatcher = NULL;
static PxScene*                 gScene      = NULL;
static PxMaterial*              gMaterial   = NULL;
static PxPvd*                   gPvd        = NULL;

std::vector<PxVec3> gContactPositions;
std::vector<PxVec3> gContactImpulses;

static PxFilterFlags contactReportFilterShader( PxFilterObjectAttributes attributes0, PxFilterData filterData0, 
                                                PxFilterObjectAttributes attributes1, PxFilterData filterData1,
                                                PxPairFlags& pairFlags, const void* constantBlock, PxU32 constantBlockSize)
{
    PX_UNUSED(attributes0);
    PX_UNUSED(attributes1);
    PX_UNUSED(filterData0);
    PX_UNUSED(filterData1);
    PX_UNUSED(constantBlockSize);
    PX_UNUSED(constantBlock);

    // all initial and persisting reports for everything, with per-point data
    pairFlags = PxPairFlag::eSOLVE_CONTACT | PxPairFlag::eDETECT_DISCRETE_CONTACT
              | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_FOUND
              | PxPairFlag::eNOTIFY_THRESHOLD_FORCE_PERSISTS
              | PxPairFlag::eNOTIFY_CONTACT_POINTS;
    return PxFilterFlag::eDEFAULT;
}

class ContactReportCallback: public PxSimulationEventCallback
{
    void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count)  { PX_UNUSED(constraints); PX_UNUSED(count); }
    void onWake(PxActor** actors, PxU32 count)                          { PX_UNUSED(actors); PX_UNUSED(count); }
    void onSleep(PxActor** actors, PxU32 count)                         { PX_UNUSED(actors); PX_UNUSED(count); }
    void onTrigger(PxTriggerPair* pairs, PxU32 count)                   { PX_UNUSED(pairs); PX_UNUSED(count); }
    void onAdvance(const PxRigidBody*const*, const PxTransform*, const PxU32) {}
    void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs) 
    {
        PX_UNUSED((pairHeader));
        std::vector<PxContactPairPoint> contactPoints;

        for(PxU32 i=0;i<nbPairs;i++)
        {
            PxU32 contactCount = pairs[i].contactCount;
            if(contactCount)
            {
                contactPoints.resize(contactCount);
                pairs[i].extractContacts(&contactPoints[0], contactCount);

                for(PxU32 j=0;j<contactCount;j++)
                {
                    gContactPositions.push_back(contactPoints[j].position);
                    gContactImpulses.push_back(contactPoints[j].impulse);
                }
            }
        }
    }
};

ContactReportCallback gContactReportCallback;

static void createStack(const PxTransform& t, PxU32 size, PxReal halfExtent)
{
    PxShape* shape = gPhysics->createShape(PxBoxGeometry(halfExtent, halfExtent, halfExtent), *gMaterial);
    for(PxU32 i=0; i<size;i++)
    {
        for(PxU32 j=0;j<size-i;j++)
        {
            PxTransform localTm(PxVec3(PxReal(j*2) - PxReal(size-i), PxReal(i*2+1), 0) * halfExtent);
            PxRigidDynamic* body = gPhysics->createRigidDynamic(t.transform(localTm));
            body->attachShape(*shape);
            body->setContactReportThreshold(0.01);
            PxRigidBodyExt::updateMassAndInertia(*body, 10.0f);
            gScene->addActor(*body);
        }
    }
    shape->release();
}

void initPhysics(bool /*interactive*/)
{
    gFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, gAllocator, gErrorCallback);
    gPvd = PxCreatePvd(*gFoundation);
    PxPvdTransport* transport = PxDefaultPvdSocketTransportCreate(PVD_HOST, 5425, 10);
    gPvd->connect(*transport,PxPvdInstrumentationFlag::eALL);
    gPhysics = PxCreatePhysics(PX_PHYSICS_VERSION, *gFoundation, PxTolerancesScale(),true,gPvd);
    PxInitExtensions(*gPhysics,gPvd);
    PxU32 numCores = SnippetUtils::getNbPhysicalCores();
    gDispatcher = PxDefaultCpuDispatcherCreate(numCores == 0 ? 0 : numCores - 1);
    PxSceneDesc sceneDesc(gPhysics->getTolerancesScale());
    sceneDesc.cpuDispatcher = gDispatcher;
    sceneDesc.gravity = PxVec3(0, -9.81f, 0);
    sceneDesc.filterShader  = contactReportFilterShader;            
    sceneDesc.simulationEventCallback = &gContactReportCallback;    
    sceneDesc.solverType = PxSolverType::ePGS;
    gScene = gPhysics->createScene(sceneDesc);

    PxPvdSceneClient* pvdClient = gScene->getScenePvdClient();
    if(pvdClient)
    {
        pvdClient->setScenePvdFlag(PxPvdSceneFlag::eTRANSMIT_CONTACTS, true);
    }
    gMaterial = gPhysics->createMaterial(0.5f, 0.5f, 0.6f);

    PxRigidStatic* groundPlane = PxCreatePlane(*gPhysics, PxPlane(0,1,0,0), *gMaterial);
    gScene->addActor(*groundPlane);
    createStack(PxTransform(PxVec3(0,3.0f,10.0f)), 5, 2.0f);
}

void stepPhysics(bool /*interactive*/)
{
    gContactPositions.clear();
    gContactImpulses.clear();

    gScene->simulate(1.0f/60.0f);
    gScene->fetchResults(true);
    printf("%d contact reports\n", PxU32(gContactPositions.size()));
}

void cleanupPhysics(bool /*interactive*/)
{
    PX_RELEASE(gScene);
    PX_RELEASE(gDispatcher);
    PxCloseExtensions();
    PX_RELEASE(gPhysics);
    if(gPvd)
    {
        PxPvdTransport* transport = gPvd->getTransport();
        gPvd->release();    gPvd = NULL;
        PX_RELEASE(transport);
    }
    PX_RELEASE(gFoundation);

    printf("SnippetContactReport done.\n");
}

int snippetMain(int, const char*const*)
{
#ifdef RENDER_SNIPPET
    extern void renderLoop();
    renderLoop();
#else
    initPhysics(false);
    for(PxU32 i=0; i<250; i++)
        stepPhysics(false);
    cleanupPhysics(false);
#endif

    return 0;
}

Expected Behavior

Console should log some contact reports.

Actual Behavior

Console never logs any contact reports. Not sure if this is by design or not. If it is, I couldn't find any mention in the docs.

preist-nvidia commented 2 months ago

Hi @goncalo - thank you for the snippet repro, very much appreciated! We'll have a look. Internal tracking: PX-5037.

vreutskyy commented 2 months ago

Hi @goncalo. Yes, you're right, these flags don't work with the TGS solver in the current version. We plan to fix this in future versions, but for now you'll need to use the PGS solver or implement your own force threshold logic using contact reports. Thanks for the report and for the repro.