载入中...
搜索中...
未找到
Cloth3DConstraints.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4
5#include <algorithm>
6#include <cmath>
7#include <vector>
8
9namespace eve::physics {
10
11float Cloth3D::getStretchCompliance() const { return stretchCompliance_; }
12float Cloth3D::getShearCompliance() const { return shearCompliance_; }
13float Cloth3D::getBendCompliance() const { return bendCompliance_; }
14float Cloth3D::getTetherScale() const { return tetherScale_; }
15float Cloth3D::getTetherCompliance() const { return tetherCompliance_; }
16float Cloth3D::getPressure() const { return pressure_; }
17float Cloth3D::getVolumeCompliance() const { return volumeCompliance_; }
18float Cloth3D::getCollisionFriction() const { return collisionFriction_; }
19float Cloth3D::getCollisionRestitution() const { return collisionRestitution_; }
20uint64_t Cloth3D::getCollisionCategoryBits() const { return collisionCategoryBits_; }
21uint64_t Cloth3D::getCollisionMaskBits() const { return collisionMaskBits_; }
22int Cloth3D::getTriangleCount() const { return static_cast<int>(triangles_.size()); }
23int Cloth3D::getDistanceConstraintCount() const { return static_cast<int>(links_.size()); }
24int Cloth3D::getTetherConstraintCount() const { return static_cast<int>(tethers_.size()); }
25int Cloth3D::getSkinConstraintCount() const { return static_cast<int>(skinConstraints_.size()); }
26namespace {
27
28struct Vec3 {
29 float x = 0.f;
30 float y = 0.f;
31 float z = 0.f;
32};
33
34Vec3 cross(const Vec3& a, const Vec3& b) {
35 return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
36}
37
38float dot(const Vec3& a, const Vec3& b) { return a.x * b.x + a.y * b.y + a.z * b.z; }
39
40float length(const Vec3& value) { return std::sqrt(dot(value, value)); }
41
42} // namespace
43
44bool Cloth3D::supportsFeature(const std::string& feature) const {
45 return feature == "arbitrary_topology" || feature == "xpbd_material" || feature == "tether" ||
46 feature == "volume_pressure" || feature == "skin_backstop" || feature == "attachment" ||
47 feature == "world_collision" || feature == "collision_filter" || feature == "friction" ||
48 feature == "self_collision" || feature == "aerodynamics" || feature == "runtime_tearing";
49}
50
51void Cloth3D::setSkinConstraint(int index, float x, float y, float z, float nx, float ny, float nz, float radius,
52 float backstopDistance, float backstopRadius, float compliance) {
53 if (!validIndex(index)) throw Exception("Cloth3D.setSkinConstraint: index out of range");
54 const float normalLength = std::sqrt(nx * nx + ny * ny + nz * nz);
55 if (!std::isfinite(normalLength) || normalLength <= 1e-7f)
56 throw Exception("Cloth3D.setSkinConstraint: normal must be finite and non-zero");
57 auto found = std::find_if(skinConstraints_.begin(), skinConstraints_.end(),
58 [index](const SkinConstraint& item) { return item.particle == index; });
59 SkinConstraint value{index,
60 x,
61 y,
62 z,
63 nx / normalLength,
64 ny / normalLength,
65 nz / normalLength,
66 std::isfinite(radius) ? std::max(0.f, radius) : 0.f,
67 std::isfinite(backstopDistance) ? backstopDistance : -1.f,
68 std::isfinite(backstopRadius) ? std::max(0.f, backstopRadius) : 0.f,
69 std::isfinite(compliance) ? std::max(0.f, compliance) : 0.f,
70 0.f};
71 if (found == skinConstraints_.end())
72 skinConstraints_.push_back(value);
73 else
74 *found = value;
75}
76
77void Cloth3D::updateSkinReference(int index, float x, float y, float z, float nx, float ny, float nz) {
78 auto found = std::find_if(skinConstraints_.begin(), skinConstraints_.end(),
79 [index](const SkinConstraint& item) { return item.particle == index; });
80 if (found == skinConstraints_.end()) throw Exception("Cloth3D.updateSkinReference: particle is not constrained");
81 const float normalLength = std::sqrt(nx * nx + ny * ny + nz * nz);
82 if (!std::isfinite(normalLength) || normalLength <= 1e-7f)
83 throw Exception("Cloth3D.updateSkinReference: normal must be finite and non-zero");
84 found->x = x;
85 found->y = y;
86 found->z = z;
87 found->nx = nx / normalLength;
88 found->ny = ny / normalLength;
89 found->nz = nz / normalLength;
90}
91
93 std::erase_if(skinConstraints_, [index](const SkinConstraint& item) { return item.particle == index; });
94}
95
97 return std::any_of(skinConstraints_.begin(), skinConstraints_.end(),
98 [index](const SkinConstraint& item) { return item.particle == index; });
99}
100
101void Cloth3D::attachParticle(int index, float x, float y, float z, float compliance) {
102 if (!validIndex(index)) throw Exception("Cloth3D.attachParticle: index out of range");
103 auto found = std::find_if(attachments_.begin(), attachments_.end(),
104 [index](const Attachment& item) { return item.particle == index; });
105 const Attachment value{index, x, y, z, std::isfinite(compliance) ? std::max(0.f, compliance) : 0.f, 0.f};
106 if (found == attachments_.end())
107 attachments_.push_back(value);
108 else
109 *found = value;
110}
111
112void Cloth3D::updateAttachment(int index, float x, float y, float z) {
113 auto found = std::find_if(attachments_.begin(), attachments_.end(),
114 [index](const Attachment& item) { return item.particle == index; });
115 if (found == attachments_.end()) throw Exception("Cloth3D.updateAttachment: particle is not attached");
116 found->x = x;
117 found->y = y;
118 found->z = z;
119}
120
122 std::erase_if(attachments_, [index](const Attachment& item) { return item.particle == index; });
123}
124
125bool Cloth3D::isAttached(int index) const {
126 return std::any_of(attachments_.begin(), attachments_.end(),
127 [index](const Attachment& item) { return item.particle == index; });
128}
129
130void Cloth3D::setTetherScale(float scale) { tetherScale_ = std::isfinite(scale) ? std::max(0.f, scale) : 0.f; }
131
132void Cloth3D::setTetherCompliance(float compliance) {
133 tetherCompliance_ = std::isfinite(compliance) ? std::max(0.f, compliance) : 0.f;
134}
135
136void Cloth3D::setPressure(float pressure) { pressure_ = std::isfinite(pressure) ? std::max(0.f, pressure) : 0.f; }
137
138void Cloth3D::setVolumeCompliance(float compliance) {
139 volumeCompliance_ = std::isfinite(compliance) ? std::max(0.f, compliance) : 0.f;
140}
141
143 if (restVolume_ == 0.f) return 0.f;
144 double volume = 0.0;
145 for (const Tri& triangle : triangles_) {
146 const Particle& a = particles_[static_cast<size_t>(triangle.v[0])];
147 const Particle& b = particles_[static_cast<size_t>(triangle.v[1])];
148 const Particle& c = particles_[static_cast<size_t>(triangle.v[2])];
149 volume += (double(a.x) * (double(b.y) * c.z - double(b.z) * c.y) +
150 double(a.y) * (double(b.z) * c.x - double(b.x) * c.z) +
151 double(a.z) * (double(b.x) * c.y - double(b.y) * c.x)) /
152 6.0;
153 }
154 return static_cast<float>(volume);
155}
156
157void Cloth3D::solveTetherConstraints(float dt) {
158 if (tetherScale_ <= 0.f || tethers_.empty() || dt <= 0.f) return;
159 const float alpha = tetherCompliance_ / (dt * dt);
160 for (Tether& tether : tethers_) {
161 Particle& particle = particles_[static_cast<size_t>(tether.particle)];
162 const Particle& anchor = particles_[static_cast<size_t>(tether.anchor)];
163 if (particle.pinned) continue;
164 const float dx = particle.x - anchor.x;
165 const float dy = particle.y - anchor.y;
166 const float dz = particle.z - anchor.z;
167 const float distance = std::sqrt(dx * dx + dy * dy + dz * dz);
168 const float maximum = tether.maxLength * tetherScale_;
169 if (distance <= maximum || distance <= 1e-7f) continue;
170 const float weight = particle.inverseMass;
171 const float deltaLambda = (-(distance - maximum) - alpha * tether.lambda) / (weight + alpha);
172 tether.lambda += deltaLambda;
173 particle.x += weight * dx / distance * deltaLambda;
174 particle.y += weight * dy / distance * deltaLambda;
175 particle.z += weight * dz / distance * deltaLambda;
176 }
177}
178
179void Cloth3D::solveVolumeConstraint(float dt) {
180 if (pressure_ <= 0.f || restVolume_ == 0.f || dt <= 0.f) return;
181 std::vector<Vec3> gradients(particles_.size());
182 for (const Tri& triangle : triangles_) {
183 const Particle& a = particles_[static_cast<size_t>(triangle.v[0])];
184 const Particle& b = particles_[static_cast<size_t>(triangle.v[1])];
185 const Particle& c = particles_[static_cast<size_t>(triangle.v[2])];
186 const Vec3 av{a.x, a.y, a.z};
187 const Vec3 bv{b.x, b.y, b.z};
188 const Vec3 cv{c.x, c.y, c.z};
189 const Vec3 ga = cross(bv, cv);
190 const Vec3 gb = cross(cv, av);
191 const Vec3 gc = cross(av, bv);
192 for (const auto& [index, gradient] :
193 {std::pair{triangle.v[0], ga}, std::pair{triangle.v[1], gb}, std::pair{triangle.v[2], gc}}) {
194 gradients[static_cast<size_t>(index)].x += gradient.x / 6.f;
195 gradients[static_cast<size_t>(index)].y += gradient.y / 6.f;
196 gradients[static_cast<size_t>(index)].z += gradient.z / 6.f;
197 }
198 }
199
200 float denominator = 0.f;
201 for (size_t i = 0; i < particles_.size(); ++i)
202 if (!particles_[i].pinned) denominator += particles_[i].inverseMass * dot(gradients[i], gradients[i]);
203 const float alpha = volumeCompliance_ / (dt * dt);
204 denominator += alpha;
205 if (denominator <= 1e-10f) return;
206 const float constraint = getCurrentVolume() - restVolume_ * pressure_;
207 const float deltaLambda = (-constraint - alpha * volumeLambda_) / denominator;
208 volumeLambda_ += deltaLambda;
209 for (size_t i = 0; i < particles_.size(); ++i) {
210 if (particles_[i].pinned) continue;
211 particles_[i].x += particles_[i].inverseMass * gradients[i].x * deltaLambda;
212 particles_[i].y += particles_[i].inverseMass * gradients[i].y * deltaLambda;
213 particles_[i].z += particles_[i].inverseMass * gradients[i].z * deltaLambda;
214 }
215}
216
217void Cloth3D::solveSkinConstraints(float dt) {
218 if (dt <= 0.f) return;
219 for (SkinConstraint& skin : skinConstraints_) {
220 Particle& particle = particles_[static_cast<size_t>(skin.particle)];
221 if (particle.pinned) continue;
222 Vec3 delta{particle.x - skin.x, particle.y - skin.y, particle.z - skin.z};
223 float distance = length(delta);
224 if (skin.radius > 0.f && distance > skin.radius && distance > 1e-7f) {
225 const float alpha = skin.compliance / (dt * dt);
226 const float weight = particle.inverseMass;
227 const float dl = (-(distance - skin.radius) - alpha * skin.lambda) / (weight + alpha);
228 skin.lambda += dl;
229 particle.x += weight * delta.x / distance * dl;
230 particle.y += weight * delta.y / distance * dl;
231 particle.z += weight * delta.z / distance * dl;
232 }
233 if (skin.backstopDistance < 0.f || skin.backstopRadius <= 0.f) continue;
234 const Vec3 center{skin.x - skin.nx * skin.backstopDistance, skin.y - skin.ny * skin.backstopDistance,
235 skin.z - skin.nz * skin.backstopDistance};
236 delta = {particle.x - center.x, particle.y - center.y, particle.z - center.z};
237 distance = length(delta);
238 if (distance >= skin.backstopRadius) continue;
239 Vec3 direction = distance > 1e-7f ? Vec3{delta.x / distance, delta.y / distance, delta.z / distance}
240 : Vec3{skin.nx, skin.ny, skin.nz};
241 const float correction = skin.backstopRadius - distance;
242 particle.x += direction.x * correction;
243 particle.y += direction.y * correction;
244 particle.z += direction.z * correction;
245 }
246}
247
248void Cloth3D::solveAttachments(float dt) {
249 if (dt <= 0.f) return;
250 for (Attachment& attachment : attachments_) {
251 Particle& particle = particles_[static_cast<size_t>(attachment.particle)];
252 const Vec3 delta{particle.x - attachment.x, particle.y - attachment.y, particle.z - attachment.z};
253 const float distance = length(delta);
254 if (distance <= 1e-7f) continue;
255 const float alpha = attachment.compliance / (dt * dt);
256 const float weight = std::max(particle.inverseMass, 1.f);
257 const float dl = (-distance - alpha * attachment.lambda) / (weight + alpha);
258 attachment.lambda += dl;
259 particle.x += weight * delta.x / distance * dl;
260 particle.y += weight * delta.y / distance * dl;
261 particle.z += weight * delta.z / distance * dl;
262 }
263}
264
265} // namespace eve::physics
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
double volume
float length
Definition CaveMesh.cpp:94
Vec3 anchor
Definition CaveMesh.cpp:90
float nx
float nz
float ny
int triangle
float maximum[3]
std::int32_t c
eve::ResourcePin pinned
std::array< float, 3 > scale
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
float distance
float radius
RoadLaneDirection direction
bool found
float dz
float dy
float dx
uint32_t index
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
float getBendCompliance() const
Return XPBD distance-bend compliance in m/N.
float getPressure() const
Return the closed-mesh target volume ratio.
uint64_t getCollisionCategoryBits() const
Return cloth collision category bits.
void setPressure(float pressure)
Set closed-mesh target volume ratio; zero disables volume solving.
uint64_t getCollisionMaskBits() const
Return cloth collision mask bits.
bool isAttached(int index) const
Return whether a particle currently has an attachment.
void setTetherScale(float scale)
Set tether maximum-length scale; zero disables tethers, one uses baked lengths.
void updateSkinReference(int index, float x, float y, float z, float nx, float ny, float nz)
Update the animated reference pose of an existing skin constraint.
bool supportsFeature(const std::string &feature) const
Query a stable cloth feature name without assuming CPU/GPU parity.
int getTetherConstraintCount() const
Return baked geodesic tether count.
float getVolumeCompliance() const
Return XPBD closed-volume compliance.
float getTetherScale() const
Return the current tether length scale.
void clearSkinConstraint(int index)
Remove a particle's skin/backstop constraint.
int getDistanceConstraintCount() const
Return the current distance-constraint count.
void detachParticle(int index)
Detach a particle; its simulated velocity remains continuous.
float getShearCompliance() const
Return XPBD shear compliance in m/N.
float getCurrentVolume() const
Return current signed mesh volume, or zero for an open model.
bool hasSkinConstraint(int index) const
Return whether a particle has a skin/backstop constraint.
float getTetherCompliance() const
Return XPBD tether compliance in m/N.
void updateAttachment(int index, float x, float y, float z)
Move an existing attachment target without recreating it.
void setVolumeCompliance(float compliance)
Set XPBD closed-volume compliance in m^3/N.
float getStretchCompliance() const
Return XPBD stretch compliance in m/N.
float getCollisionRestitution() const
Return cloth-side restitution.
int getSkinConstraintCount() const
Return active skin-constraint count.
int getTriangleCount() const
Return the current render/collision triangle count, reduced by tearing.
void setTetherCompliance(float compliance)
Set XPBD tether compliance in m/N.
void attachParticle(int index, float x, float y, float z, float compliance=0.f)
Attach a particle to a world-space target using an XPBD point constraint.
void setSkinConstraint(int index, float x, float y, float z, float nx, float ny, float nz, float radius, float backstopDistance=-1.f, float backstopRadius=0.f, float compliance=0.f)
Constrain a particle around an animated skin reference and optional backstop sphere.
float getCollisionFriction() const
Return cloth-side Coulomb friction.
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36
double dot(const Vec2 &a, const Vec2 &b)
Dot.
Definition UrbanTypes.h:38
double cross(const Vec2 &a, const Vec2 &b)
Cross.
Definition UrbanTypes.h:36