PhysX - Scene scaling, object size, timestep and gravity inconsistency.

Started by
29 comments, last by N01 10 years, 7 months ago

Hey, i've started integrating PhysX into my graphical engine recently(purpose is basic collision with level, which is represented by static primitives and some debris, like cans, barrels and bottles reacting at bullets and impact with a player). Currently done is representing the scene with static primitives and using convex hulls for dynamic actors. And at this stage i've noticed PhysX acting not exactly like one would expect. I'll try to describe it as verbose as possible, and i would like to hear some logical explanation and advice.

My initial setup was:

  • Timestep 1/30.0f, called in a separate from rendering thread, which runs at 30 FPS.
  • simulate() and fetchResults() Are called exactly, as in example from official documentation.
  • Gravity: Y = -9.8
  • Material for static actors: 1.0f, 1.0f, 0.5f (static friction, dynamic friction, restitution)
  • Material for dynamic actors: 0.5f, 0.5f, 0.3f
  • Static actors don't have any other parameters. All actors are positioned and scaled correctly, checked it through PVD.

As for dynamic actors:


setMass(PxReal(Objects[f].weight)); //currently 1-30 for different objects
setLinearDamping(PxReal(0.05f));
setAngularDamping(PxReal(0.05f));

i also set setCMassLocalPose to model's relative center(because some models may be not centered correctly).


First of all, i would expect to just set object's mass in kilograms, or at least relative to other objects, with default gravity of -9.8 and timestep of 30. Neither worked. Objects with mass of hundreds or thousands kilograms end up falling through the floor or bouncing crazily. And smaller objects with lower mass were too floaty.

At first, all big\medium-sized objects were freaking out. And by looking at PVD's grid in comparison with my scene, i figured i need to rescale my scene. My level is up to 3000 by 3000 units size. Medium-sized object is 20-30 units, and small object is around 2-4 units of size. So in process passing physical objects to PhysX, i now rescale them and their positions by some factor(now it's 0.05f). And it made things better.

But there are significant problems remaining. First thing i don't get is why scene get's slo-mo, if i decrease time step size. PhysX Tips page says "Few substeps: moon gravity effects", so it's vice-versa.
Actors with bigger mass still act inadequately.
Different scaling factor affects simulation speed. So setting scale to a smaler value, makes everything move more rapidly and with less inertia\bounce. With smaller scene scale, smaller objects act like they have enormous friction or low bounce\angular velocity.

And that leads to the main problem i can't solve yet is that with scene, rescaled by 0.05f smaller objects(like soda can or a pack of cigarettes) act really floaty. They don't react well on collision, they rotate too slowly and don't bounce. I have a scene with a character model, some barrels, a big box and soda can, represented with convex hulls. And environment, represented with boxes, spheres and capsules. I fly around and shoot spheres at them, and look how they react. Medium-sized objects have a mass of 20-30, and small objects have a mass of 5(yeah, i did mention relative mass inconsistency. if i put more appropriate relative weight to smaller objects, they act even floatier and don't react much on a sphere with 100000 density and while falling, they act more like poo).

I just don't want to fiddle with magic numbers. I would like to find some explanation and make it more predictable and consistent.

P.S. PhysX version is 3.2.4 and platform is Windows.
P.S.S. I hope this is an appropriate forum for such questions.

Advertisement

Physx...you might find yourself in trouble....there are not many(almost none) tutorials,or documentations for it.I mean sure,there's the official one,but that's it.So you're kinda on your own... (i'm talking about the current version)

BTW i like how your game looks

Well, first I'd try leaving all the weird scaling factors untouched and decide exactly the measures of my world. Start with 1 unit = 1m and begin experimenting with some convex hulls no larger than 5 units (unscaled). Too large objects can be deceiving as they may seem to fall in slo-mo. After these "standard sized" actors are behaving correctly, you can start putting more arbitrary actors in. And if physx provides any scaling factors I strongly suggest ignoring them as they can initially cause problems exactly like these.

if i pass my scene, without re-scaling it to lower size with all the default parameters, it's super slow-mo. and objects with the mass of dozens already start to freak out(penetrating through static actors, bouncing weirdly or falling through completely). smaller objects(up to 5 units) are also slow-mo. that's why i figured problem is my scene's size, but it just doesn't sound right.

here's how i set object's transformations:


 glmTempMatrix = glm::translate(glm::mat4(), Objects[f].position.toVec3());
 glmTempMatrix = glm::rotate(glmTempMatrix , Objects[f].rotation.x, 1.0f, 0.0f, 0.0f);
 glmTempMatrix = glm::rotate(glmTempMatrix , Objects[f].rotation.y, 0.0f, 1.0f, 0.0f);
 glmTempMatrix = glm::rotate(glmTempMatrix , Objects[f].rotation.z, 0.0f, 0.0f, 1.0f);

 pxTempMatrix = PxMat44(glm::value_ptr(glmTempMatrix));

 convexEntities[dynamicEntityId].mDynamicActor->setGlobalPose(PxTransform(pxTempMatrix));

 //note: not re-scaling factor, jsut a normal objects's scale in a real scene.
 PxMeshScale scale(PxVec3(Objects[f].scale.x, Objects[f].scale.y, Objects[f].scale.z), PxQuat::createIdentity());
 PxShape* aConvexShape = convexEntities[dynamicEntityId].mDynamicActor->createShape(PxConvexMeshGeometry(convexMesh, scale), *mDynamicMaterial);
That PxMeshScale is probably what you should attempt to get rid of, for now. Scale should be 1.0, and the actor size determined entirely by the convex hull dimensions (at the point where you "cook" it). How big is the convex hull anyway, I mean the approximate distance between two furthest points forming it?
At least in earlier versions of physx 3.x, that PxMeshScale didn't fix oversized convex hulls. Did you already try unit spheres (for example), to see if they make any difference in speed? And how's your initialization code?

That PxMeshScale is probably what you should attempt to get rid of, for now. Scale should be 1.0, and the actor size determined entirely by the convex hull dimensions (at the point where you "cook" it). How big is the convex hull anyway, I mean the approximate distance between two furthest points forming it?

ok, tried pre-scaling convex hull(by multiplying vertices), thus removing PxMeshScale. everything acts exactly the same.

to clarify, my convex hulls are based on the model. but this model can be used by different entities, with different scaling. that's it. so i rescale initial convex hull by that entitiy's scale using PxMeshScale, before passing it to the scene, so it fits perfectly. and they look fine in PVD. so data is 100% ok, but i may set some parameters wrong. documentation is not clear on call order and physical parameters of objects and doesn't say much about scale\mass.

At least in earlier versions of physx 3.x, that PxMeshScale didn't fix oversized convex hulls. Did you already try unit spheres (for example), to see if they make any difference in speed? And how's your initialization code?

just tried PxSphereGeometry(1.0f) instead of convex geometry, they're in slow-mo as well, but they don't fall through static stuff(i guess, cause sphere collision is really basic and predictable).

my PhysX base scene initialization:


        mFoundation = PxCreateFoundation(
            PX_PHYSICS_VERSION,
            gDefaultAllocatorCallback,
            gDefaultErrorCallback);

        mCooking = PxCreateCooking(PX_PHYSICS_VERSION, *mFoundation, PxCookingParams());

        mPhysics = PxCreatePhysics(
            PX_PHYSICS_VERSION,
            *mFoundation,
            PxTolerancesScale());

        PxInitExtensions(*mPhysics);

        PxSceneDesc sceneDesc(mPhysics->getTolerancesScale());
        sceneDesc.gravity = PxVec3(0.0f, -9.8f, 0.0f);

        if(!sceneDesc.cpuDispatcher)
        {
            PxDefaultCpuDispatcher* mCpuDispatcher = PxDefaultCpuDispatcherCreate(1);
            sceneDesc.cpuDispatcher = mCpuDispatcher;
        }

        if(!sceneDesc.filterShader)
            sceneDesc.filterShader  = gDefaultFilterShader;


        mScene = mPhysics->createScene(sceneDesc);

Timestep:

mStepSize = 1.0f / 30.0f;


float deltaTime = clock() - mLastTime;
        mAccumulator  += deltaTime;
        if(mAccumulator < mStepSize)
            return;

        mAccumulator -= mStepSize;

        mScene->simulate(mStepSize);
        mScene->fetchResults(true);

        mLastTime = clock();

running in a separate thread at 30 FPS.

Hm ok, so that's not the problem then. I recall that scale thing being a source of some problems earlier, but don't know about that anymore. I'm using 3.3 beta 2 myself now.

Initialization seems ok. Check filter shaders and possibly compare your code to those (awful) examples in case you're missing something. Are you calling PxRigidBodyExt::setMassAndUpdateInertia for dynamic actors after creating all the shapes? That's also pretty important. You shouldn't need that setMass() at all.

I'll post if something comes to mind while coding my own stuff.

oh, i just used PxRigidDynamic::setMass(...);

i replaced it with PxRigidBodyExt::setMassAndUpdateInertia after createShape(), using the same masses(~25 for medium object and 1-5 for small objects). and now, if i set scene scale factor to 1.0f, objects don't fall through stuff, but everything is still in slow-mo. so maybe i've missed some other parameter?

here's how i initialize convex hull actors(beware of temporary\experimental grabage-code):



mDynamicMaterial = mPhysics->createMaterial(0.5f, 0.5f, 0.3f);
mStaticMaterial = mPhysics->createMaterial(1.0f, 1.0f, 0.5f);

...

convexEntities[convexEntityId].objectId = f;
convexEntities[convexEntityId].isStatic = Objects[f].isImmovable;

tempVertices.resize(Objects[f].convexHull.size()/3); //convert to PxVec3, eliminate this part in future
for(unsigned v = 0, p = 0; v < Objects[f].convexHull.size() - 1; v += 3, p++) {
    tempVertices[p] = PxVec3(Objects[f].convexHull[v], Objects[f].convexHull[v + 1], Objects[f].convexHull[v + 2]);
}

convexDesc.points.count     = tempVertices.size();
convexDesc.points.stride    = sizeof(PxVec3);
convexDesc.points.data      = &tempVertices[0];
convexDesc.flags            = PxConvexFlag::eCOMPUTE_CONVEX | PxConvexFlag::eINFLATE_CONVEX;//just in case, so it never exceeds 256. mesh reduction algorithms i found, screw up convex hulls for some reason, so i decided to rely on PhysX.

PxToolkit::MemoryOutputStream buf;
if(!mCooking->cookConvexMesh(convexDesc, buf)) {
    LOG << " #Failed to cook convex hull for " << modelStorage[Objects[f].modelId].modelName << endl;
    break;
}

PxToolkit::MemoryInputData input(buf.getData(), buf.getSize());
PxConvexMesh* convexMesh = mPhysics->createConvexMesh(input);

tempMatrix = glm::translate(glm::mat4(), Objects[f].position.toVec3());
tempMatrix = glm::rotate(tempMatrix, Objects[f].rotation.x, 1.0f, 0.0f, 0.0f);
tempMatrix = glm::rotate(tempMatrix, Objects[f].rotation.y, 0.0f, 1.0f, 0.0f);
tempMatrix = glm::rotate(tempMatrix, Objects[f].rotation.z, 0.0f, 0.0f, 1.0f);

pxTempMatrix = PxMat44(glm::value_ptr(tempMatrix));
if(Objects[f].isImmovable == false) {
    convexEntities[convexEntityId].mDynamicActor = mPhysics->createRigidDynamic(PxTransform(PxVec3(0.0)));
    convexEntities[convexEntityId].mDynamicActor->setGlobalPose(PxTransform(pxTempMatrix));
    convexEntities[convexEntityId].mDynamicActor->setLinearDamping(PxReal(0.05f));
    convexEntities[convexEntityId].mDynamicActor->setAngularDamping(PxReal(0.05f));

    convexEntities[convexEntityId].mDynamicActor->setCMassLocalPose(PxTransform(PxVec3(Objects[f].localCenter.x, Objects[f].localCenter.y, Objects[f].localCenter.z)));

    PxMeshScale scale(PxVec3(Objects[f].scale.x,Objects[f].scale.y,Objects[f].scale.z), PxQuat::createIdentity()); //because it's more convenient in my case
    PxShape* aConvexShape = convexEntities[convexEntityId].mDynamicActor->createShape(PxConvexMeshGeometry(convexMesh, scale), *mDynamicMaterial);
    PxRigidBodyExt::setMassAndUpdateInertia(*convexEntities[convexEntityId].mDynamicActor, Objects[f].weight);

    mScene->addActor(*convexEntities[convexEntityId].mDynamicActor);
} else {
    convexEntities[convexEntityId].mStaticActor = mPhysics->createRigidStatic(PxTransform(PxVec3(0.0)));
    convexEntities[convexEntityId].mStaticActor->setGlobalPose(PxTransform(pxTempMatrix));

    PxMeshScale scale(PxVec3(Objects[f].scale.x,Objects[f].scale.y,Objects[f].scale.z), PxQuat::createIdentity());
    PxShape* aConvexShape = convexEntities[convexEntityId].mStaticActor->createShape(PxConvexMeshGeometry(convexMesh, scale), *mStaticMaterial);

    mScene->addActor(*convexEntities[convexEntityId].mStaticActor);
}

P.S. i use PxDefaultSimulationFilterShader.

If you're sure that slowness isn't just an illusion caused by big objects (after all, even a unit sphere can seem to accelerate pretty slow from distance in empty world) it's most likely a bug in timestepping or anything related. Like I'm not sure if that "mLastTime = clock();" should be there after "return" -> deltaTime grows too big if mAccumulator < mStepSize (mLastTime should be updated in any case). But slo-mo is usually pretty easy to fix if the simulation is otherwise working correctly.

yeah, the proplem is, most likely, there. i copied this snippet straight from documentation:


virtual bool advance(PxReal dt)
{
    mAccumulator  += dt;
    if(mAccumulator < mStepSize)
        return false;

    mAccumulator -= mStepSize;

    mScene->simulate(mStepSize);
    return true;
}

and it doesn't explain, what "dt" is. but it's certainly not what i passed. because it's float and it should be smaller, than 1.0f/stepSize; but if i remove all these extra-timings and leave only simulate() and fetchResults(), it's still slow-mo. this parameter only makes difference, if i divide it by really high values, like tens of thousands, then physx turns into a complete slideshow. if i make it's value bigger, it doesn't matter.

yes, simulation looks kinda correct, but it's hard to judge bounce and friction in such slow motion.

maybe, i shouldn't call fetchResults(true) immedieately after simulate(). it's not very clear.



This topic is closed to new replies.

Advertisement