Program Listing for File Constraints.cpp

Return to documentation for file (Simulation/Constraints.cpp)

#include "Constraints.h"
#include "SimulationModel.h"
#include "PositionBasedDynamics/PositionBasedDynamics.h"
#include "PositionBasedDynamics/XPBD.h"
#include "PositionBasedDynamics/PositionBasedRigidBodyDynamics.h"
#include "TimeManager.h"
#include "Simulation/IDFactory.h"
#include "PositionBasedDynamics/PositionBasedElasticRods.h"


#include <set>
#include <map>

using namespace PBD;


int BallJoint::TYPE_ID = IDFactory::getId();
int BallOnLineJoint::TYPE_ID = IDFactory::getId();
int HingeJoint::TYPE_ID = IDFactory::getId();
int UniversalJoint::TYPE_ID = IDFactory::getId();
int RigidBodyParticleBallJoint::TYPE_ID = IDFactory::getId();
int RigidBodySpring::TYPE_ID = IDFactory::getId();
int DistanceJoint::TYPE_ID = IDFactory::getId();
int DistanceConstraint::TYPE_ID = IDFactory::getId();
int DistanceConstraint_XPBD::TYPE_ID = IDFactory::getId();
int DihedralConstraint::TYPE_ID = IDFactory::getId();
int IsometricBendingConstraint::TYPE_ID = IDFactory::getId();
int IsometricBendingConstraint_XPBD::TYPE_ID = IDFactory::getId();
int FEMTriangleConstraint::TYPE_ID = IDFactory::getId();
int StrainTriangleConstraint::TYPE_ID = IDFactory::getId();
int VolumeConstraint::TYPE_ID = IDFactory::getId();
int VolumeConstraint_XPBD::TYPE_ID = IDFactory::getId();
int FEMTetConstraint::TYPE_ID = IDFactory::getId();
int XPBD_FEMTetConstraint::TYPE_ID = IDFactory::getId();
int StrainTetConstraint::TYPE_ID = IDFactory::getId();
int ShapeMatchingConstraint::TYPE_ID = IDFactory::getId();
int TargetAngleMotorHingeJoint::TYPE_ID = IDFactory::getId();
int TargetVelocityMotorHingeJoint::TYPE_ID = IDFactory::getId();
int SliderJoint::TYPE_ID = IDFactory::getId();
int TargetPositionMotorSliderJoint::TYPE_ID = IDFactory::getId();
int TargetVelocityMotorSliderJoint::TYPE_ID = IDFactory::getId();
int DamperJoint::TYPE_ID = IDFactory::getId();
int RigidBodyContactConstraint::TYPE_ID = IDFactory::getId();
int ParticleRigidBodyContactConstraint::TYPE_ID = IDFactory::getId();
int ParticleTetContactConstraint::TYPE_ID = IDFactory::getId();
int StretchShearConstraint::TYPE_ID = IDFactory::getId();
int BendTwistConstraint::TYPE_ID = IDFactory::getId();
int StretchBendingTwistingConstraint::TYPE_ID = IDFactory::getId();
int DirectPositionBasedSolverForStiffRodsConstraint::TYPE_ID = IDFactory::getId();

// BallJoint
bool BallJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_BallJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos,
        m_jointInfo);
}

bool BallJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_BallJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool BallJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_BallJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}


// BallOnLineJoint
bool BallOnLineJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Vector3r &dir)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_BallOnLineJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos, dir,
        m_jointInfo);
}

bool BallOnLineJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_BallOnLineJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool BallOnLineJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_BallOnLineJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}


// HingeJoint
bool HingeJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Vector3r &axis)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_HingeJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos, axis,
        m_jointInfo);
}

bool HingeJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_HingeJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool HingeJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_HingeJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}


// UniversalJoint
bool UniversalJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Vector3r &axis1, const Vector3r &axis2)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_UniversalJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos,
        axis1,
        axis2,
        m_jointInfo);
}

bool UniversalJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_UniversalJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool UniversalJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_UniversalJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}


// SliderJoint
bool SliderJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &axis)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_SliderJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        axis,
        m_jointInfo);
}

bool SliderJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_SliderJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool SliderJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_SliderJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}


// TargetPositionMotorSliderJoint
bool TargetPositionMotorSliderJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &axis)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_TargetPositionMotorSliderJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        axis,
        m_jointInfo);
}

bool TargetPositionMotorSliderJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_TargetPositionMotorSliderJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool TargetPositionMotorSliderJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_TargetPositionMotorSliderJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_target,
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}



// TargetVelocityMotorSliderJoint
bool TargetVelocityMotorSliderJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &axis)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_TargetVelocityMotorSliderJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        axis,
        m_jointInfo);
}

bool TargetVelocityMotorSliderJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_TargetVelocityMotorSliderJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool TargetVelocityMotorSliderJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_TargetVelocityMotorSliderJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}


bool TargetVelocityMotorSliderJoint::solveVelocityConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_v1, corr_v2;
    Vector3r corr_omega1, corr_omega2;
    const bool res = PositionBasedRigidBodyDynamics::velocitySolve_TargetVelocityMotorSliderJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getVelocity(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb1.getAngularVelocity(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getVelocity(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        rb2.getAngularVelocity(),
        m_target,
        m_jointInfo,
        corr_v1,
        corr_omega1,
        corr_v2,
        corr_omega2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getVelocity() += corr_v1;
            rb1.getAngularVelocity() += corr_omega1;
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getVelocity() += corr_v2;
            rb2.getAngularVelocity() += corr_omega2;
        }
    }
    return res;
}

// TargetAngleMotorHingeJoint
bool TargetAngleMotorHingeJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Vector3r &axis)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_TargetAngleMotorHingeJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos, axis,
        m_jointInfo);
}

bool TargetAngleMotorHingeJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_TargetAngleMotorHingeJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool TargetAngleMotorHingeJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_TargetAngleMotorHingeJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_target,
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}

// TargetVelocityMotorHingeJoint
bool TargetVelocityMotorHingeJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos, const Vector3r &axis)
{
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_TargetVelocityMotorHingeJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos, axis,
        m_jointInfo);
}

bool TargetVelocityMotorHingeJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_TargetVelocityMotorHingeJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool TargetVelocityMotorHingeJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_TargetVelocityMotorHingeJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}

bool TargetVelocityMotorHingeJoint::solveVelocityConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_v1, corr_v2;
    Vector3r corr_omega1, corr_omega2;
    const bool res = PositionBasedRigidBodyDynamics::velocitySolve_TargetVelocityMotorHingeJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getVelocity(),
        rb1.getInertiaTensorInverseW(),
        rb1.getAngularVelocity(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getVelocity(),
        rb2.getInertiaTensorInverseW(),
        rb2.getAngularVelocity(),
        m_target,
        m_jointInfo,
        corr_v1,
        corr_omega1,
        corr_v2,
        corr_omega2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getVelocity() += corr_v1;
            rb1.getAngularVelocity() += corr_omega1;
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getVelocity() += corr_v2;
            rb2.getAngularVelocity() += corr_omega2;
        }
    }
    return res;
}


// DamperJoint
bool DamperJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &axis, const Real stiffness)
{
    m_stiffness = stiffness;
    m_lambda = 0.0;
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_DamperJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        axis,
        m_jointInfo);
}

bool DamperJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_DamperJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool DamperJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    const Real dt = TimeManager::getCurrent()->getTimeStepSize();

    if (iter == 0)
        m_lambda = 0.0;

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_DamperJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_stiffness,
        dt,
        m_jointInfo,
        m_lambda,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}

// RigidBodyParticleBallJoint
bool RigidBodyParticleBallJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex, const unsigned int particleIndex)
{
    m_bodies[0] = rbIndex;
    m_bodies[1] = particleIndex;
    SimulationModel::RigidBodyVector &rbs = model.getRigidBodies();
    ParticleData &pd = model.getParticles();
    RigidBody &rb = *rbs[m_bodies[0]];
    return PositionBasedRigidBodyDynamics::init_RigidBodyParticleBallJoint(
        rb.getPosition(),
        rb.getRotation(),
        pd.getPosition(particleIndex),
        m_jointInfo);
}

bool RigidBodyParticleBallJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    ParticleData &pd = model.getParticles();
    RigidBody &rb1 = *rb[m_bodies[0]];
    return PositionBasedRigidBodyDynamics::update_RigidBodyParticleBallJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        pd.getPosition(m_bodies[1]),
        m_jointInfo);
}

bool RigidBodyParticleBallJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    ParticleData &pd = model.getParticles();

    RigidBody &rb1 = *rb[m_bodies[0]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1;
    const bool res = PositionBasedRigidBodyDynamics::solve_RigidBodyParticleBallJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        pd.getInvMass(m_bodies[1]),
        pd.getPosition(m_bodies[1]),
        m_jointInfo,
        corr_x1,
        corr_q1,
        corr_x2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (pd.getMass(m_bodies[1]) != 0.0)
        {
            pd.getPosition(m_bodies[1]) += corr_x2;
        }
    }
    return res;
}

// RigidBodySpring
bool RigidBodySpring::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos1, const Vector3r &pos2, const Real stiffness)
{
    m_stiffness = stiffness;
    m_lambda = 0.0;
    m_restLength = (pos1 - pos2).norm();
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_DistanceJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos1,
        pos2,
        m_jointInfo);
}

bool RigidBodySpring::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_DistanceJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool RigidBodySpring::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    const Real dt = TimeManager::getCurrent()->getTimeStepSize();

    if (iter == 0)
        m_lambda = 0.0;

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_DistanceJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        m_stiffness,
        m_restLength,
        dt,
        m_jointInfo,
        m_lambda,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}


// DistanceJoint
bool DistanceJoint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2, const Vector3r &pos1, const Vector3r &pos2)
{
    m_restLength = (pos1 - pos2).norm();
    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::init_DistanceJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        pos1,
        pos2,
        m_jointInfo);
}

bool DistanceJoint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];
    return PositionBasedRigidBodyDynamics::update_DistanceJoint(
        rb1.getPosition(),
        rb1.getRotation(),
        rb2.getPosition(),
        rb2.getRotation(),
        m_jointInfo);
}

bool DistanceJoint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Real lambda = 0.0;

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = PositionBasedRigidBodyDynamics::solve_DistanceJoint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        0.0,
        m_restLength,
        0.0,
        m_jointInfo,
        lambda,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += corr_x1;
            rb1.getRotation().coeffs() += corr_q1.coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getPosition() += corr_x2;
            rb2.getRotation().coeffs() += corr_q2.coeffs();
            rb2.getRotation().normalize();
            rb2.rotationUpdated();
        }
    }
    return res;
}

// DistanceConstraint
bool DistanceConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2, const Real stiffness)
{
    m_stiffness = stiffness;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    ParticleData &pd = model.getParticles();

    const Vector3r &x1_0 = pd.getPosition0(particle1);
    const Vector3r &x2_0 = pd.getPosition0(particle2);

    m_restLength = (x2_0 - x1_0).norm();

    return true;
}

bool DistanceConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);

    Vector3r corr1, corr2;
    const bool res = PositionBasedDynamics::solve_DistanceConstraint(
        x1, invMass1, x2, invMass2,
        m_restLength, m_stiffness, corr1, corr2);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
    }
    return res;
}

// DistanceConstraint_XPBD
bool DistanceConstraint_XPBD::initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2, const Real stiffness)
{
    m_stiffness = stiffness;
    m_lambda = 0.0;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    ParticleData& pd = model.getParticles();

    const Vector3r& x1_0 = pd.getPosition0(particle1);
    const Vector3r& x2_0 = pd.getPosition0(particle2);

    m_restLength = (x2_0 - x1_0).norm();

    return true;
}

bool DistanceConstraint_XPBD::solvePositionConstraint(SimulationModel& model, const unsigned int iter)
{
    ParticleData& pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];

    Vector3r& x1 = pd.getPosition(i1);
    Vector3r& x2 = pd.getPosition(i2);
    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);

    const Real dt = TimeManager::getCurrent()->getTimeStepSize();

    if (iter == 0)
        m_lambda = 0.0;

    Vector3r corr1, corr2;
    const bool res = XPBD::solve_DistanceConstraint(
        x1, invMass1, x2, invMass2,
        m_restLength, m_stiffness, dt, m_lambda,
        corr1, corr2);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
    }
    return res;
}

// DihedralConstraint

bool DihedralConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
                                        const unsigned int particle3, const unsigned int particle4, const Real stiffness)
{
    m_stiffness = stiffness;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;
    ParticleData &pd = model.getParticles();

    const Vector3r &p0 = pd.getPosition0(particle1);
    const Vector3r &p1 = pd.getPosition0(particle2);
    const Vector3r &p2 = pd.getPosition0(particle3);
    const Vector3r &p3 = pd.getPosition0(particle4);

    Vector3r e = p3 - p2;
    Real  elen = e.norm();
    if (elen < 1e-6)
        return false;

    Real invElen = static_cast<Real>(1.0) / elen;

    Vector3r n1 = (p2 - p0).cross(p3 - p0); n1 /= n1.squaredNorm();
    Vector3r n2 = (p3 - p1).cross(p2 - p1); n2 /= n2.squaredNorm();

    n1.normalize();
    n2.normalize();
    Real dot = n1.dot(n2);

    if (dot < -1.0) dot = -1.0;
    if (dot > 1.0) dot = 1.0;

    m_restAngle = acos(dot);

    return true;
}

bool DihedralConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);
    Vector3r &x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    Vector3r corr1, corr2, corr3, corr4;
    const bool res = PositionBasedDynamics::solve_DihedralConstraint(
        x1, invMass1, x2, invMass2, x3, invMass3, x4, invMass4,
        m_restAngle,
        m_stiffness,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}


// IsometricBendingConstraint
bool IsometricBendingConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
                                                const unsigned int particle3, const unsigned int particle4, const Real stiffness)
{
    m_stiffness = stiffness;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;

    ParticleData &pd = model.getParticles();

    const Vector3r &x1 = pd.getPosition0(particle1);
    const Vector3r &x2 = pd.getPosition0(particle2);
    const Vector3r &x3 = pd.getPosition0(particle3);
    const Vector3r &x4 = pd.getPosition0(particle4);

    return PositionBasedDynamics::init_IsometricBendingConstraint(x1, x2, x3, x4, m_Q);
}

bool IsometricBendingConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);
    Vector3r &x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    Vector3r corr1, corr2, corr3, corr4;
    const bool res = PositionBasedDynamics::solve_IsometricBendingConstraint(
        x1, invMass1, x2, invMass2, x3, invMass3, x4, invMass4,
        m_Q,
        m_stiffness,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}

// IsometricBendingConstraint_XPBD
bool IsometricBendingConstraint_XPBD::initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2,
        const unsigned int particle3, const unsigned int particle4, const Real stiffness)
{
    m_lambda = 0.0;
    m_stiffness = stiffness;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;

    ParticleData& pd = model.getParticles();

    const Vector3r& x1 = pd.getPosition0(particle1);
    const Vector3r& x2 = pd.getPosition0(particle2);
    const Vector3r& x3 = pd.getPosition0(particle3);
    const Vector3r& x4 = pd.getPosition0(particle4);

    return PositionBasedDynamics::init_IsometricBendingConstraint(x1, x2, x3, x4, m_Q);
}

bool IsometricBendingConstraint_XPBD::solvePositionConstraint(SimulationModel& model, const unsigned int iter)
{
    ParticleData& pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r& x1 = pd.getPosition(i1);
    Vector3r& x2 = pd.getPosition(i2);
    Vector3r& x3 = pd.getPosition(i3);
    Vector3r& x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    const Real dt = TimeManager::getCurrent()->getTimeStepSize();

    if (iter == 0)
        m_lambda = 0.0;

    Vector3r corr1, corr2, corr3, corr4;
    const bool res = XPBD::solve_IsometricBendingConstraint(
        x1, invMass1, x2, invMass2, x3, invMass3, x4, invMass4,
        m_Q,
        m_stiffness,
        dt, m_lambda,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}

// FEMTriangleConstraint
bool FEMTriangleConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
    const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness,
    const Real xyPoissonRatio, const Real yxPoissonRatio)
{
    m_xxStiffness = xxStiffness;
    m_yyStiffness = yyStiffness;
    m_xyStiffness = xyStiffness;
    m_xyPoissonRatio = xyPoissonRatio;
    m_yxPoissonRatio = yxPoissonRatio;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;

    ParticleData &pd = model.getParticles();

    Vector3r &x1 = pd.getPosition0(particle1);
    Vector3r &x2 = pd.getPosition0(particle2);
    Vector3r &x3 = pd.getPosition0(particle3);

    return PositionBasedDynamics::init_FEMTriangleConstraint(x1, x2, x3, m_area, m_invRestMat);
}

bool FEMTriangleConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);

    Vector3r corr1, corr2, corr3;
    const bool res = PositionBasedDynamics::solve_FEMTriangleConstraint(
        x1, invMass1,
        x2, invMass2,
        x3, invMass3,
        m_area,
        m_invRestMat,
        m_xxStiffness,
        m_yyStiffness,
        m_xyStiffness,
        m_xyPoissonRatio,
        m_yxPoissonRatio,
        corr1, corr2, corr3);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
    }
    return res;
}


// StrainTriangleConstraint
bool StrainTriangleConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
    const unsigned int particle3, const Real xxStiffness, const Real yyStiffness, const Real xyStiffness,
    const bool normalizeStretch, const bool normalizeShear)
{
    m_xxStiffness = xxStiffness;
    m_yyStiffness = yyStiffness;
    m_xyStiffness = xyStiffness;
    m_normalizeStretch = normalizeStretch;
    m_normalizeShear = normalizeShear;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;

    ParticleData &pd = model.getParticles();

    Vector3r &x1 = pd.getPosition0(particle1);
    Vector3r &x2 = pd.getPosition0(particle2);
    Vector3r &x3 = pd.getPosition0(particle3);

    // Bring triangles to xy plane
    const Vector3r y1(x1[0], x1[2], 0.0);
    const Vector3r y2(x2[0], x2[2], 0.0);
    const Vector3r y3(x3[0], x3[2], 0.0);

    return PositionBasedDynamics::init_StrainTriangleConstraint(y1, y2, y3, m_invRestMat);
}

bool StrainTriangleConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);

    Vector3r corr1, corr2, corr3;
    const bool res = PositionBasedDynamics::solve_StrainTriangleConstraint(
        x1, invMass1,
        x2, invMass2,
        x3, invMass3,
        m_invRestMat,
        m_xxStiffness,
        m_yyStiffness,
        m_xyStiffness,
        m_normalizeStretch,
        m_normalizeShear,
        corr1, corr2, corr3);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
    }
    return res;
}


// VolumeConstraint

bool VolumeConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
    const unsigned int particle3, const unsigned int particle4, const Real stiffness)
{
    m_stiffness = stiffness;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;
    ParticleData &pd = model.getParticles();

    const Vector3r &p0 = pd.getPosition0(particle1);
    const Vector3r &p1 = pd.getPosition0(particle2);
    const Vector3r &p2 = pd.getPosition0(particle3);
    const Vector3r &p3 = pd.getPosition0(particle4);

    m_restVolume = fabs(static_cast<Real>(1.0 / 6.0) * (p3 - p0).dot((p2 - p0).cross(p1 - p0)));

    return true;
}

bool VolumeConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);
    Vector3r &x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    Vector3r corr1, corr2, corr3, corr4;
    const bool res = PositionBasedDynamics::solve_VolumeConstraint(x1, invMass1,
        x2, invMass2,
        x3, invMass3,
        x4, invMass4,
        m_restVolume,
        m_stiffness,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}

// VolumeConstraint_XPBD

bool VolumeConstraint_XPBD::initConstraint(SimulationModel& model, const unsigned int particle1, const unsigned int particle2,
    const unsigned int particle3, const unsigned int particle4, const Real stiffness)
{
    m_stiffness = stiffness;
    m_lambda = 0.0;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;
    ParticleData& pd = model.getParticles();

    const Vector3r& p0 = pd.getPosition0(particle1);
    const Vector3r& p1 = pd.getPosition0(particle2);
    const Vector3r& p2 = pd.getPosition0(particle3);
    const Vector3r& p3 = pd.getPosition0(particle4);

    m_restVolume = fabs(static_cast<Real>(1.0 / 6.0) * (p3 - p0).dot((p2 - p0).cross(p1 - p0)));

    return true;
}

bool VolumeConstraint_XPBD::solvePositionConstraint(SimulationModel& model, const unsigned int iter)
{
    ParticleData& pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r& x1 = pd.getPosition(i1);
    Vector3r& x2 = pd.getPosition(i2);
    Vector3r& x3 = pd.getPosition(i3);
    Vector3r& x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    const Real dt = TimeManager::getCurrent()->getTimeStepSize();

    if (iter == 0)
        m_lambda = 0.0;

    Vector3r corr1, corr2, corr3, corr4;
    const bool res = XPBD::solve_VolumeConstraint(x1, invMass1,
        x2, invMass2,
        x3, invMass3,
        x4, invMass4,
        m_restVolume,
        m_stiffness,
        dt, m_lambda,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}

// FEMTetConstraint
bool FEMTetConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
                                    const unsigned int particle3, const unsigned int particle4,
                                    const Real stiffness, const Real poissonRatio)
{
    m_stiffness = stiffness;
    m_poissonRatio = poissonRatio;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;

    ParticleData &pd = model.getParticles();

    Vector3r &x1 = pd.getPosition0(particle1);
    Vector3r &x2 = pd.getPosition0(particle2);
    Vector3r &x3 = pd.getPosition0(particle3);
    Vector3r &x4 = pd.getPosition0(particle4);

    return PositionBasedDynamics::init_FEMTetraConstraint(x1, x2, x3, x4, m_volume, m_invRestMat);
}

bool FEMTetConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);
    Vector3r &x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    Real currentVolume = -static_cast<Real>(1.0 / 6.0) * (x4 - x1).dot((x3 - x1).cross(x2 - x1));
    bool handleInversion = false;
    if (currentVolume / m_volume < 0.2)     // Only 20% of initial volume left
        handleInversion = true;


    Vector3r corr1, corr2, corr3, corr4;
    const bool res = PositionBasedDynamics::solve_FEMTetraConstraint(
        x1, invMass1,
        x2, invMass2,
        x3, invMass3,
        x4, invMass4,
        m_volume,
        m_invRestMat,
        m_stiffness,
        m_poissonRatio, handleInversion,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}

// XPBD_FEMTetConstraint
bool XPBD_FEMTetConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
                                    const unsigned int particle3, const unsigned int particle4,
                                    const Real stiffness, const Real poissonRatio)
{
    m_stiffness = stiffness;
    m_poissonRatio = poissonRatio;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;

    ParticleData &pd = model.getParticles();

    Vector3r &x1 = pd.getPosition0(particle1);
    Vector3r &x2 = pd.getPosition0(particle2);
    Vector3r &x3 = pd.getPosition0(particle3);
    Vector3r &x4 = pd.getPosition0(particle4);

    return PositionBasedDynamics::init_FEMTetraConstraint(x1, x2, x3, x4, m_volume, m_invRestMat);
}

bool XPBD_FEMTetConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);
    Vector3r &x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    Real currentVolume = -static_cast<Real>(1.0 / 6.0) * (x4 - x1).dot((x3 - x1).cross(x2 - x1));
    bool handleInversion = false;
    if (currentVolume / m_volume < 0.2)     // Only 20% of initial volume left
        handleInversion = true;

    const Real dt = TimeManager::getCurrent()->getTimeStepSize();

    if (iter == 0)
        m_lambda = 0.0;

    Vector3r corr1, corr2, corr3, corr4;
    const bool res = XPBD::solve_FEMTetraConstraint(
        x1, invMass1,
        x2, invMass2,
        x3, invMass3,
        x4, invMass4,
        m_volume,
        m_invRestMat,
        m_stiffness,
        m_poissonRatio, handleInversion,
        dt,
        m_lambda,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}


// StrainTetConstraint
bool StrainTetConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
    const unsigned int particle3, const unsigned int particle4,
    const Real stretchStiffness, const Real shearStiffness,
    const bool normalizeStretch, const bool normalizeShear)
{
    m_stretchStiffness = stretchStiffness;
    m_shearStiffness = shearStiffness;
    m_normalizeStretch = normalizeStretch;
    m_normalizeShear = normalizeShear;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = particle3;
    m_bodies[3] = particle4;

    ParticleData &pd = model.getParticles();

    Vector3r &x1 = pd.getPosition0(particle1);
    Vector3r &x2 = pd.getPosition0(particle2);
    Vector3r &x3 = pd.getPosition0(particle3);
    Vector3r &x4 = pd.getPosition0(particle4);

    return PositionBasedDynamics::init_StrainTetraConstraint(x1, x2, x3, x4, m_invRestMat);
}

bool StrainTetConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned i3 = m_bodies[2];
    const unsigned i4 = m_bodies[3];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Vector3r &x3 = pd.getPosition(i3);
    Vector3r &x4 = pd.getPosition(i4);

    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMass3 = pd.getInvMass(i3);
    const Real invMass4 = pd.getInvMass(i4);

    Vector3r corr1, corr2, corr3, corr4;
    const bool res = PositionBasedDynamics::solve_StrainTetraConstraint(
        x1, invMass1,
        x2, invMass2,
        x3, invMass3,
        x4, invMass4,
        m_invRestMat,
        m_stretchStiffness * Vector3r::Ones(),
        m_shearStiffness * Vector3r::Ones(),
        m_normalizeStretch,
        m_normalizeShear,
        corr1, corr2, corr3, corr4);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMass3 != 0.0)
            x3 += corr3;
        if (invMass4 != 0.0)
            x4 += corr4;
    }
    return res;
}

// ShapeMatchingConstraint
bool ShapeMatchingConstraint::initConstraint(SimulationModel &model,
            const unsigned int particleIndices[], const unsigned int numClusters[],
            const Real stiffness)
{
    m_stiffness = stiffness;
    ParticleData &pd = model.getParticles();
    for (unsigned int i = 0; i < numberOfBodies(); i++)
    {
        m_bodies[i] = particleIndices[i];
        m_x0[i] = pd.getPosition0(m_bodies[i]);
        m_w[i] = pd.getInvMass(m_bodies[i]);
        m_numClusters[i] = numClusters[i];
    }

    const bool res = PositionBasedDynamics::init_ShapeMatchingConstraint(m_x0, m_w, numberOfBodies(), m_restCm);
    return res;
}

bool ShapeMatchingConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();
    for (unsigned int i = 0; i < numberOfBodies(); i++)
    {
        m_x[i] = pd.getPosition(m_bodies[i]);
    }

    const bool res = PositionBasedDynamics::solve_ShapeMatchingConstraint(
        m_x0, m_x, m_w, numberOfBodies(),
        m_restCm,
        m_stiffness, false,
        m_corr);

    if (res)
    {
        for (unsigned int i = 0; i < numberOfBodies(); i++)
        {
            // Important: Divide position correction by the number of clusters
            // which contain the vertex.
            if (m_w[i] != 0.0)
                pd.getPosition(m_bodies[i]) += (1.0 / m_numClusters[i]) * m_corr[i];
        }
    }
    return res;
}


// RigidBodyContactConstraint
bool RigidBodyContactConstraint::initConstraint(SimulationModel &model, const unsigned int rbIndex1, const unsigned int rbIndex2,
        const Vector3r &cp1, const Vector3r &cp2,
        const Vector3r &normal, const Real dist,
        const Real restitutionCoeff, const Real stiffness, const Real frictionCoeff)
{
    m_stiffness = stiffness;
    m_frictionCoeff = frictionCoeff;

    m_bodies[0] = rbIndex1;
    m_bodies[1] = rbIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    m_sum_impulses = 0.0;

    return PositionBasedRigidBodyDynamics::init_RigidBodyContactConstraint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getVelocity(),
        rb1.getInertiaTensorInverseW(),
        rb1.getRotation(),
        rb1.getAngularVelocity(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getVelocity(),
        rb2.getInertiaTensorInverseW(),
        rb2.getRotation(),
        rb2.getAngularVelocity(),
        cp1, cp2, normal, restitutionCoeff,
        m_constraintInfo);
}

bool RigidBodyContactConstraint::solveVelocityConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &rb1 = *rb[m_bodies[0]];
    RigidBody &rb2 = *rb[m_bodies[1]];

    Vector3r corr_v1, corr_v2;
    Vector3r corr_omega1, corr_omega2;
    const bool res = PositionBasedRigidBodyDynamics::velocitySolve_RigidBodyContactConstraint(
        rb1.getInvMass(),
        rb1.getPosition(),
        rb1.getVelocity(),
        rb1.getInertiaTensorInverseW(),
        rb1.getAngularVelocity(),
        rb2.getInvMass(),
        rb2.getPosition(),
        rb2.getVelocity(),
        rb2.getInertiaTensorInverseW(),
        rb2.getAngularVelocity(),
        m_stiffness,
        m_frictionCoeff,
        m_sum_impulses,
        m_constraintInfo,
        corr_v1,
        corr_omega1,
        corr_v2,
        corr_omega2);

    if (res)
    {
        if (rb1.getMass() != 0.0)
        {
            rb1.getVelocity() += corr_v1;
            rb1.getAngularVelocity() += corr_omega1;
        }
        if (rb2.getMass() != 0.0)
        {
            rb2.getVelocity() += corr_v2;
            rb2.getAngularVelocity() += corr_omega2;
        }
    }
    return res;
}

// ParticleRigidBodyContactConstraint
bool ParticleRigidBodyContactConstraint::initConstraint(SimulationModel &model,
    const unsigned int particleIndex, const unsigned int rbIndex,
    const Vector3r &cp1, const Vector3r &cp2,
    const Vector3r &normal, const Real dist,
    const Real restitutionCoeff, const Real stiffness, const Real frictionCoeff)
{
    m_stiffness = stiffness;
    m_frictionCoeff = frictionCoeff;

    m_bodies[0] = particleIndex;
    m_bodies[1] = rbIndex;
    SimulationModel::RigidBodyVector &rbs = model.getRigidBodies();
    ParticleData &pd = model.getParticles();

    RigidBody &rb = *rbs[m_bodies[1]];

    m_sum_impulses = 0.0;

    return PositionBasedRigidBodyDynamics::init_ParticleRigidBodyContactConstraint(
        pd.getInvMass(particleIndex),
        pd.getPosition(particleIndex),
        pd.getVelocity(particleIndex),
        rb.getInvMass(),
        rb.getPosition(),
        rb.getVelocity(),
        rb.getInertiaTensorInverseW(),
        rb.getRotation(),
        rb.getAngularVelocity(),
        cp1, cp2, normal, restitutionCoeff,
        m_constraintInfo);
}

bool ParticleRigidBodyContactConstraint::solveVelocityConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rbs = model.getRigidBodies();
    ParticleData &pd = model.getParticles();

    RigidBody &rb = *rbs[m_bodies[1]];

    Vector3r corr_v1, corr_v2;
    Vector3r corr_omega2;
    const bool res = PositionBasedRigidBodyDynamics::velocitySolve_ParticleRigidBodyContactConstraint(
        pd.getInvMass(m_bodies[0]),
        pd.getPosition(m_bodies[0]),
        pd.getVelocity(m_bodies[0]),
        rb.getInvMass(),
        rb.getPosition(),
        rb.getVelocity(),
        rb.getInertiaTensorInverseW(),
        rb.getAngularVelocity(),
        m_stiffness,
        m_frictionCoeff,
        m_sum_impulses,
        m_constraintInfo,
        corr_v1,
        corr_v2,
        corr_omega2);

    if (res)
    {
        if (pd.getMass(m_bodies[0]) != 0.0)
        {
            pd.getVelocity(m_bodies[0]) += corr_v1;
        }
        if (rb.getMass() != 0.0)
        {
            rb.getVelocity() += corr_v2;
            rb.getAngularVelocity() += corr_omega2;
        }
    }
    return res;
}

// ParticleSolidContactConstraint
bool ParticleTetContactConstraint::initConstraint(SimulationModel &model,
    const unsigned int particleIndex, const unsigned int solidIndex,
    const unsigned int tetIndex, const Vector3r &bary,
    const Vector3r &cp1, const Vector3r &cp2,
    const Vector3r &normal, const Real dist,
    const Real frictionCoeff)
{
    m_frictionCoeff = frictionCoeff;

    m_bodies[0] = particleIndex;
    m_bodies[1] = solidIndex;
    m_tetIndex = tetIndex;
    m_solidIndex = solidIndex;
    m_bary = bary;
    ParticleData &pd = model.getParticles();

    const SimulationModel::TetModelVector &tetModels = model.getTetModels();
    TetModel *tm = tetModels[solidIndex];
    const unsigned int offset = tm->getIndexOffset();
    const unsigned int *indices = tm->getParticleMesh().getTets().data();
    m_x[0] = pd.getPosition(indices[4 * tetIndex] + offset);
    m_x[1] = pd.getPosition(indices[4 * tetIndex + 1] + offset);
    m_x[2] = pd.getPosition(indices[4 * tetIndex + 2] + offset);
    m_x[3] = pd.getPosition(indices[4 * tetIndex + 3] + offset);
    m_v[0] = pd.getVelocity(indices[4 * tetIndex] + offset);
    m_v[1] = pd.getVelocity(indices[4 * tetIndex + 1] + offset);
    m_v[2] = pd.getVelocity(indices[4 * tetIndex + 2] + offset);
    m_v[3] = pd.getVelocity(indices[4 * tetIndex + 3] + offset);
    m_invMasses[0] = pd.getInvMass(indices[4 * tetIndex] + offset);
    m_invMasses[1] = pd.getInvMass(indices[4 * tetIndex + 1] + offset);
    m_invMasses[2] = pd.getInvMass(indices[4 * tetIndex + 2] + offset);
    m_invMasses[3] = pd.getInvMass(indices[4 * tetIndex + 3] + offset);

    return PositionBasedDynamics::init_ParticleTetContactConstraint(
        pd.getInvMass(particleIndex),
        pd.getPosition(particleIndex),
        pd.getVelocity(particleIndex),
        m_invMasses,
        m_x.data(),
        m_v.data(),
        bary, normal,
        m_constraintInfo);
}

bool ParticleTetContactConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const SimulationModel::TetModelVector &tetModels = model.getTetModels();
    TetModel *tm = tetModels[m_solidIndex];
    const unsigned int offset = tm->getIndexOffset();
    const unsigned int *indices = tm->getParticleMesh().getTets().data();
    Vector3r &x0 = pd.getPosition(indices[4 * m_tetIndex] + offset);
    Vector3r &x1 = pd.getPosition(indices[4 * m_tetIndex + 1] + offset);
    Vector3r &x2 = pd.getPosition(indices[4 * m_tetIndex + 2] + offset);
    Vector3r &x3 = pd.getPosition(indices[4 * m_tetIndex + 3] + offset);

    Vector3r corr0;
    Vector3r corr[4];
    const bool res = PositionBasedDynamics::solve_ParticleTetContactConstraint(
        pd.getInvMass(m_bodies[0]),
        pd.getPosition(m_bodies[0]),
        m_invMasses,
        m_x.data(),
        m_bary,
        m_constraintInfo,
        m_lambda,
        corr0,
        corr);

    if (res)
    {
        if (pd.getMass(m_bodies[0]) != 0.0)
            pd.getPosition(m_bodies[0]) += corr0;
        if (m_invMasses[0] != 0.0)
            x0 += corr[0];
        if (m_invMasses[1] != 0.0)
            x1 += corr[1];
        if (m_invMasses[2] != 0.0)
            x2 += corr[2];
        if (m_invMasses[3] != 0.0)
            x3 += corr[3];
    }
    return res;
}

bool ParticleTetContactConstraint::solveVelocityConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();

    const SimulationModel::TetModelVector &tetModels = model.getTetModels();
    TetModel *tm = tetModels[m_solidIndex];
    const unsigned int offset = tm->getIndexOffset();
    const unsigned int *indices = tm->getParticleMesh().getTets().data();
    Vector3r &v0 = pd.getVelocity(indices[4 * m_tetIndex] + offset);
    Vector3r &v1 = pd.getVelocity(indices[4 * m_tetIndex + 1] + offset);
    Vector3r &v2 = pd.getVelocity(indices[4 * m_tetIndex + 2] + offset);
    Vector3r &v3 = pd.getVelocity(indices[4 * m_tetIndex + 3] + offset);
    m_v[0] = v0;
    m_v[1] = v1;
    m_v[2] = v2;
    m_v[3] = v3;


    Vector3r corr_v0;
    Vector3r corr_v[4];
    const bool res = PositionBasedDynamics::velocitySolve_ParticleTetContactConstraint(
        pd.getInvMass(m_bodies[0]),
        pd.getPosition(m_bodies[0]),
        pd.getVelocity(m_bodies[0]),
        m_invMasses,
        m_x.data(),
        m_v.data(),
        m_bary,
        m_lambda,
        m_frictionCoeff,
        m_constraintInfo,
        corr_v0,
        corr_v);

    if (res)
    {
        if (pd.getMass(m_bodies[0]) != 0.0)
            pd.getVelocity(m_bodies[0]) += corr_v0;
        if (m_invMasses[0] != 0.0)
            v0 += corr_v[0];
        if (m_invMasses[1] != 0.0)
            v1 += corr_v[1];
        if (m_invMasses[2] != 0.0)
            v2 += corr_v[2];
        if (m_invMasses[3] != 0.0)
            v3 += corr_v[3];
    }
    return res;
}

// StretchShearConstraint
bool StretchShearConstraint::initConstraint(SimulationModel &model, const unsigned int particle1, const unsigned int particle2,
    const unsigned int quaternion1, const Real stretchingStiffness,
    const Real shearingStiffness1, const Real shearingStiffness2)
{
    m_stretchingStiffness = stretchingStiffness;
    m_shearingStiffness1 = shearingStiffness1;
    m_shearingStiffness2 = shearingStiffness2;
    m_bodies[0] = particle1;
    m_bodies[1] = particle2;
    m_bodies[2] = quaternion1;
    ParticleData &pd = model.getParticles();

    const Vector3r &x1_0 = pd.getPosition0(particle1);
    const Vector3r &x2_0 = pd.getPosition0(particle2);

    m_restLength = (x2_0 - x1_0).norm();

    return true;
}

bool StretchShearConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    ParticleData &pd = model.getParticles();
    OrientationData &od = model.getOrientations();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];
    const unsigned iq1 = m_bodies[2];

    Vector3r &x1 = pd.getPosition(i1);
    Vector3r &x2 = pd.getPosition(i2);
    Quaternionr &q1 = od.getQuaternion(iq1);
    const Real invMass1 = pd.getInvMass(i1);
    const Real invMass2 = pd.getInvMass(i2);
    const Real invMassq1 = od.getInvMass(iq1);
    Vector3r stiffness(m_shearingStiffness1,
                        m_shearingStiffness2,
                        m_stretchingStiffness);

    Vector3r corr1, corr2;
    Quaternionr corrq1;
    const bool res = PositionBasedCosseratRods::solve_StretchShearConstraint(
        x1, invMass1, x2, invMass2, q1, invMassq1,
        stiffness,
        m_restLength, corr1, corr2, corrq1);

    if (res)
    {
        if (invMass1 != 0.0)
            x1 += corr1;
        if (invMass2 != 0.0)
            x2 += corr2;
        if (invMassq1 != 0.0)
        {
            q1.coeffs() += corrq1.coeffs();
            q1.normalize();
        }
    }
    return res;
}

// BendTwistConstraint
bool BendTwistConstraint::initConstraint(SimulationModel &model, const unsigned int quaternion1,
    const unsigned int quaternion2, const Real twistingStiffness,
    const Real bendingStiffness1, const Real bendingStiffness2)
{
    m_twistingStiffness = twistingStiffness;
    m_bendingStiffness1 = bendingStiffness1;
    m_bendingStiffness2 = bendingStiffness2;
    m_bodies[0] = quaternion1;
    m_bodies[1] = quaternion2;
    OrientationData &od = model.getOrientations();

    const Quaternionr &q1_0 = od.getQuaternion(quaternion1);
    const Quaternionr &q2_0 = od.getQuaternion(quaternion2);

    m_restDarbouxVector = q1_0.conjugate() * q2_0;
    Quaternionr omega_plus, omega_minus;
    omega_plus.coeffs() = m_restDarbouxVector.coeffs() + Quaternionr(1, 0, 0, 0).coeffs();
    omega_minus.coeffs() = m_restDarbouxVector.coeffs() - Quaternionr(1, 0, 0, 0).coeffs();
    if (omega_minus.squaredNorm() > omega_plus.squaredNorm())
        m_restDarbouxVector.coeffs() *= -1.0;

    return true;
}

bool BendTwistConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    OrientationData &od = model.getOrientations();

    const unsigned i1 = m_bodies[0];
    const unsigned i2 = m_bodies[1];

    Quaternionr &q1 = od.getQuaternion(i1);
    Quaternionr &q2 = od.getQuaternion(i2);
    const Real invMass1 = od.getInvMass(i1);
    const Real invMass2 = od.getInvMass(i2);
    Vector3r stiffness(m_bendingStiffness1,
                       m_bendingStiffness2,
                       m_twistingStiffness);

    Quaternionr corr1, corr2;
    const bool res = PositionBasedCosseratRods::solve_BendTwistConstraint(
        q1, invMass1, q2, invMass2,
        stiffness,
        m_restDarbouxVector, corr1, corr2);

    if (res)
    {
        if (invMass1 != 0.0)
        {
            q1.coeffs() += corr1.coeffs();
            q1.normalize();
        }

        if (invMass2 != 0.0)
        {
            q2.coeffs() += corr2.coeffs();
            q2.normalize();
        }
    }
    return res;
}

// StretchBendingTwistingConstraint

bool PBD::StretchBendingTwistingConstraint::initConstraint(
    SimulationModel &model,
    const unsigned int segmentIndex1,
    const unsigned int segmentIndex2,
    const Vector3r &pos,
    const Real averageRadius,
    const Real averageSegmentLength,
    Real youngsModulus,
    Real torsionModulus)
{
    m_bodies[0] = segmentIndex1;
    m_bodies[1] = segmentIndex2;
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    const RigidBody &segment1 = *rb[m_bodies[0]];
    const RigidBody &segment2 = *rb[m_bodies[1]];

    m_lambdaSum.setZero();
    m_averageRadius = averageRadius;
    m_averageSegmentLength = averageSegmentLength;

    return DirectPositionBasedSolverForStiffRods::init_StretchBendingTwistingConstraint(
        segment1.getPosition(),
        segment1.getRotation(),
        segment2.getPosition(),
        segment2.getRotation(),
        pos,
        m_averageRadius,
        m_averageSegmentLength,
        youngsModulus,
        torsionModulus,
        m_constraintInfo,
        m_stiffnessCoefficientK,
        m_restDarbouxVector);
}


bool StretchBendingTwistingConstraint::initConstraintBeforeProjection(SimulationModel &model)
{
    DirectPositionBasedSolverForStiffRods::initBeforeProjection_StretchBendingTwistingConstraint(
        m_stiffnessCoefficientK,
        static_cast<Real>(1.0) / TimeManager::getCurrent()->getTimeStepSize(),
        m_averageSegmentLength,
        m_stretchCompliance,
        m_bendingAndTorsionCompliance,
        m_lambdaSum);
    return true;
};

bool StretchBendingTwistingConstraint::updateConstraint(SimulationModel &model)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();
    const RigidBody &segment1 = *rb[m_bodies[0]];
    const RigidBody &segment2 = *rb[m_bodies[1]];
    return DirectPositionBasedSolverForStiffRods::update_StretchBendingTwistingConstraint(
        segment1.getPosition(),
        segment1.getRotation(),
        segment2.getPosition(),
        segment2.getRotation(),
        m_constraintInfo);
}

bool StretchBendingTwistingConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    SimulationModel::RigidBodyVector &rb = model.getRigidBodies();

    RigidBody &segment1 = *rb[m_bodies[0]];
    RigidBody &segment2 = *rb[m_bodies[1]];

    Vector3r corr_x1, corr_x2;
    Quaternionr corr_q1, corr_q2;
    const bool res = DirectPositionBasedSolverForStiffRods::solve_StretchBendingTwistingConstraint(
        segment1.getInvMass(),
        segment1.getPosition(),
        segment1.getInertiaTensorInverseW(),
        segment1.getRotation(),
        segment2.getInvMass(),
        segment2.getPosition(),
        segment2.getInertiaTensorInverseW(),
        segment2.getRotation(),
        m_restDarbouxVector,
        m_averageSegmentLength,
        m_stretchCompliance,
        m_bendingAndTorsionCompliance,
        m_constraintInfo,
        corr_x1,
        corr_q1,
        corr_x2,
        corr_q2,
        m_lambdaSum);

    if (res)
    {
        if (segment1.getMass() != 0.0)
        {
            segment1.getPosition() += corr_x1;
            segment1.getRotation().coeffs() += corr_q1.coeffs();
            segment1.getRotation().normalize();
            segment1.rotationUpdated();
        }
        if (segment2.getMass() != 0.0)
        {
            segment2.getPosition() += corr_x2;
            segment2.getRotation().coeffs() += corr_q2.coeffs();
            segment2.getRotation().normalize();
            segment2.rotationUpdated();
        }
    }
    return res;
}

// DirectPositionBasedSolverForStiffRodsConstraint

PBD::DirectPositionBasedSolverForStiffRodsConstraint::~DirectPositionBasedSolverForStiffRodsConstraint()
{
    deleteNodes();
    if (intervals != NULL)
        delete[] intervals;
    if (forward != NULL)
        delete[] forward;
    if (backward != NULL)
        delete[] backward;
    if (root != NULL)
        delete[] root;
    root = NULL;
    forward = NULL;
    backward = NULL;
    intervals = NULL;
    numberOfIntervals = 0;
}

void DirectPositionBasedSolverForStiffRodsConstraint::deleteNodes()
{
    std::list<Node*>::iterator nodeIter;
    for (int i = 0; i < numberOfIntervals; i++)
    {
        for (nodeIter = forward[i].begin(); nodeIter != forward[i].end(); nodeIter++)
        {
            Node *node = *nodeIter;

            // Root node does not have to be deleted
            if (node->parent != NULL)
                delete node;
        }
    }
}

bool PBD::DirectPositionBasedSolverForStiffRodsConstraint::initConstraint(
    SimulationModel &model,
    const std::vector<std::pair<unsigned int, unsigned int>> & constraintSegmentIndices,
    const std::vector<Vector3r> &constraintPositions,
    const std::vector<Real> &averageRadii,
    const std::vector<Real> &averageSegmentLengths,
    const std::vector<Real> &youngsModuli,
    const std::vector<Real> &torsionModuli
    )
{
    // create unique segment indices from joints

    std::set<unsigned int> uniqueSegmentIndices;
    for (auto &idxPair :  constraintSegmentIndices)
    {
        uniqueSegmentIndices.insert(idxPair.first);
        uniqueSegmentIndices.insert(idxPair.second);
    }

    m_bodies.resize(uniqueSegmentIndices.size());

    // initialize m_bodies for constraint colouring algorithm of multi threading implementation

    size_t segmentIdx(0);

    for (auto idx : uniqueSegmentIndices)
    {
        m_bodies[segmentIdx] = idx;
        ++segmentIdx;
    }

    // create RodSegment instances and map simulation model body indices to RodSegment indices

    std::map<unsigned int, unsigned int> idxMap;
    unsigned int idx(0);

    m_Segments.reserve(uniqueSegmentIndices.size());
    m_rodSegments.reserve(uniqueSegmentIndices.size());
    for (auto bodyIdx : uniqueSegmentIndices)
    {
        idx = (unsigned int)m_Segments.size();
        idxMap[bodyIdx] = idx;
        m_Segments.push_back(RodSegmentImpl(model, bodyIdx));
        m_rodSegments.push_back(&m_Segments.back());
    }

    // create rod constraints

    m_Constraints.resize(constraintPositions.size());
    m_rodConstraints.resize(constraintPositions.size());

    for (size_t idx(0); idx < constraintPositions.size(); ++idx)
    {
        const std::pair<unsigned int, unsigned int> &bodyIndices( constraintSegmentIndices[idx]);
        unsigned int firstSegmentIndex(idxMap.find(bodyIndices.first)->second);
        unsigned int secondSegmentIndex(idxMap.find(bodyIndices.second)->second);

        m_Constraints[idx].m_segments.push_back(firstSegmentIndex);
        m_Constraints[idx].m_segments.push_back(secondSegmentIndex);
        m_Constraints[idx].m_averageSegmentLength = averageSegmentLengths[idx];
        m_rodConstraints[idx] = &m_Constraints[idx];
    }

    // initialize data of the sparse direct solver
    deleteNodes();
    DirectPositionBasedSolverForStiffRods::init_DirectPositionBasedSolverForStiffRodsConstraint(
        m_rodConstraints, m_rodSegments, intervals, numberOfIntervals, forward, backward, root,
        constraintPositions, averageRadii, youngsModuli, torsionModuli,
        m_rightHandSide, m_lambdaSums, m_bendingAndTorsionJacobians, m_corr_x, m_corr_q);

    return true;
}

bool PBD::DirectPositionBasedSolverForStiffRodsConstraint::initConstraintBeforeProjection(SimulationModel &model)
{
    DirectPositionBasedSolverForStiffRods::initBeforeProjection_DirectPositionBasedSolverForStiffRodsConstraint(
        m_rodConstraints, static_cast<Real>(1.0) / TimeManager::getCurrent()->getTimeStepSize(), m_lambdaSums);
    return true;
}


bool PBD::DirectPositionBasedSolverForStiffRodsConstraint::updateConstraint(SimulationModel &model)
{
    DirectPositionBasedSolverForStiffRods::update_DirectPositionBasedSolverForStiffRodsConstraint(
        m_rodConstraints, m_rodSegments);
    return true;
}


bool PBD::DirectPositionBasedSolverForStiffRodsConstraint::solvePositionConstraint(SimulationModel &model, const unsigned int iter)
{
    const bool res = DirectPositionBasedSolverForStiffRods::solve_DirectPositionBasedSolverForStiffRodsConstraint(
        m_rodConstraints, m_rodSegments, intervals, numberOfIntervals, forward, backward,
        m_rightHandSide, m_lambdaSums, m_bendingAndTorsionJacobians, m_corr_x, m_corr_q
        );

    // apply corrections to bodies
    SimulationModel::RigidBodyVector &rbs = model.getRigidBodies();

    for (size_t i(0); i < m_rodSegments.size(); ++i)
    {
        RodSegmentImpl & segment = m_Segments[i];
        RigidBody &rb1 = *rbs[segment.m_segmentIdx];
        if (rb1.getMass() != 0.0)
        {
            rb1.getPosition() += m_corr_x[i];
            rb1.getRotation().coeffs() += m_corr_q[i].coeffs();
            rb1.getRotation().normalize();
            rb1.rotationUpdated();
        }
    }

    return res;
}

bool PBD::DirectPositionBasedSolverForStiffRodsConstraint::RodSegmentImpl::isDynamic()
{
    return 0 != (m_model.getRigidBodies())[m_segmentIdx]->getMass();
}

Real PBD::DirectPositionBasedSolverForStiffRodsConstraint::RodSegmentImpl::Mass()
{
    return (m_model.getRigidBodies())[m_segmentIdx]->getMass();
}

const Vector3r & PBD::DirectPositionBasedSolverForStiffRodsConstraint::RodSegmentImpl::InertiaTensor()
{
    return (m_model.getRigidBodies())[m_segmentIdx]->getInertiaTensor();
}

const Vector3r & PBD::DirectPositionBasedSolverForStiffRodsConstraint::RodSegmentImpl::Position()
{
    return (m_model.getRigidBodies())[m_segmentIdx]->getPosition();
}

const Quaternionr & PBD::DirectPositionBasedSolverForStiffRodsConstraint::RodSegmentImpl::Rotation()
{
    return (m_model.getRigidBodies())[m_segmentIdx]->getRotation();
}