include/PxConstraintDesc.h

File members: include/PxConstraintDesc.h

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2024 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.

#ifndef PX_CONSTRAINT_DESC_H
#define PX_CONSTRAINT_DESC_H

#include "PxPhysXConfig.h"
#include "foundation/PxFlags.h"
#include "foundation/PxVec3.h"
#include "common/PxBase.h"

#if !PX_DOXYGEN
namespace physx { namespace pvdsdk {
#endif
    class PvdDataStream;
#if !PX_DOXYGEN
}}
#endif

#if !PX_DOXYGEN
namespace physx
{
#endif

struct Px1DConstraintFlag
{
    PX_CUDA_CALLABLE Px1DConstraintFlag(){}

    enum Type
    {
        eSPRING                 = 1<<0,
        eACCELERATION_SPRING    = 1<<1,
        eRESTITUTION            = 1<<2,
        eKEEPBIAS               = 1<<3,
        eOUTPUT_FORCE           = 1<<4,
        eHAS_DRIVE_LIMIT        = 1<<5,
        eANGULAR_CONSTRAINT     = 1<<6,
        eDEPRECATED_DRIVE_ROW   = 1<<7
    };
};

typedef PxFlags<Px1DConstraintFlag::Type, PxU16> Px1DConstraintFlags;
PX_FLAGS_OPERATORS(Px1DConstraintFlag::Type, PxU16)

struct PxConstraintSolveHint
{
    enum Enum
    {
        eNONE                   = 0,
        eACCELERATION1          = 256,
        eSLERP_SPRING           = 258,
        eACCELERATION2          = 512,
        eACCELERATION3          = 768,
        eROTATIONAL_EQUALITY    = 1024,
        eROTATIONAL_INEQUALITY  = 1025,

        eEQUALITY               = 2048,

        eINEQUALITY             = 2049
    };
};

/***
\brief A one-dimensional constraint that constrains the relative motion of two rigid bodies.

A constraint is expressed as a set of 1-dimensional constraint rows which define the required constraint
on the objects' velocities.

The constraint Jacobian J is specified by the parameters linear0, angular0, linear1, angular1 as follows

    J = {linear0, angular0, -linear1, -angular1}

The velocity target of the constraint is specified by Px1DConstraint::velocityTarget and the geometric error of the constraint
is specified by Px1DConstraint::geometricError.

The output of the constraint is a velocity state (sdot = ds/dt with s denoting the constraint state) expressed in the world frame:

    sdot = {linearVelocity0, angularVelocity0, linearVelocity1, angularVelocity1}

with linearVelocity0 and angularVelocity0 denoting the linear and angular velocity of body0 of the constraint;
and linearVelocity1 and angularVelocity1 denoting the linear and angular velocity of body1 of the constraint.

The constraint seeks an updated sdot that obeys a simple constraint rule:

    J*sdot + BaumgarteTerm*geometricError/dt - velocityTarget = 0

where BaumgarteTerm is a multiplier in range (0, 1). The Baumgarte term is not exposed but is instead internally
set according to a simple metric chosen to enhance numerical stability. If the PGS solver is employed then dt is
taken from the scene timestep.

Another way of expressing the constraint rule is as follows:

    linear0.dot(linearVelocity0) + angular0.dot(angularVelocity0)
    - linear1.dot(linearVelocity1) - angular1.dot(angularVelocity1)
    + BaumgarteTerm*geometricError/dt - velocityTarget = 0

The PhysX solver runs two phases: position iterations followed by velocity iterations. Position iterations derive
the velocity that is used to integrate the transform of a rigid body. Velocity iterations on the other hand derive
the final velocity of a rigid body. The constraint rule presented above only gets applied during position iterations,
during velocity iterations the geometricError term is usually ignored and the applied constraint rule is:

    J*sdot - velocityTarget = 0

The flag Px1DConstraintFlag::eKEEPBIAS can be used to have velocity iterations apply the same constraint rule as
position iterations.

A 1d constraint may be either a restitution constraint or a hard constraint or a spring constraint.

Restitution constraints have two modes of operation, depending on the speed of the constraint. These two modes are:
a) a bounce mode that employs a restitution value specified by RestitutionModifiers::restitution
b) a non-bounce mode that employs zero restitution and ignores RestitutionModifiers::restitution.
The constraint speed immediately before the solver begins is computed as follows:
    constraintPreSolverSpeed = J * sdotPreSolver
with sdotPreSolver denoting the rigid body velocities recorded after applying external forces and torques to the rigid bodies
but before the solver begins.
If the bounce mode is active, the pre solver velocity is expected to flip direction and have restitution applied:
     bounceSpeed = -restitution * constraintPreSolverSpeed
Restitution will kick in if the following conditions are met:
\li -constraintPreSolverSpeed exceeds the bounce threshold (RestitutionModifiers::velocityThreshold)
\li (bounceSpeed * Px1DConstraint::geometricError) <= 0 (bounceSpeed points in the
opposite direction of the geometric error)
If these hold, then the provided Px1DConstraint::geometricError and Px1DConstraint::velocityTarget parameter will get overriden
internally. The former will get set to zero, the latter will get set to bounceSpeed. If restitution does not activate because
the listed conditions are not met, then the target velocity will be taken from the value stored in velocityTarget and the
geometric error will be taken from the value stored in geometricError.
RestitutionModifiers::restitution may be greater than 1 and may be less than 0 ie it is not limited to 0 <= restitution <= 1.

Hard constraints attempt to find sdot that satisfies the constraint equation:

    J*sdot + BaumgarteTerm*geometricError/dt - velocityTarget = 0

Spring constraints are quite different from restitution and hard constraints in that they attempt to compute and apply a spring force as follows:

    F = stiffness * -geometricError + damping * (velocityTarget - J*sdot)

where F is the constraint force or acceleration and J*sdot is the instantaneous constraint speed. Springs are
implemented with a fully implicit time-stepping scheme: that is, the force or acceleration is a function of the position
and velocity after the solve. Note that F gets applied to the first rigid body and -F to the second rigid body.

All constraints support limits on the minimum or maximum impulse applied.
*/

PX_ALIGN_PREFIX(16)
struct Px1DConstraint
{
    PxVec3          linear0;
    PxReal          geometricError;
    PxVec3          angular0;
    PxReal          velocityTarget;

    PxVec3          linear1;
    PxReal          minImpulse;
    PxVec3          angular1;
    PxReal          maxImpulse;

    union
    {
        struct SpringModifiers
        {
            PxReal  stiffness;
            PxReal  damping;
        } spring;
        struct RestitutionModifiers
        {
            PxReal  restitution;
            PxReal  velocityThreshold;
        } bounce;
    } mods;

    PxU16           flags;
    PxU16           solveHint;

    PxU32           pad;  // for padding only
}
PX_ALIGN_SUFFIX(16);

struct PxConstraintVisualizationFlag
{
    enum Enum
    {
        eLOCAL_FRAMES   = 1,
        eLIMITS         = 2
    };
};

PX_ALIGN_PREFIX(16)
struct PxConstraintInvMassScale
{
    PxReal linear0;
    PxReal angular0;
    PxReal linear1;
    PxReal angular1;

    PX_CUDA_CALLABLE PX_FORCE_INLINE PxConstraintInvMassScale(){}
    PX_CUDA_CALLABLE PX_FORCE_INLINE PxConstraintInvMassScale(PxReal lin0, PxReal ang0, PxReal lin1, PxReal ang1) : linear0(lin0), angular0(ang0), linear1(lin1), angular1(ang1){}
}
PX_ALIGN_SUFFIX(16);

typedef PxU32 (*PxConstraintSolverPrep)(Px1DConstraint* constraints,
                                        PxVec3p& bodyAWorldOffset,
                                        PxU32 maxConstraints,
                                        PxConstraintInvMassScale& invMassScale,
                                        const void* constantBlock,
                                        const PxTransform& bodyAToWorld,
                                        const PxTransform& bodyBToWorld,
                                        bool useExtendedLimits,
                                        PxVec3p& cAtW,
                                        PxVec3p& cBtW);

class PxConstraintVisualizer
{
protected:
    virtual ~PxConstraintVisualizer(){}
public:
    virtual void visualizeJointFrames(const PxTransform& parent, const PxTransform& child) = 0;

    virtual void visualizeLinearLimit(const PxTransform& t0, const PxTransform& t1, PxReal value) = 0;

    virtual void visualizeAngularLimit(const PxTransform& t0, PxReal lower, PxReal upper) = 0;

    virtual void visualizeLimitCone(const PxTransform& t, PxReal tanQSwingY, PxReal tanQSwingZ) = 0;

    virtual void visualizeDoubleCone(const PxTransform& t, PxReal angle) = 0;

    virtual void visualizeLine(const PxVec3& p0, const PxVec3& p1, PxU32 color) = 0;
};

typedef void (*PxConstraintVisualize)(PxConstraintVisualizer& visualizer,
                                      const void* constantBlock,
                                      const PxTransform& body0Transform,
                                      const PxTransform& body1Transform,
                                      PxU32 flags);

struct PxPvdUpdateType
{
    enum Enum
    {
        CREATE_INSTANCE,
        RELEASE_INSTANCE,
        UPDATE_ALL_PROPERTIES,
        UPDATE_SIM_PROPERTIES
    };
};

class PxConstraintConnector
{
public:
    virtual void*   prepareData()   = 0;

    virtual bool    updatePvdProperties(physx::pvdsdk::PvdDataStream& pvdConnection,
                                        const PxConstraint* c,
                                        PxPvdUpdateType::Enum updateType) const     = 0;

    virtual void    updateOmniPvdProperties() const     = 0;

    virtual void    onConstraintRelease()   = 0;

    virtual void    onComShift(PxU32 actor) = 0;

    virtual void    onOriginShift(const PxVec3& shift)  = 0;

    virtual void*   getExternalReference(PxU32& typeID) = 0;

    virtual PxBase* getSerializable()   = 0;

    virtual PxConstraintSolverPrep  getPrep()   const   = 0;

    virtual const void* getConstantBlock()  const   = 0;

    virtual void    connectToConstraint(PxConstraint*)  {}

    virtual ~PxConstraintConnector() {}
};

#if !PX_DOXYGEN
} // namespace physx
#endif

#endif