Program Listing for File Constraints.cpp

Return to documentation for file (Source\Physics\Src\PBD\Cloth\Constraints.cpp)

#include "Physics/PBD/Cloth/Constraints.h"
#include <array>

namespace Azura {
namespace Physics {
namespace PBD {

namespace {
float ComputeBendingC(const Containers::Vector<Vector3f>& vertices, U32 dest1, U32 dest2, U32 source) {
  const Vector3f v1 = vertices[dest1] - vertices[source];
  const Vector3f v2 = vertices[dest2] - vertices[source];

  return Vector3f::DotProduct(v1, v2) / Vector3f::CrossProduct(v1, v2).Length();
}
} // namespace

DistanceConstraint::DistanceConstraint(const ConstraintPoint& x1,
                                       const ConstraintPoint& x2,
                                       float restLength)
  : m_restLength(restLength) {
  m_points[0] = x1;
  m_points[1] = x2;
}

void DistanceConstraint::Compute(float stiffness,
                                 const Containers::Vector<Vector3f>& currentPoints,
                                 const Containers::Vector<float>& currentInvMass,
                                 Containers::Vector<ConstraintPointDelta>& result) {

  const U32 indexA = m_points[0].m_dataIdx;
  const U32 indexB = m_points[1].m_dataIdx;

  const float invMassSum     = currentInvMass[indexA] + currentInvMass[indexB];
  const float invMassFactor1 = currentInvMass[indexA] / invMassSum;
  const float invMassFactor2 = currentInvMass[indexB] / invMassSum;

  const Vector3f x12 = currentPoints[indexA] - currentPoints[indexB];

  result[0] = ConstraintPointDelta{
    indexA, ((stiffness * -1.0f * invMassFactor1 * (x12.Length() - m_restLength)) * x12.Normalized())
  };
  result[1] = ConstraintPointDelta{
    indexB, ((stiffness * invMassFactor2 * (x12.Length() - m_restLength)) * x12.Normalized())
  };
}

bool DistanceConstraint::operator<(const DistanceConstraint& rhs) const {
  return std::tie(m_points[0].m_dataIdx, m_points[1].m_dataIdx) < std::tie(rhs.m_points[0].m_dataIdx,
                                                                           rhs.m_points[1].m_dataIdx);
}

bool DistanceConstraint::operator==(const DistanceConstraint& rhs) const {
  const U32 indexA = m_points[0].m_dataIdx;
  const U32 indexB = m_points[0].m_dataIdx;

  const U32 rhsIndexA = rhs.m_points[0].m_dataIdx;
  const U32 rhsIndexB = rhs.m_points[0].m_dataIdx;

  return ((indexA == rhsIndexA) && (indexB == rhsIndexB)) || ((indexA == rhsIndexB) && (indexB == rhsIndexA));
}

LongRangeConstraint::LongRangeConstraint(const ConstraintPoint& x1,
  const ConstraintPoint& x2,
  float restLength)
  : m_restLength(restLength) {
  m_points[0] = x1;
  m_points[1] = x2;
}

void LongRangeConstraint::Compute(float stiffness,
  const Containers::Vector<Vector3f>& currentPoints,
  const Containers::Vector<float>& currentInvMass,
  Containers::Vector<ConstraintPointDelta>& result) {

  const U32 indexA = m_points[0].m_dataIdx;
  const U32 indexB = m_points[1].m_dataIdx;

  const float invMassSum     = currentInvMass[indexA] + currentInvMass[indexB];
  const float invMassFactor1 = currentInvMass[indexA] / invMassSum;
  const float invMassFactor2 = currentInvMass[indexB] / invMassSum;

  const Vector3f x12 = currentPoints[indexA] - currentPoints[indexB];

  result[0] = ConstraintPointDelta{
    indexA, ((stiffness * -1.0f * invMassFactor1 * (x12.Length() - m_restLength)) * x12.Normalized())
  };
  result[1] = ConstraintPointDelta{
    indexB, ((stiffness * invMassFactor2 * (x12.Length() - m_restLength)) * x12.Normalized())
  };
}

bool LongRangeConstraint::operator<(const LongRangeConstraint& rhs) const {
  return std::tie(m_points[0].m_dataIdx, m_points[1].m_dataIdx) < std::tie(rhs.m_points[0].m_dataIdx,
    rhs.m_points[1].m_dataIdx);
}

bool LongRangeConstraint::operator==(const LongRangeConstraint& rhs) const {
  const U32 indexA = m_points[0].m_dataIdx;
  const U32 indexB = m_points[0].m_dataIdx;

  const U32 rhsIndexA = rhs.m_points[0].m_dataIdx;
  const U32 rhsIndexB = rhs.m_points[0].m_dataIdx;

  return ((indexA == rhsIndexA) && (indexB == rhsIndexB)) || ((indexA == rhsIndexB) && (indexB == rhsIndexA));
}

BendingConstraint::BendingConstraint(const Containers::Vector<Vector3f>& currentPoints,
                                     const ConstraintPoint& x0,
                                     const ConstraintPoint& x1,
                                     const ConstraintPoint& x2,
                                     const ConstraintPoint& x3) {

  m_points[0] = x0;
  m_points[1] = x1;
  m_points[2] = x2;
  m_points[3] = x3;

  const float c01 = ComputeBendingC(currentPoints, x2.m_dataIdx, x0.m_dataIdx, x1.m_dataIdx);
  const float c04 = ComputeBendingC(currentPoints, x3.m_dataIdx, x0.m_dataIdx, x1.m_dataIdx);
  const float c03 = ComputeBendingC(currentPoints, x3.m_dataIdx, x1.m_dataIdx, x0.m_dataIdx);
  const float c02 = ComputeBendingC(currentPoints, x2.m_dataIdx, x1.m_dataIdx, x0.m_dataIdx);

  const float aValue = c01 + c04;
  const float bValue = c02 + c03;
  const float cValue = -c01 - c02;
  const float dValue = -c03 - c04;

  // Triangle 0
  const Vector3f s1 = currentPoints[x2.m_dataIdx] - currentPoints[x1.m_dataIdx];
  const Vector3f s2 = currentPoints[x0.m_dataIdx] - currentPoints[x1.m_dataIdx];
  const float Area0 = Vector3f::CrossProduct(s1, s2).Length() / 2.0f;

  // Triangle 1
  const Vector3f s3 = currentPoints[x3.m_dataIdx] - currentPoints[x1.m_dataIdx];
  const Vector3f s4 = currentPoints[x0.m_dataIdx] - currentPoints[x1.m_dataIdx];
  const float Area1 = Vector3f::CrossProduct(s3, s4).Length() / 2.0f;

  m_Q              = Matrix4f(0.0f);
  m_Q.GetColumn(0) = Vector4f(aValue * aValue, bValue * aValue, cValue * aValue, dValue * aValue);
  m_Q.GetColumn(1) = Vector4f(aValue * bValue, bValue * bValue, cValue * bValue, dValue * bValue);
  m_Q.GetColumn(2) = Vector4f(aValue * cValue, bValue * cValue, cValue * cValue, dValue * cValue);
  m_Q.GetColumn(3) = Vector4f(aValue * dValue, bValue * dValue, cValue * dValue, dValue * dValue);

  m_Q = (3.0f / (Area0 + Area1)) * m_Q;
}

void BendingConstraint::Compute(float stiffness,
                                const Containers::Vector<Vector3f>& currentPoints,
                                const Containers::Vector<float>& currentInvMass,
                                Containers::Vector<ConstraintPointDelta>& result) {

  std::array<U32, 4> indices = {
    m_points[0].m_dataIdx, m_points[1].m_dataIdx, m_points[2].m_dataIdx, m_points[3].m_dataIdx
  };
  const std::array<float, 4> invMasses = {
    currentInvMass[indices[0]], currentInvMass[indices[1]], currentInvMass[indices[2]], currentInvMass[indices[3]]
  };

  float cX = 0.0f;

  for (int idx   = 0; idx < 4; ++idx) {
    for (int idy = 0; idy < 4; ++idy) {
      cX += m_Q(idx, idy) * Vector3f::DotProduct(currentPoints[indices[idx]], currentPoints[indices[idy]]); // NOLINT
    }
  }

  cX = cX / 2.0f;

  std::array<Vector3f, 4> partialDeltas = {};
  float sum                             = 0.0f;

  for (U32 idx         = 0; idx < 4; ++idx) {
    partialDeltas[idx] = ComputeBendingGradient(currentPoints, m_Q, indices, idx);          // NOLINT
    sum += (invMasses[idx] * Vector3f::DotProduct(partialDeltas[idx], partialDeltas[idx])); // NOLINT
  }

  if (std::abs(sum) > EPSILON) {
    for (U32 idx  = 0; idx < 4; ++idx) {
      result[idx] = ConstraintPointDelta{
        indices[idx], ((stiffness * -1.0f * cX * invMasses[idx] * partialDeltas[idx]) / sum)
      };
    }
  }
}

Vector3f BendingConstraint::ComputeBendingGradient(const Containers::Vector<Vector3f>& currentPositions,
                                                   const Matrix4f& Q,
                                                   const std::array<U32, 4>& indices,
                                                   U32 rowI) {
  Vector3f sum = Vector3f(0.0f);
  for (U32 idj = 0; idj < 4; ++idj) {
    sum += Q(rowI, idj) * currentPositions[indices[idj]];
  }

  return sum;
}

} // namespace PBD
} // namespace Physics
} // namespace Azura