Friday 11 July 2014

PhysX - Distance Joint

Introduction to Joint Springs

Just like Fixed Jointswe need a couple rigid actors.
    PxRigidActor* pRA1,* pRA2;

I'll give them box shapes of unit length.
    PxBoxGeometry geo(PxVec3(0.5,0.5,0.5));

    PxTransform pform = PxTransform::createIdentity();

In our example one will be kinematic, to hold up the other.
    pform.p = PxVec3(0,5,0);
    pRA1 = PxCreateDynamic(*gPhysicsSDK, pform,geo,*defaultMaterial, 1.0);
    pRA1->isRigidDynamic()->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC,true);

    pform.p = PxVec3(2,5,2);
    pRA2 = PxCreateDynamic(*gPhysicsSDK, pform,geo,*defaultMaterial, 1.0);
 
Add them to the scene.
    gScene->addActor(*pRA1);
    gScene->addActor(*pRA2);

We will also need a couple transforms to represent the where the joint attaches to the two actors.
    PxTransform local1 = PxTransform::createIdentity(), local2= PxTransform::createIdentity();

Creating the joint is done as before.
    PxDistanceJoint* pJoint = PxDistanceJointCreate(*gPhysicsSDK, pRA1, local1 ,pRA2, local2);

and although not necessary this time we can set the collision flag.
    pJoint->setConstraintFlag(PxConstraintFlag::eCOLLISION_ENABLED,true);

The distance joint may act as a string or a spring, connecting the two actors. There are three flags available for the distance joint.

    PxDistanceJointFlag::
        eSPRING_ENABLED
        eMAX_DISTANCE_ENABLED
        eMIN_DISTANCE_ENABLED

by default only the eMAX_DISTANCE_ENABLED is active. With the spring flag disabled the max distance is a hard limit for the joint. However if the spring flag is enabled then this becomes the distance at which the spring force becomes active.



Similarly a minimum distance can be set, but cannot exceed the maximum distance, the spring force will pull the actors to remain between the min and max distance limits.

Recall the Force equation for a spring:

    F_spring = - k * x

where 'k' is the spring constant, and 'x' is the distance from the natural length, or in our case, the distance from the min or max limits.

The force experienced by the actors will need to be adjusted by a damping force

    F_damping = - c * x'

where 'c' is the damping coefficient, and x' is the first derivative with respect to time of the distance from the natural length (the speed)

    F_net = F_spring + F_damping - F_gravity

A spring experiencing a damping force will eventually stop at an equilibrium length (not necessarily the same as the natural length)

The magnitude of the damping coefficient should be chosen to fall into one of four categories. These categories are decided by the ability of the damping to stop the oscillation.

let z = c/sqrt(4mk);

  • Un-Damped ( z == 0, so c must be 0)
    Choose Un-Damped if you want the spring to oscillate back and forth forever.
  • Under-Damped ( 0 < z < 1)
    Choose under damped if you want some resemblance of a spring effect where it may oscillate back and forth a few times before coming to rest at an equilibrium state. 
  • Critically-Damped ( z ==1 )
    Choose critically damped if you want the spring to slow perfectly to it's equilibrium.
  • Over-Damped ( z > 1 )
    Choose over damped if you want the spring to slow down a lot to start and very gradually approach equilibrium. 



From left to right we have No Spring, Un-damped, Under-damped, Critically-damped, and Over-damped. The distance joint without a spring stops immediately at its maximum distance. For the rest, the spring does not begin applying a tension until they pass that maximum distance.

Notice that although critically-damped, the box overshoots it's equilibrium position anyway. This is because of the extra momentum generated before the spring was active. I will explain this further in a future post.

Setting the values are straight forward,

    pJoint->setSpring(15.0f);
  pJoint->setMaxDistance(3.0f);
    pJoint->setMinDistance(1.0f);
    pJoint->setDamping(0.5);

but are meaningless without the corresponding flag set.

    pJoint->setDistanceJointFlag(PxDistanceJointFlag::eSPRING_ENABLED,true);


Hopefully this gives an preview of how distance joints are created and how the spring feature works.
Happy Coding!


Thursday 3 July 2014

Getting Started : PhysX - Fixed Joints

Introduction to PhysX Joints

PhysX provides a number of options for the types of joints which may be created. I will work through the creation of each pointing out aspects which might be of interest.

The basic formula for creating any joint is the same. We need access to the PxPhysics singleton factory, at least one rigid actor, and the transform from each actor to the point where the joint is formed.

    Px[Type]Joint* Px[Type]JointCreate(
        PxPhysics& physics,
        PxRigidActor* actor0, const PxTransform& localFrame0,
        PxRigidActor* actor1, const PxTransform& localFrame1);

As mentioned, only one of the two actors needs to be defined. It is possible to create a joint by attaching one side to a point in world space (in which case a NULL pointer may be passed for the actor).

Fixed Joint

Arguably the simplest case, and perhaps the most unused, a fixed joint provides no freedom of movement. It's most useful feature is that it can be broken while experiencing an applied force. After creating a fixed joint I explain how to choose appropriate values for the break force and torque.

First we need our shapes and actors, we can start with some boxes of unit size.

    PxBoxGeometry boxGeo(PxVec3(0.5,0.5,0.5)); //Dimensions are for half-length

    PxRigidActor *pRA1, *pRA2;
    PxTransform kform = PxTransform::createIdentity();

    kform.p = PxVec3(0,3,0);
    pRA1 = PxCreateDynamic(*gPhysicsSDK, kform ,boxGeo,*defaultMaterial, 1.0);
    pRA1->isRigidDynamic()->setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC,true);

    kform.p = PxVec3(1,3,0);
    pRA2 = PxCreateDynamic(*gPhysicsSDK, kform , boxGeo,*defaultMaterial, 1.0);

I have set one of the actors to be kinematic so it will remain suspended despite gravity, while the other will be held up entirely by the strength of the joint. Since the boxes they are of unit length, and are one unit apart, the surfaces will be in contact.

It should also be noted that the joint is formed between the two actors and not the shapes themselves. If the shape isn't centered on the actor, or the actor contained multiple shapes then we might want to consider different local frames.

    PxTransform local1 = PxTransform::createIdentity(),
        local2= PxTransform::createIdentity();

    local1.p = PxVec3(0.5,0,0);
    local2.p = PxVec3(-0.5,0,0);

    PxFixedJoint* pJoint = PxFixedJointCreate(*gPhysicsSDK, pRA1, local1 ,pRA2, local2);

I have moved the position of the local frame to be the same distance as the half-length of the box. This way the joint exists at the edge of the box and not within it. Leaving the position at (0,0,0) will center the joint on the middle of the actor causing the two actors to draw together until they overlap. Moving the local frame further from the origin will force the actors to start further apart. I have made it so that the joint end positions fall exactly on the surface of the box.

For a fixed joint there are two important modifiers to consider. First is the break force, which defines the force and the torque at which the joint will break. These are set to a maximum value by default, but there are some cases in which you would want actors to break apart.

    pJoint->setBreakForce(fForce, fTorque);

In this example the weight of the actor is a force of its own which could break the joint. A box of unit size, and of unit density will have a unit mass.

    mass = 1.0f;

and if we set gravity to a convenient number,

    gravity = PxVec3(0.0f, -10.0f, 0.0);

then the shearing force on our arrangement is

    Force = mass*gravity = 1.0 * -10.0 = -10.0

so in order for one box to not break the joint we need a minimum break force of 10.0.

We also need to consider the torque of the actors themselves. In the simple case like this, where the applied force (due to the weight of the object) is already perpendicular to the axis of the joint the torque calculation is simple.

    Torque = length x force = |length|*|force|*sin(90deg) = |length|*|force|

the length of the lever arm is measured from the joint to the actor, which is also the magnitude of the position vector of the local frame.

    length = |localPose2.p| = |(-0.5,0,0)| = 0.5

    Torque = length * force = 0.5 * 10.0 = 5.0

These are example calculations of the minimum force and torque required to suspend a box of unit size and density off the side of a fixed actor. Further estimations should be made to accommodate for collisions with other actors as well.



The other important modifier is the constraint flag. In particular the default setting is that objects constrained by a joint are allowed to pass through each other. The only constraint is the one enforced by the joint itself. In some cases it may be better for actors connected by a joint to be allowed to interact and collide with each other.

    pJoint->setConstraintFlag(PxConstraintFlag::eCOLLISION_ENABLED, true);

Best of luck finding uses for fixed joints.
Happy Coding!