18 tearThreshold_ = std::isfinite(strain) && strain > 1.f ? strain : 0.f;
24 if (!validIndex(particleA) || !validIndex(particleB))
25 throw Exception(
"Cloth3D.tearConstraint: particle index out of range");
26 const auto found = std::find_if(links_.begin(), links_.end(), [particleA, particleB](
const Link& link) {
27 return link.kind == ClothConstraintKind::Structural &&
28 ((link.a == particleA && link.b == particleB) || (link.a == particleB && link.b == particleA));
30 if (
found == links_.end())
throw Exception(
"Cloth3D.tearConstraint: structural edge not found");
31 tearLink(
static_cast<size_t>(std::distance(links_.begin(),
found)));
34void Cloth3D::applyAutomaticTearing() {
35 if (tearThreshold_ <= 1.f || maxTearsPerStep_ <= 0)
return;
37 for (
size_t index = 0;
index < links_.size() && torn < maxTearsPerStep_;) {
38 const Link& link = links_[
index];
43 const Particle&
a = particles_[
static_cast<size_t>(link.a)];
44 const Particle&
b = particles_[
static_cast<size_t>(link.b)];
45 const float dx =
b.x -
a.x;
46 const float dy =
b.y -
a.y;
47 const float dz =
b.z -
a.z;
48 if (std::sqrt(
dx *
dx +
dy *
dy +
dz *
dz) <= link.rest * tearThreshold_) {
57void Cloth3D::tearLink(
size_t linkIndex) {
58 const int a = links_[linkIndex].a;
59 const int b = links_[linkIndex].b;
60 links_.erase(links_.begin() +
static_cast<std::ptrdiff_t
>(linkIndex));
61 std::erase_if(triangles_, [
a,
b](
const Tri&
triangle) {
65 hasA = hasA || vertex ==
a;
66 hasB = hasB || vertex ==
b;
70 ++tornConstraintCount_;
72 rebuildFoldPairsFromTriangles();
75void Cloth3D::rebuildFoldPairsFromTriangles() {
79 std::vector<int> opposites;
81 std::map<std::pair<int, int>, EdgeUse>
edges;
82 for (
const Tri&
triangle : triangles_) {
91 use.opposites.push_back(opposite);
96 if (use.opposites.
size() == 2)
97 foldPairs_.push_back({use.a, use.b, use.opposites[0], use.opposites[1]});
EVENGINE_API_FOUNDATION public API.
int getMaxTearsPerStep() const
Return the automatic tear budget per substep.
void setMaxTearsPerStep(int count)
Limit automatic structural tears per simulation substep.
int getTornConstraintCount() const
Return the cumulative number of torn structural constraints.
void tearConstraint(int particleA, int particleB)
Tear an existing structural edge.
float getTearThreshold() const
Return the automatic tear strain threshold, or zero when disabled.
void setTearThreshold(float strain)
Set automatic structural-edge tear strain; zero disables, values must exceed one.
constexpr HexDirection opposite(HexDirection d) noexcept
The direction opposite to d.
Optional physics backend for vehicle mobility and body attach.