schteppe / p2.js

JavaScript 2D physics library
Other
2.64k stars 330 forks source link

Non-bouncy objects bounce when time step is reduced #203

Open leaf-node opened 8 years ago

leaf-node commented 8 years ago

I experimented with refining the time step for increased simulation accuracy. When I made the time step really small, I saw an object (that previously had no bouce) bounce very high multiple times over the same period of time.

If I increased the stiffness of the contact material in question when using the small time step, the strange bounciness reduced to no bounciness.

I think that the time step used should not have much of an impact on the bounciness of objects. Rather stiff objects probably shouldn't bounce so high.

var p2, main, worldInit, multiStepWorld;

p2 = require('p2');

main = function () {

    "use strict";

    var time, fps, spf,
        timeStep, worldAndBodies,
        world, body0;

    // steps per frame
    spf = 1;       // not bouncy
    // spf = 1000; // makes the object bouncy unless stiffness is increased

    fps = 60;
    timeStep = 1 / fps / spf;

    worldAndBodies = worldInit();

    world = worldAndBodies.world;
    body0 = worldAndBodies.bodies[0];

    time = 0;
    setInterval(function () {
        time += 1 / fps;
        multiStepWorld(world, spf, timeStep);
        console.log("time: " + time);
        console.log("circle x: " + body0.position[0]);
        console.log("circle y: " + body0.position[1]);
        console.log("circle angle: " + body0.angle);
    }, 1000 / fps);

};

worldInit = function () {

    "use strict";

    var world, circleBody, circleShape,
        groundBody, groundShape,
        genericMaterial;

    world = new p2.World();

    circleBody = new p2.Body({
        mass: 5,
        position: [0, 10]
    });
    circleShape = new p2.Circle({ radius: 1 });
    circleBody.addShape(circleShape);

    groundBody = new p2.Body({ mass: 0 });
    groundShape = new p2.Plane();
    groundBody.addShape(groundShape);

    genericMaterial = new p2.Material();
    world.addContactMaterial(new p2.ContactMaterial(genericMaterial, genericMaterial, {restitution: 0, stiffness: 1e6}));
    // world.addContactMaterial(new p2.ContactMaterial(genericMaterial, genericMaterial, {restitution: 0, stiffness: 1e12}));

    circleShape.material = genericMaterial;
    groundShape.material = genericMaterial;

    world.addBody(groundBody);
    world.addBody(circleBody);

    return {world: world, bodies: [circleBody]};
};

multiStepWorld = function (world, steps, timeStep) {

    "use strict";

    var i;

    for (i = 0; i < steps; i += 1) {

        world.step(timeStep);
    }
};

main();
schteppe commented 8 years ago

Hi, you have to adjust the relaxation time of the contacts to make them stable. The default relaxation time is 4 time steps, which makes the contacts nice at frame rates of about 30-60. When you decrease the time step, you must increase this number, to get the same behavior. If you decrease by a factor 10, you should increase relaxation by a factor 10. Therefore I suggest you to use:

var relaxation = 4 * spf;

The easiest solution is to set the default relaxation globally before creating the World:

p2.Equation.DEFAULT_RELAXATION = relaxation;

If you don't want to edit the global, you can set it manually for all ContactMaterials and Constraints you create.

var constraint = new p2.DistanceConstraint();
constraint.setRelaxation(relaxation);
var contactMaterial = new p2.ContactMaterial(materialA, materialB, {
    relaxation: relaxation,
    frictionRelaxation: relaxation
});

And don't forget to set it for the default contact material, if you use it:

world.defaultContactMaterial.frictionRelaxation = relaxation;
world.defaultContactMaterial.relaxation = relaxation;

This makes me wonder, if the relaxation should be specified in seconds rather than the number of time steps.. this would give the same result no matter what time step used.

Read more about the relaxation parameter here (eqs 9,10,11): http://www8.cs.umu.se/kurser/5DV058/VT15/lectures/spooknotes.pdf

leaf-node commented 8 years ago

Setting this variable works for me. : )

What about automatic internal setting of global relaxation based on the first time step duration? Would that be a backwards compatible change?

schteppe commented 8 years ago

Not a good idea I think. A global constant should never change.

The .relaxation property of equations needs to be taken care of as well. Not sure if it can be made backwards compatible. But probably, in a way or another. The first step is to test if this approach is good for various time step sizes, not only small ones but also big.