Program Listing for File ClothPlane.cpp

Return to documentation for file (Source\Physics\Src\Geometry\ClothPlane.cpp)

#include "Physics/Geometry/ClothPlane.h"

#include "Physics/Geometry/GenericTypes.h"

namespace Azura {
namespace Physics {

using namespace PBD; // NOLINT

namespace {
const RawStorageFormat VERTEX_FORMAT = RawStorageFormat::R32G32B32_FLOAT;
const RawStorageFormat UV_FORMAT = RawStorageFormat::R32G32_FLOAT;
const RawStorageFormat NORMAL_FORMAT = RawStorageFormat::R32G32B32_FLOAT;
const RawStorageFormat INDEX_FORMAT  = RawStorageFormat::R32_UINT;

float ComputeBendingC(const std::vector<Vector4f>& vertices, U32 dest1, U32 dest2, U32 source) {
  Vector3f v1 = vertices[dest1].xyz() - vertices[source].xyz();
  Vector3f v2 = vertices[dest2].xyz() - vertices[source].xyz();

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

} // namespace

ClothPlane::ClothPlane(const Vector2f& boundMin, const Vector2f& boundMax, Memory::Allocator& allocator)
  : ClothPlane(ClothTriangulation::Regular, boundMin, boundMax, 0.0f, Vector2u(1, 1), Vector2u(1, 1), allocator) {
}

ClothPlane::ClothPlane(ClothTriangulation triangulation,
                       const Vector2f& boundMin,
                       const Vector2f& boundMax,
                       float worldHeight,
                       const Vector2u& subDivisions,
                       const Vector2u& uvScale,
                       Memory::Allocator& allocator)
  : m_vertices(allocator),
    m_vertexInvMass(allocator),
    m_normals(allocator),
    m_triangles(allocator),
    m_uv(allocator) {
  const float stepX = float(boundMax[0] - boundMin[0]) / subDivisions[0];
  const float stepY = float(boundMax[1] - boundMin[1]) / subDivisions[1];

  const U32 totalVertices = (subDivisions[0] + 1) * (subDivisions[1] + 1);

  m_vertices.Reserve(totalVertices);
  m_normals.Reserve(totalVertices);
  m_uv.Reserve(totalVertices);
  m_triangles.Reserve(subDivisions[0] * subDivisions[1] * 2);

  U32 xCount = 0;
  U32 yCount = 0;
  for (float xCoord   = boundMin[0]; xCoord <= boundMax[0] + EPSILON;) {
    yCount = 0;
    for (float yCoord = boundMin[1]; yCoord <= boundMax[1] + EPSILON;) {
      m_vertices.EmplaceBack(xCoord, worldHeight, yCoord);
      m_normals.EmplaceBack(0.0f, 1.0f, 0.0f);

      const float uvX = uvScale[0] * xCount / float(subDivisions[0]);
      const float uvY = uvScale[1] * yCount / float(subDivisions[1]);
      m_uv.EmplaceBack(uvX, uvY);

      yCoord += stepY;
      ++yCount;
    }

    xCoord += stepX;
    ++xCount;
  }

  m_vertexInvMass.Reserve(m_vertices.GetSize());

  for (U32 idx      = 0; idx < subDivisions[0]; ++idx) {
    for (U32 idy    = 0; idy < subDivisions[1]; ++idy) {
      const U32 i1  = ((subDivisions[1] + 1) * idx) + idy;
      const U32 i2  = ((subDivisions[1] + 1) * (idx + 1)) + idy;
      const U32 i21 = i2 + 1;
      const U32 i11 = i1 + 1;

      // Anchors
      const bool isEvenX = idx % 2 == 0;
      const bool isEvenY = idy % 2 == 0;

      const auto triangleIdx = U32(m_triangles.GetSize());

      if (triangulation == ClothTriangulation::Regular || ((isEvenX && !isEvenY) || (!isEvenX && isEvenY))) {
        m_triangles.EmplaceBack(i1, i2, i21);
        m_triangles.EmplaceBack(i1, i21, i11);

        AddEdgeTriangleNeighbor(Edge{i1, i2}, triangleIdx);
        AddEdgeTriangleNeighbor(Edge{i2, i21}, triangleIdx);
        AddEdgeTriangleNeighbor(Edge{i21, i1}, triangleIdx);

        AddEdgeTriangleNeighbor(Edge{i1, i21}, triangleIdx + 1);
        AddEdgeTriangleNeighbor(Edge{i21, i11}, triangleIdx + 1);
        AddEdgeTriangleNeighbor(Edge{i11, i1}, triangleIdx + 1);
      } else {
        m_triangles.EmplaceBack(Vector3u(i1, i2, i11));
        m_triangles.EmplaceBack(Vector3u(i2, i21, i11));

        AddEdgeTriangleNeighbor(Edge{i1, i2}, triangleIdx);
        AddEdgeTriangleNeighbor(Edge{i2, i11}, triangleIdx);
        AddEdgeTriangleNeighbor(Edge{i11, i1}, triangleIdx);

        AddEdgeTriangleNeighbor(Edge{i2, i21}, triangleIdx + 1);
        AddEdgeTriangleNeighbor(Edge{i21, i11}, triangleIdx + 1);
        AddEdgeTriangleNeighbor(Edge{i11, i2}, triangleIdx + 1);
      }
    }
  }
}

U32 ClothPlane::VertexDataSize() const {
  return U32(m_vertices.GetSize() * GetFormatSize(VERTEX_FORMAT));
}

U32 ClothPlane::IndexDataSize() const {
  return U32(m_triangles.GetSize() * GetFormatSize(INDEX_FORMAT) * 3);
}

U32 ClothPlane::NormalDataSize() const {
  return U32(m_normals.GetSize() * GetFormatSize(NORMAL_FORMAT));
}

U32 ClothPlane::UVDataSize() const {
  return U32(m_vertices.GetSize() * GetFormatSize(UV_FORMAT));
}

const U8* ClothPlane::VertexData() const {
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
  return reinterpret_cast<const U8*>(m_vertices.Data());
}

const U8* ClothPlane::IndexData() const {
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
  return reinterpret_cast<const U8*>(m_triangles.Data());
}

const U8* ClothPlane::NormalData() const {
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
  return reinterpret_cast<const U8*>(m_normals.Data());
}

const U8* ClothPlane::UVData() const {
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
  return reinterpret_cast<const U8*>(m_uv.Data());
}

RawStorageFormat ClothPlane::GetVertexFormat() const {
  return VERTEX_FORMAT;
}

RawStorageFormat ClothPlane::GetIndexFormat() const {
  return INDEX_FORMAT;
}

RawStorageFormat ClothPlane::GetNormalFormat() const {
  return NORMAL_FORMAT;
}

RawStorageFormat ClothPlane::GetUVFormat() const {
  return UV_FORMAT;
}

void ClothPlane::SetAnchorOnIndex(U32 idx) {
  m_anchorIdx.push_back(idx);
}

const Containers::Vector<float>& ClothPlane::GetVertexInverseMass() const {
  return m_vertexInvMass;
}

ClothSolvingView ClothPlane::GetPBDSolvingView(Memory::Allocator& allocator) {
  for (SizeType idx = 0; idx < m_vertices.GetSize(); ++idx) {
    if (std::find(m_anchorIdx.begin(), m_anchorIdx.end(), idx) != m_anchorIdx.end()) {
      m_vertexInvMass.PushBack(0.0f);
      continue;
    }

    m_vertexInvMass.PushBack(1.0f);
  }

  U32 numBendingConstraints = 0;
  for (const auto& pair : m_edgeTriangleMap) {
    if (pair.second.size() != 2) {
      continue;
    }

    ++numBendingConstraints;
  }

  ClothSolvingView solvingView = ClothSolvingView(
    m_vertices,
    m_vertexInvMass,
    U32(m_edgeTriangleMap.size()),
    U32(m_edgeTriangleMap.size()),
    numBendingConstraints,
    allocator
  );

  U32 vertIdx = 0;

  for(const auto& vertex : m_vertices)
  {
    float closestDistance = std::numeric_limits<float>::max();
    U32 closestAnchorIdx = 0;
    for (const auto& anchorIdx : m_anchorIdx)
    {
      const float distance = (m_vertices[anchorIdx] - vertex).Length();
      if (distance < closestDistance) {
        closestDistance = distance;
        closestAnchorIdx = anchorIdx;
      }
    }
    solvingView.AddConstraint(LongRangeConstraint{
      ConstraintPoint{vertIdx},
      ConstraintPoint{closestAnchorIdx},
      closestDistance
      });
    ++vertIdx;
  }

  for (const auto& pair : m_edgeTriangleMap) {
    const Edge& edge = pair.first;

    // Add Distance Constraint
    solvingView.AddConstraint(DistanceConstraint{
      ConstraintPoint{edge.m_indexA},
      ConstraintPoint{edge.m_indexB},
      (m_vertices[edge.m_indexA] - m_vertices[edge.m_indexB]).Length()
    });

    // Add Bending Constraint
    if (pair.second.size() != 2) {
      continue;
    }

    const U32 indexX0 = edge.m_indexA;
    const U32 indexX1 = edge.m_indexB;
    U32 indexX2       = 0;
    U32 indexX3       = 0;

    Vector3u tri1 = m_triangles[pair.second[0]];
    Vector3u tri2 = m_triangles[pair.second[1]];

    for (U32 idx = 0; idx < 3; ++idx) {
      if (tri1[idx] == edge.m_indexA) {
        continue;
      }

      if (tri1[idx] == edge.m_indexB) {
        continue;
      }

      indexX2 = tri1[idx];
    }

    for (U32 idx = 0; idx < 3; ++idx) {
      if (tri2[idx] == edge.m_indexA) {
        continue;
      }

      if (tri2[idx] == edge.m_indexB) {
        continue;
      }

      indexX3 = tri2[idx];
    }

    solvingView.AddConstraint(BendingConstraint(
      m_vertices,
      ConstraintPoint{indexX0},
      ConstraintPoint{indexX1},
      ConstraintPoint{indexX2},
      ConstraintPoint{indexX3}
    ));
  }

  return solvingView;
}

U32 ClothPlane::GetVertexCount() const {
  return U32(m_vertices.GetSize());
}

U32 ClothPlane::GetIndexCount() const {
  return U32(m_triangles.GetSize() * 3);
}

U32 ClothPlane::TotalDataSize() const {
  return VertexDataSize() + IndexDataSize() + NormalDataSize() + UVDataSize();
}

void ClothPlane::AddEdgeTriangleNeighbor(const Edge& edge, const U32 triangleIdx) {
  const auto itr = m_edgeTriangleMap.find(edge);

  if (itr == m_edgeTriangleMap.end()) {
    m_edgeTriangleMap[edge] = std::vector<U32>();
    m_edgeTriangleMap[edge].reserve(2);
  }

  m_edgeTriangleMap[edge].push_back(triangleIdx);
}

} // namespace Physics
} // namespace Azura