Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/jolt_physics/Jolt/Physics/SoftBody/SoftBodySharedSettings.cpp
21345 views
1
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2
// SPDX-FileCopyrightText: 2023 Jorrit Rouwe
3
// SPDX-License-Identifier: MIT
4
5
#include <Jolt/Jolt.h>
6
7
#include <Jolt/Physics/SoftBody/SoftBodySharedSettings.h>
8
#include <Jolt/Physics/SoftBody/SoftBodyUpdateContext.h>
9
#include <Jolt/ObjectStream/TypeDeclarations.h>
10
#include <Jolt/Core/StreamIn.h>
11
#include <Jolt/Core/StreamOut.h>
12
#include <Jolt/Core/QuickSort.h>
13
#include <Jolt/Core/UnorderedMap.h>
14
#include <Jolt/Core/UnorderedSet.h>
15
#include <Jolt/Core/BinaryHeap.h>
16
17
JPH_NAMESPACE_BEGIN
18
19
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Vertex)
20
{
21
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mPosition)
22
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mVelocity)
23
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mInvMass)
24
}
25
26
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Face)
27
{
28
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mVertex)
29
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mMaterialIndex)
30
}
31
32
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Edge)
33
{
34
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mVertex)
35
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mRestLength)
36
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mCompliance)
37
}
38
39
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::RodStretchShear)
40
{
41
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodStretchShear, mVertex)
42
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodStretchShear, mLength)
43
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodStretchShear, mInvMass)
44
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodStretchShear, mCompliance)
45
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodStretchShear, mBishop)
46
}
47
48
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::RodBendTwist)
49
{
50
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodBendTwist, mRod)
51
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodBendTwist, mCompliance)
52
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::RodBendTwist, mOmega0)
53
}
54
55
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::DihedralBend)
56
{
57
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mVertex)
58
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mCompliance)
59
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mInitialAngle)
60
}
61
62
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Volume)
63
{
64
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mVertex)
65
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mSixRestVolume)
66
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mCompliance)
67
}
68
69
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind)
70
{
71
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mJointIndex)
72
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mInvBind)
73
}
74
75
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::SkinWeight)
76
{
77
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mInvBindIndex)
78
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mWeight)
79
}
80
81
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Skinned)
82
{
83
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mVertex)
84
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mWeights)
85
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mMaxDistance)
86
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopDistance)
87
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopRadius)
88
}
89
90
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::LRA)
91
{
92
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mVertex)
93
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mMaxDistance)
94
}
95
96
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings)
97
{
98
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertices)
99
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mFaces)
100
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeConstraints)
101
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mDihedralBendConstraints)
102
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVolumeConstraints)
103
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mSkinnedConstraints)
104
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mInvBindMatrices)
105
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mLRAConstraints)
106
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mRodStretchShearConstraints)
107
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mRodBendTwistConstraints)
108
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mMaterials)
109
}
110
111
void SoftBodySharedSettings::CalculateClosestKinematic()
112
{
113
// Check if we already calculated this
114
if (!mClosestKinematic.empty())
115
return;
116
117
// Reserve output size
118
mClosestKinematic.resize(mVertices.size());
119
120
// Create a list of connected vertices
121
Array<Array<uint32>> connectivity;
122
connectivity.resize(mVertices.size());
123
for (const Edge &e : mEdgeConstraints)
124
{
125
connectivity[e.mVertex[0]].push_back(e.mVertex[1]);
126
connectivity[e.mVertex[1]].push_back(e.mVertex[0]);
127
}
128
for (const RodStretchShear &r : mRodStretchShearConstraints)
129
{
130
connectivity[r.mVertex[0]].push_back(r.mVertex[1]);
131
connectivity[r.mVertex[1]].push_back(r.mVertex[0]);
132
}
133
134
// Use Dijkstra's algorithm to find the closest kinematic vertex for each vertex
135
// See: https://en.wikipedia.org/wiki/Dijkstra's_algorithm
136
//
137
// An element in the open list
138
struct Open
139
{
140
// Order so that we get the shortest distance first
141
bool operator < (const Open &inRHS) const
142
{
143
return mDistance > inRHS.mDistance;
144
}
145
146
uint32 mVertex;
147
float mDistance;
148
};
149
150
// Start with all kinematic elements
151
Array<Open> to_visit;
152
for (uint32 v = 0; v < mVertices.size(); ++v)
153
if (mVertices[v].mInvMass == 0.0f)
154
{
155
mClosestKinematic[v].mVertex = v;
156
mClosestKinematic[v].mHops = 0;
157
mClosestKinematic[v].mDistance = 0.0f;
158
to_visit.push_back({ v, 0.0f });
159
BinaryHeapPush(to_visit.begin(), to_visit.end(), std::less<Open> { });
160
}
161
162
// Visit all vertices remembering the closest kinematic vertex and its distance
163
JPH_IF_ENABLE_ASSERTS(float last_closest = 0.0f;)
164
while (!to_visit.empty())
165
{
166
// Pop element from the open list
167
BinaryHeapPop(to_visit.begin(), to_visit.end(), std::less<Open> { });
168
Open current = to_visit.back();
169
to_visit.pop_back();
170
JPH_ASSERT(current.mDistance >= last_closest);
171
JPH_IF_ENABLE_ASSERTS(last_closest = current.mDistance;)
172
173
// Loop through all of its connected vertices
174
for (uint32 v : connectivity[current.mVertex])
175
{
176
// Calculate distance from the current vertex to this target vertex and check if it is smaller
177
float new_distance = current.mDistance + (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current.mVertex].mPosition)).Length();
178
if (new_distance < mClosestKinematic[v].mDistance)
179
{
180
// Remember new closest vertex
181
mClosestKinematic[v].mVertex = mClosestKinematic[current.mVertex].mVertex;
182
mClosestKinematic[v].mHops = mClosestKinematic[current.mVertex].mHops + 1;
183
mClosestKinematic[v].mDistance = new_distance;
184
to_visit.push_back({ v, new_distance });
185
BinaryHeapPush(to_visit.begin(), to_visit.end(), std::less<Open> { });
186
}
187
}
188
}
189
}
190
191
void SoftBodySharedSettings::CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType, float inAngleTolerance)
192
{
193
struct EdgeHelper
194
{
195
uint32 mVertex[2];
196
uint32 mEdgeIdx;
197
};
198
199
// Create list of all edges
200
Array<EdgeHelper> edges;
201
edges.reserve(mFaces.size() * 3);
202
for (const Face &f : mFaces)
203
for (int i = 0; i < 3; ++i)
204
{
205
uint32 v0 = f.mVertex[i];
206
uint32 v1 = f.mVertex[(i + 1) % 3];
207
208
EdgeHelper e;
209
e.mVertex[0] = min(v0, v1);
210
e.mVertex[1] = max(v0, v1);
211
e.mEdgeIdx = uint32(&f - mFaces.data()) * 3 + i;
212
edges.push_back(e);
213
}
214
215
// Sort the edges
216
QuickSort(edges.begin(), edges.end(), [](const EdgeHelper &inLHS, const EdgeHelper &inRHS) { return inLHS.mVertex[0] < inRHS.mVertex[0] || (inLHS.mVertex[0] == inRHS.mVertex[0] && inLHS.mVertex[1] < inRHS.mVertex[1]); });
217
218
// Only add edges if one of the vertices is movable
219
auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inCompliance1, float inCompliance2) {
220
if ((mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f)
221
&& inCompliance1 < FLT_MAX && inCompliance2 < FLT_MAX)
222
{
223
Edge temp_edge;
224
temp_edge.mVertex[0] = inVtx1;
225
temp_edge.mVertex[1] = inVtx2;
226
temp_edge.mCompliance = 0.5f * (inCompliance1 + inCompliance2);
227
temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length();
228
JPH_ASSERT(temp_edge.mRestLength > 0.0f);
229
mEdgeConstraints.push_back(temp_edge);
230
}
231
};
232
233
// Helper function to get the attributes of a vertex
234
auto attr = [inVertexAttributes, inVertexAttributesLength](uint32 inVertex) {
235
return inVertexAttributes[min(inVertex, inVertexAttributesLength - 1)];
236
};
237
238
// Create the constraints
239
float sq_sin_tolerance = Square(Sin(inAngleTolerance));
240
float sq_cos_tolerance = Square(Cos(inAngleTolerance));
241
mEdgeConstraints.clear();
242
mEdgeConstraints.reserve(edges.size());
243
for (Array<EdgeHelper>::size_type i = 0; i < edges.size(); ++i)
244
{
245
const EdgeHelper &e0 = edges[i];
246
247
// Get attributes for the vertices of the edge
248
const VertexAttributes &a0 = attr(e0.mVertex[0]);
249
const VertexAttributes &a1 = attr(e0.mVertex[1]);
250
251
// Flag that indicates if this edge is a shear edge (if 2 triangles form a quad-like shape and this edge is on the diagonal)
252
bool is_shear = false;
253
254
// Test if there are any shared edges
255
for (Array<EdgeHelper>::size_type j = i + 1; j < edges.size(); ++j)
256
{
257
const EdgeHelper &e1 = edges[j];
258
if (e0.mVertex[0] == e1.mVertex[0] && e0.mVertex[1] == e1.mVertex[1])
259
{
260
// Get opposing vertices
261
const Face &f0 = mFaces[e0.mEdgeIdx / 3];
262
const Face &f1 = mFaces[e1.mEdgeIdx / 3];
263
uint32 vopposite0 = f0.mVertex[(e0.mEdgeIdx + 2) % 3];
264
uint32 vopposite1 = f1.mVertex[(e1.mEdgeIdx + 2) % 3];
265
const VertexAttributes &a_opposite0 = attr(vopposite0);
266
const VertexAttributes &a_opposite1 = attr(vopposite1);
267
268
// If the opposite vertices happen to be the same vertex then we have 2 triangles back to back and we skip creating shear / bend constraints
269
if (vopposite0 == vopposite1)
270
continue;
271
272
// Faces should be roughly in a plane
273
Vec3 n0 = (Vec3(mVertices[f0.mVertex[2]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f0.mVertex[1]].mPosition) - Vec3(mVertices[f0.mVertex[0]].mPosition));
274
Vec3 n1 = (Vec3(mVertices[f1.mVertex[2]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition)).Cross(Vec3(mVertices[f1.mVertex[1]].mPosition) - Vec3(mVertices[f1.mVertex[0]].mPosition));
275
float n0_dot_n1 = n0.Dot(n1);
276
if (n0_dot_n1 > 0.0f
277
&& Square(n0_dot_n1) > sq_cos_tolerance * n0.LengthSq() * n1.LengthSq())
278
{
279
// Faces should approximately form a quad
280
Vec3 e0_dir = Vec3(mVertices[vopposite0].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
281
Vec3 e1_dir = Vec3(mVertices[vopposite1].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
282
if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq())
283
{
284
// Shear constraint
285
add_edge(vopposite0, vopposite1, a_opposite0.mShearCompliance, a_opposite1.mShearCompliance);
286
is_shear = true;
287
}
288
}
289
290
// Bend constraint
291
switch (inBendType)
292
{
293
case EBendType::None:
294
// Do nothing
295
break;
296
297
case EBendType::Distance:
298
// Create an edge constraint to represent the bend constraint
299
// Use the bend compliance of the shared edge
300
if (!is_shear)
301
add_edge(vopposite0, vopposite1, a0.mBendCompliance, a1.mBendCompliance);
302
break;
303
304
case EBendType::Dihedral:
305
// Test if both opposite vertices are free to move
306
if ((mVertices[vopposite0].mInvMass > 0.0f || mVertices[vopposite1].mInvMass > 0.0f)
307
&& a0.mBendCompliance < FLT_MAX && a1.mBendCompliance < FLT_MAX)
308
{
309
// Create a bend constraint
310
// Use the bend compliance of the shared edge
311
mDihedralBendConstraints.emplace_back(e0.mVertex[0], e0.mVertex[1], vopposite0, vopposite1, 0.5f * (a0.mBendCompliance + a1.mBendCompliance));
312
}
313
break;
314
}
315
}
316
else
317
{
318
// Start iterating from the first non-shared edge
319
i = j - 1;
320
break;
321
}
322
}
323
324
// Create a edge constraint for the current edge
325
add_edge(e0.mVertex[0], e0.mVertex[1], is_shear? a0.mShearCompliance : a0.mCompliance, is_shear? a1.mShearCompliance : a1.mCompliance);
326
}
327
mEdgeConstraints.shrink_to_fit();
328
329
// Calculate the initial angle for all bend constraints
330
CalculateBendConstraintConstants();
331
332
// Check if any vertices have LRA constraints
333
bool has_lra_constraints = false;
334
for (const VertexAttributes *va = inVertexAttributes; va < inVertexAttributes + inVertexAttributesLength; ++va)
335
if (va->mLRAType != ELRAType::None)
336
{
337
has_lra_constraints = true;
338
break;
339
}
340
if (has_lra_constraints)
341
{
342
// Ensure we have calculated the closest kinematic vertex for each vertex
343
CalculateClosestKinematic();
344
345
// Find non-kinematic vertices
346
for (uint32 v = 0; v < (uint32)mVertices.size(); ++v)
347
if (mVertices[v].mInvMass > 0.0f)
348
{
349
// Check if a closest vertex was found
350
uint32 closest = mClosestKinematic[v].mVertex;
351
if (closest != 0xffffffff)
352
{
353
// Check which LRA constraint to create
354
const VertexAttributes &va = attr(v);
355
switch (va.mLRAType)
356
{
357
case ELRAType::None:
358
break;
359
360
case ELRAType::EuclideanDistance:
361
mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * (Vec3(mVertices[closest].mPosition) - Vec3(mVertices[v].mPosition)).Length());
362
break;
363
364
case ELRAType::GeodesicDistance:
365
mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * mClosestKinematic[v].mDistance);
366
break;
367
}
368
}
369
}
370
}
371
}
372
373
void SoftBodySharedSettings::CalculateEdgeLengths()
374
{
375
for (Edge &e : mEdgeConstraints)
376
{
377
JPH_ASSERT(e.mVertex[0] != e.mVertex[1], "Edges need to connect 2 different vertices");
378
e.mRestLength = (Vec3(mVertices[e.mVertex[1]].mPosition) - Vec3(mVertices[e.mVertex[0]].mPosition)).Length();
379
JPH_ASSERT(e.mRestLength > 0.0f);
380
}
381
}
382
383
void SoftBodySharedSettings::CalculateRodProperties()
384
{
385
// Mark connections through bend twist constraints
386
Array<Array<uint32>> connections;
387
connections.resize(mRodStretchShearConstraints.size());
388
for (const RodBendTwist &c : mRodBendTwistConstraints)
389
{
390
JPH_ASSERT(c.mRod[0] != c.mRod[1], "A bend twist constraint needs to be attached to different rods");
391
connections[c.mRod[1]].push_back(c.mRod[0]);
392
connections[c.mRod[0]].push_back(c.mRod[1]);
393
}
394
395
// Now calculate the Bishop frames for all rods
396
struct Entry
397
{
398
uint32 mFrom; // Rod we're coming from
399
uint32 mTo; // Rod we're going to
400
};
401
Array<Entry> stack;
402
stack.reserve(mRodStretchShearConstraints.size());
403
for (uint32 r0_idx = 0; r0_idx < mRodStretchShearConstraints.size(); ++r0_idx)
404
{
405
RodStretchShear &r0 = mRodStretchShearConstraints[r0_idx];
406
407
// Do not calculate a 2nd time
408
if (r0.mBishop == Quat::sZero())
409
{
410
// Calculate the frame for this rod
411
{
412
Vec3 tangent = Vec3(mVertices[r0.mVertex[1]].mPosition) - Vec3(mVertices[r0.mVertex[0]].mPosition);
413
r0.mLength = tangent.Length();
414
JPH_ASSERT(r0.mLength > 0.0f, "Rods of zero length are not supported!");
415
tangent /= r0.mLength;
416
Vec3 normal = tangent.GetNormalizedPerpendicular();
417
Vec3 binormal = tangent.Cross(normal);
418
r0.mBishop = Mat44(Vec4(normal, 0), Vec4(binormal, 0), Vec4(tangent, 0), Vec4(0, 0, 0, 1)).GetQuaternion().Normalized();
419
}
420
421
// Add connected rods to the stack if they haven't been calculated yet
422
for (uint32 r1_idx : connections[r0_idx])
423
if (mRodStretchShearConstraints[r1_idx].mBishop == Quat::sZero())
424
stack.push_back({ r0_idx, r1_idx });
425
426
// Now connect the bishop frame for all connected rods on the stack
427
// This follows the procedure outlined in "Discrete Elastic Rods" - M. Bergou et al.
428
// See: https://www.cs.columbia.edu/cg/pdfs/143-rods.pdf
429
while (!stack.empty())
430
{
431
uint32 r1_idx = stack.back().mFrom;
432
uint32 r2_idx = stack.back().mTo;
433
stack.pop_back();
434
435
const RodStretchShear &r1 = mRodStretchShearConstraints[r1_idx];
436
RodStretchShear &r2 = mRodStretchShearConstraints[r2_idx];
437
438
// Get the normal and tangent of the first rod's Bishop frame (that was already calculated)
439
Mat44 r1_frame = Mat44::sRotation(r1.mBishop);
440
Vec3 tangent1 = r1_frame.GetAxisZ();
441
Vec3 normal1 = r1_frame.GetAxisX();
442
443
// Calculate the Bishop frame for the 2nd rod
444
Vec3 tangent2 = Vec3(mVertices[r2.mVertex[1]].mPosition) - Vec3(mVertices[r2.mVertex[0]].mPosition);
445
if (tangent1.Dot(tangent2) < 0.0f)
446
{
447
// Edge is oriented in the opposite direction of the previous edge, flip it
448
std::swap(r2.mVertex[0], r2.mVertex[1]);
449
tangent2 = -tangent2;
450
}
451
r2.mLength = tangent2.Length();
452
JPH_ASSERT(r2.mLength > 0.0f, "Rods of zero length are not supported!");
453
tangent2 /= r2.mLength;
454
Vec3 t1_cross_t2 = tangent1.Cross(tangent2);
455
float sin_angle = t1_cross_t2.Length();
456
Vec3 normal2 = normal1;
457
if (sin_angle > 1.0e-6f)
458
{
459
t1_cross_t2 /= sin_angle;
460
normal2 = Quat::sRotation(t1_cross_t2, ASin(sin_angle)) * normal2;
461
}
462
Vec3 binormal2 = tangent2.Cross(normal2);
463
r2.mBishop = Mat44(Vec4(normal2, 0), Vec4(binormal2, 0), Vec4(tangent2, 0), Vec4(0, 0, 0, 1)).GetQuaternion().Normalized();
464
465
// Add connected rods to the stack if they haven't been calculated yet
466
for (uint32 r3_idx : connections[r2_idx])
467
if (mRodStretchShearConstraints[r3_idx].mBishop == Quat::sZero())
468
stack.push_back({ r2_idx, r3_idx });
469
}
470
}
471
}
472
473
// Calculate inverse mass for all rods by taking the minimum inverse mass (aka the heaviest vertex) of both vertices
474
for (RodStretchShear &r : mRodStretchShearConstraints)
475
{
476
JPH_ASSERT(r.mVertex[0] != r.mVertex[1], "A rod stretch shear constraint requires two different vertices");
477
r.mInvMass = min(mVertices[r.mVertex[0]].mInvMass, mVertices[r.mVertex[1]].mInvMass);
478
}
479
480
// Calculate the initial rotation between the rods
481
for (RodBendTwist &r : mRodBendTwistConstraints)
482
r.mOmega0 = (mRodStretchShearConstraints[r.mRod[0]].mBishop.Conjugated() * mRodStretchShearConstraints[r.mRod[1]].mBishop).Normalized();
483
}
484
485
void SoftBodySharedSettings::CalculateLRALengths(float inMaxDistanceMultiplier)
486
{
487
for (LRA &l : mLRAConstraints)
488
{
489
JPH_ASSERT(l.mVertex[0] != l.mVertex[1], "LRA constraints need to connect 2 different vertices");
490
l.mMaxDistance = inMaxDistanceMultiplier * (Vec3(mVertices[l.mVertex[1]].mPosition) - Vec3(mVertices[l.mVertex[0]].mPosition)).Length();
491
JPH_ASSERT(l.mMaxDistance > 0.0f);
492
}
493
}
494
495
void SoftBodySharedSettings::CalculateBendConstraintConstants()
496
{
497
for (DihedralBend &b : mDihedralBendConstraints)
498
{
499
JPH_ASSERT(b.mVertex[0] != b.mVertex[1] && b.mVertex[0] != b.mVertex[2] && b.mVertex[0] != b.mVertex[3]
500
&& b.mVertex[1] != b.mVertex[2] && b.mVertex[1] != b.mVertex[3]
501
&& b.mVertex[2] != b.mVertex[3], "Bend constraints need 4 different vertices");
502
503
// Get positions
504
Vec3 x0 = Vec3(mVertices[b.mVertex[0]].mPosition);
505
Vec3 x1 = Vec3(mVertices[b.mVertex[1]].mPosition);
506
Vec3 x2 = Vec3(mVertices[b.mVertex[2]].mPosition);
507
Vec3 x3 = Vec3(mVertices[b.mVertex[3]].mPosition);
508
509
/*
510
x2
511
e1/ \e3
512
/ \
513
x0----x1
514
\ e0 /
515
e2\ /e4
516
x3
517
*/
518
519
// Calculate edges
520
Vec3 e0 = x1 - x0;
521
Vec3 e1 = x2 - x0;
522
Vec3 e2 = x3 - x0;
523
524
// Normals of both triangles
525
Vec3 n1 = e0.Cross(e1);
526
Vec3 n2 = e2.Cross(e0);
527
float denom = sqrt(n1.LengthSq() * n2.LengthSq());
528
if (denom < 1.0e-12f)
529
b.mInitialAngle = 0.0f;
530
else
531
{
532
float sign = Sign(n2.Cross(n1).Dot(e0));
533
b.mInitialAngle = sign * ACosApproximate(n1.Dot(n2) / denom); // Runtime uses the approximation too
534
}
535
}
536
}
537
538
void SoftBodySharedSettings::CalculateVolumeConstraintVolumes()
539
{
540
for (Volume &v : mVolumeConstraints)
541
{
542
JPH_ASSERT(v.mVertex[0] != v.mVertex[1] && v.mVertex[0] != v.mVertex[2] && v.mVertex[0] != v.mVertex[3]
543
&& v.mVertex[1] != v.mVertex[2] && v.mVertex[1] != v.mVertex[3]
544
&& v.mVertex[2] != v.mVertex[3], "Volume constraints need 4 different vertices");
545
546
Vec3 x1(mVertices[v.mVertex[0]].mPosition);
547
Vec3 x2(mVertices[v.mVertex[1]].mPosition);
548
Vec3 x3(mVertices[v.mVertex[2]].mPosition);
549
Vec3 x4(mVertices[v.mVertex[3]].mPosition);
550
551
Vec3 x1x2 = x2 - x1;
552
Vec3 x1x3 = x3 - x1;
553
Vec3 x1x4 = x4 - x1;
554
555
v.mSixRestVolume = abs(x1x2.Cross(x1x3).Dot(x1x4));
556
}
557
}
558
559
void SoftBodySharedSettings::CalculateSkinnedConstraintNormals()
560
{
561
// Clear any previous results
562
mSkinnedConstraintNormals.clear();
563
564
// If there are no skinned constraints, we're done
565
if (mSkinnedConstraints.empty())
566
return;
567
568
// First collect all vertices that are skinned
569
using VertexIndexSet = UnorderedSet<uint32>;
570
VertexIndexSet skinned_vertices;
571
skinned_vertices.reserve(VertexIndexSet::size_type(mSkinnedConstraints.size()));
572
for (const Skinned &s : mSkinnedConstraints)
573
skinned_vertices.insert(s.mVertex);
574
575
// Now collect all faces that connect only to skinned vertices
576
using ConnectedFacesMap = UnorderedMap<uint32, VertexIndexSet>;
577
ConnectedFacesMap connected_faces;
578
connected_faces.reserve(ConnectedFacesMap::size_type(mVertices.size()));
579
for (const Face &f : mFaces)
580
{
581
// Must connect to only skinned vertices
582
bool valid = true;
583
for (uint32 v : f.mVertex)
584
valid &= skinned_vertices.find(v) != skinned_vertices.end();
585
if (!valid)
586
continue;
587
588
// Store faces that connect to vertices
589
for (uint32 v : f.mVertex)
590
connected_faces[v].insert(uint32(&f - mFaces.data()));
591
}
592
593
// Populate the list of connecting faces per skinned vertex
594
mSkinnedConstraintNormals.reserve(mFaces.size());
595
for (Skinned &s : mSkinnedConstraints)
596
{
597
uint32 start = uint32(mSkinnedConstraintNormals.size());
598
JPH_ASSERT((start >> 24) == 0);
599
ConnectedFacesMap::const_iterator connected_faces_it = connected_faces.find(s.mVertex);
600
if (connected_faces_it != connected_faces.cend())
601
{
602
const VertexIndexSet &faces = connected_faces_it->second;
603
uint32 num = uint32(faces.size());
604
JPH_ASSERT(num < 256);
605
mSkinnedConstraintNormals.insert(mSkinnedConstraintNormals.end(), faces.begin(), faces.end());
606
QuickSort(mSkinnedConstraintNormals.begin() + start, mSkinnedConstraintNormals.begin() + start + num);
607
s.mNormalInfo = start + (num << 24);
608
}
609
else
610
s.mNormalInfo = 0;
611
}
612
mSkinnedConstraintNormals.shrink_to_fit();
613
}
614
615
void SoftBodySharedSettings::Optimize(OptimizationResults &outResults)
616
{
617
// Clear any previous results
618
mUpdateGroups.clear();
619
620
// Create a list of connected vertices
621
struct Connection
622
{
623
uint32 mVertex;
624
uint32 mCount;
625
};
626
Array<Array<Connection>> connectivity;
627
connectivity.resize(mVertices.size());
628
auto add_connection = [&connectivity](uint inV1, uint inV2) {
629
for (int i = 0; i < 2; ++i)
630
{
631
bool found = false;
632
for (Connection &c : connectivity[inV1])
633
if (c.mVertex == inV2)
634
{
635
c.mCount++;
636
found = true;
637
break;
638
}
639
if (!found)
640
connectivity[inV1].push_back({ inV2, 1 });
641
642
std::swap(inV1, inV2);
643
}
644
};
645
for (const Edge &c : mEdgeConstraints)
646
add_connection(c.mVertex[0], c.mVertex[1]);
647
for (const LRA &c : mLRAConstraints)
648
add_connection(c.mVertex[0], c.mVertex[1]);
649
for (const RodStretchShear &c : mRodStretchShearConstraints)
650
add_connection(c.mVertex[0], c.mVertex[1]);
651
for (const RodBendTwist &c : mRodBendTwistConstraints)
652
{
653
add_connection(mRodStretchShearConstraints[c.mRod[0]].mVertex[0], mRodStretchShearConstraints[c.mRod[1]].mVertex[0]);
654
add_connection(mRodStretchShearConstraints[c.mRod[0]].mVertex[1], mRodStretchShearConstraints[c.mRod[1]].mVertex[0]);
655
add_connection(mRodStretchShearConstraints[c.mRod[0]].mVertex[0], mRodStretchShearConstraints[c.mRod[1]].mVertex[1]);
656
add_connection(mRodStretchShearConstraints[c.mRod[0]].mVertex[1], mRodStretchShearConstraints[c.mRod[1]].mVertex[1]);
657
}
658
for (const DihedralBend &c : mDihedralBendConstraints)
659
{
660
add_connection(c.mVertex[0], c.mVertex[1]);
661
add_connection(c.mVertex[0], c.mVertex[2]);
662
add_connection(c.mVertex[0], c.mVertex[3]);
663
add_connection(c.mVertex[1], c.mVertex[2]);
664
add_connection(c.mVertex[1], c.mVertex[3]);
665
add_connection(c.mVertex[2], c.mVertex[3]);
666
}
667
for (const Volume &c : mVolumeConstraints)
668
{
669
add_connection(c.mVertex[0], c.mVertex[1]);
670
add_connection(c.mVertex[0], c.mVertex[2]);
671
add_connection(c.mVertex[0], c.mVertex[3]);
672
add_connection(c.mVertex[1], c.mVertex[2]);
673
add_connection(c.mVertex[1], c.mVertex[3]);
674
add_connection(c.mVertex[2], c.mVertex[3]);
675
}
676
// Skinned constraints only update 1 vertex, so we don't need special logic here
677
678
// Maps each of the vertices to a group index
679
Array<int> group_idx;
680
group_idx.resize(mVertices.size(), -1);
681
682
// Which group we are currently filling and its vertices
683
int current_group_idx = 0;
684
Array<uint> current_group;
685
686
// Start greedy algorithm to group vertices
687
for (;;)
688
{
689
// Find the bounding box of the ungrouped vertices
690
AABox bounds;
691
for (uint i = 0; i < (uint)mVertices.size(); ++i)
692
if (group_idx[i] == -1)
693
bounds.Encapsulate(Vec3(mVertices[i].mPosition));
694
695
// If the bounds are invalid, it means that there were no ungrouped vertices
696
if (!bounds.IsValid())
697
break;
698
699
// Determine longest and shortest axis
700
Vec3 bounds_size = bounds.GetSize();
701
uint max_axis = bounds_size.GetHighestComponentIndex();
702
uint min_axis = bounds_size.GetLowestComponentIndex();
703
if (min_axis == max_axis)
704
min_axis = (min_axis + 1) % 3;
705
uint mid_axis = 3 - min_axis - max_axis;
706
707
// Find the vertex that has the lowest value on the axis with the largest extent
708
uint current_vertex = UINT_MAX;
709
Float3 current_vertex_position { FLT_MAX, FLT_MAX, FLT_MAX };
710
for (uint i = 0; i < (uint)mVertices.size(); ++i)
711
if (group_idx[i] == -1)
712
{
713
const Float3 &vertex_position = mVertices[i].mPosition;
714
float max_axis_value = vertex_position[max_axis];
715
float mid_axis_value = vertex_position[mid_axis];
716
float min_axis_value = vertex_position[min_axis];
717
718
if (max_axis_value < current_vertex_position[max_axis]
719
|| (max_axis_value == current_vertex_position[max_axis]
720
&& (mid_axis_value < current_vertex_position[mid_axis]
721
|| (mid_axis_value == current_vertex_position[mid_axis]
722
&& min_axis_value < current_vertex_position[min_axis]))))
723
{
724
current_vertex_position = mVertices[i].mPosition;
725
current_vertex = i;
726
}
727
}
728
if (current_vertex == UINT_MAX)
729
break;
730
731
// Initialize the current group with 1 vertex
732
current_group.push_back(current_vertex);
733
group_idx[current_vertex] = current_group_idx;
734
735
// Fill up the group
736
for (;;)
737
{
738
// Find the vertex that is most connected to the current group
739
uint best_vertex = UINT_MAX;
740
uint best_num_connections = 0;
741
float best_dist_sq = FLT_MAX;
742
for (uint i = 0; i < (uint)current_group.size(); ++i) // For all vertices in the current group
743
for (const Connection &c : connectivity[current_group[i]]) // For all connections to other vertices
744
{
745
uint v = c.mVertex;
746
if (group_idx[v] == -1) // Ungrouped vertices only
747
{
748
// Count the number of connections to this group
749
uint num_connections = 0;
750
for (const Connection &v2 : connectivity[v])
751
if (group_idx[v2.mVertex] == current_group_idx)
752
num_connections += v2.mCount;
753
754
// Calculate distance to group centroid
755
float dist_sq = (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current_group.front()].mPosition)).LengthSq();
756
757
if (best_vertex == UINT_MAX
758
|| num_connections > best_num_connections
759
|| (num_connections == best_num_connections && dist_sq < best_dist_sq))
760
{
761
best_vertex = v;
762
best_num_connections = num_connections;
763
best_dist_sq = dist_sq;
764
}
765
}
766
}
767
768
// Add the best vertex to the current group
769
if (best_vertex != UINT_MAX)
770
{
771
current_group.push_back(best_vertex);
772
group_idx[best_vertex] = current_group_idx;
773
}
774
775
// Create a new group?
776
if (current_group.size() >= SoftBodyUpdateContext::cVertexConstraintBatch // If full, yes
777
|| (current_group.size() > SoftBodyUpdateContext::cVertexConstraintBatch / 2 && best_vertex == UINT_MAX)) // If half full and we found no connected vertex, yes
778
{
779
current_group.clear();
780
current_group_idx++;
781
break;
782
}
783
784
// If we didn't find a connected vertex, we need to find a new starting vertex
785
if (best_vertex == UINT_MAX)
786
break;
787
}
788
}
789
790
// If the last group is more than half full, we'll keep it as a separate group, otherwise we merge it with the 'non parallel' group
791
if (current_group.size() > SoftBodyUpdateContext::cVertexConstraintBatch / 2)
792
++current_group_idx;
793
794
// We no longer need the current group array, free the memory
795
current_group.clear();
796
current_group.shrink_to_fit();
797
798
// We're done with the connectivity list, free the memory
799
connectivity.clear();
800
connectivity.shrink_to_fit();
801
802
// Assign the constraints to their groups
803
struct Group
804
{
805
uint GetSize() const
806
{
807
return (uint)mEdgeConstraints.size() + (uint)mLRAConstraints.size() + (uint)mRodStretchShearConstraints.size() + (uint)mRodBendTwistConstraints.size() + (uint)mDihedralBendConstraints.size() + (uint)mVolumeConstraints.size() + (uint)mSkinnedConstraints.size();
808
}
809
810
Array<uint> mEdgeConstraints;
811
Array<uint> mLRAConstraints;
812
Array<uint> mRodStretchShearConstraints;
813
Array<uint> mRodBendTwistConstraints;
814
Array<uint> mDihedralBendConstraints;
815
Array<uint> mVolumeConstraints;
816
Array<uint> mSkinnedConstraints;
817
};
818
Array<Group> groups;
819
groups.resize(current_group_idx + 1); // + non parallel group
820
for (const Edge &e : mEdgeConstraints)
821
{
822
int g1 = group_idx[e.mVertex[0]];
823
int g2 = group_idx[e.mVertex[1]];
824
JPH_ASSERT(g1 >= 0 && g2 >= 0);
825
if (g1 == g2) // In the same group
826
groups[g1].mEdgeConstraints.push_back(uint(&e - mEdgeConstraints.data()));
827
else // In different groups -> parallel group
828
groups.back().mEdgeConstraints.push_back(uint(&e - mEdgeConstraints.data()));
829
}
830
for (const LRA &l : mLRAConstraints)
831
{
832
int g1 = group_idx[l.mVertex[0]];
833
int g2 = group_idx[l.mVertex[1]];
834
JPH_ASSERT(g1 >= 0 && g2 >= 0);
835
if (g1 == g2) // In the same group
836
groups[g1].mLRAConstraints.push_back(uint(&l - mLRAConstraints.data()));
837
else // In different groups -> parallel group
838
groups.back().mLRAConstraints.push_back(uint(&l - mLRAConstraints.data()));
839
}
840
for (const RodStretchShear &r : mRodStretchShearConstraints)
841
{
842
int g1 = group_idx[r.mVertex[0]];
843
int g2 = group_idx[r.mVertex[1]];
844
JPH_ASSERT(g1 >= 0 && g2 >= 0);
845
if (g1 == g2) // In the same group
846
groups[g1].mRodStretchShearConstraints.push_back(uint(&r - mRodStretchShearConstraints.data()));
847
else // In different groups -> parallel group
848
groups.back().mRodStretchShearConstraints.push_back(uint(&r - mRodStretchShearConstraints.data()));
849
}
850
for (const RodBendTwist &r : mRodBendTwistConstraints)
851
{
852
int g1 = group_idx[mRodStretchShearConstraints[r.mRod[0]].mVertex[0]];
853
int g2 = group_idx[mRodStretchShearConstraints[r.mRod[0]].mVertex[1]];
854
int g3 = group_idx[mRodStretchShearConstraints[r.mRod[1]].mVertex[0]];
855
int g4 = group_idx[mRodStretchShearConstraints[r.mRod[1]].mVertex[1]];
856
JPH_ASSERT(g1 >= 0 && g2 >= 0 && g3 >= 0 && g4 >= 0);
857
if (g1 == g2 && g1 == g3 && g1 == g4) // In the same group
858
groups[g1].mRodBendTwistConstraints.push_back(uint(&r - mRodBendTwistConstraints.data()));
859
else // In different groups -> parallel group
860
groups.back().mRodBendTwistConstraints.push_back(uint(&r - mRodBendTwistConstraints.data()));
861
}
862
for (const DihedralBend &d : mDihedralBendConstraints)
863
{
864
int g1 = group_idx[d.mVertex[0]];
865
int g2 = group_idx[d.mVertex[1]];
866
int g3 = group_idx[d.mVertex[2]];
867
int g4 = group_idx[d.mVertex[3]];
868
JPH_ASSERT(g1 >= 0 && g2 >= 0 && g3 >= 0 && g4 >= 0);
869
if (g1 == g2 && g1 == g3 && g1 == g4) // In the same group
870
groups[g1].mDihedralBendConstraints.push_back(uint(&d - mDihedralBendConstraints.data()));
871
else // In different groups -> parallel group
872
groups.back().mDihedralBendConstraints.push_back(uint(&d - mDihedralBendConstraints.data()));
873
}
874
for (const Volume &v : mVolumeConstraints)
875
{
876
int g1 = group_idx[v.mVertex[0]];
877
int g2 = group_idx[v.mVertex[1]];
878
int g3 = group_idx[v.mVertex[2]];
879
int g4 = group_idx[v.mVertex[3]];
880
JPH_ASSERT(g1 >= 0 && g2 >= 0 && g3 >= 0 && g4 >= 0);
881
if (g1 == g2 && g1 == g3 && g1 == g4) // In the same group
882
groups[g1].mVolumeConstraints.push_back(uint(&v - mVolumeConstraints.data()));
883
else // In different groups -> parallel group
884
groups.back().mVolumeConstraints.push_back(uint(&v - mVolumeConstraints.data()));
885
}
886
for (const Skinned &s : mSkinnedConstraints)
887
{
888
int g1 = group_idx[s.mVertex];
889
JPH_ASSERT(g1 >= 0);
890
groups[g1].mSkinnedConstraints.push_back(uint(&s - mSkinnedConstraints.data()));
891
}
892
893
// Sort the parallel groups from big to small (this means the big groups will be scheduled first and have more time to complete)
894
QuickSort(groups.begin(), groups.end() - 1, [](const Group &inLHS, const Group &inRHS) { return inLHS.GetSize() > inRHS.GetSize(); });
895
896
// Make sure we know the closest kinematic vertex so we can sort
897
CalculateClosestKinematic();
898
899
// Sort within each group
900
for (Group &group : groups)
901
{
902
// Sort the edge constraints
903
QuickSort(group.mEdgeConstraints.begin(), group.mEdgeConstraints.end(), [this](uint inLHS, uint inRHS)
904
{
905
const Edge &e1 = mEdgeConstraints[inLHS];
906
const Edge &e2 = mEdgeConstraints[inRHS];
907
908
// First sort so that the edge with the smallest distance to a kinematic vertex comes first
909
float d1 = min(mClosestKinematic[e1.mVertex[0]].mDistance, mClosestKinematic[e1.mVertex[1]].mDistance);
910
float d2 = min(mClosestKinematic[e2.mVertex[0]].mDistance, mClosestKinematic[e2.mVertex[1]].mDistance);
911
if (d1 != d2)
912
return d1 < d2;
913
914
// Order the edges so that the ones with the smallest index go first (hoping to get better cache locality when we process the edges).
915
// Note we could also re-order the vertices but that would be much more of a burden to the end user
916
uint32 m1 = e1.GetMinVertexIndex();
917
uint32 m2 = e2.GetMinVertexIndex();
918
if (m1 != m2)
919
return m1 < m2;
920
921
return inLHS < inRHS;
922
});
923
924
// Sort the LRA constraints
925
QuickSort(group.mLRAConstraints.begin(), group.mLRAConstraints.end(), [this](uint inLHS, uint inRHS)
926
{
927
const LRA &l1 = mLRAConstraints[inLHS];
928
const LRA &l2 = mLRAConstraints[inRHS];
929
930
// First sort so that the longest constraint comes first (meaning the shortest constraint has the most influence on the end result)
931
// Most of the time there will be a single LRA constraint per vertex and since the LRA constraint only modifies a single vertex,
932
// updating one constraint will not violate another constraint.
933
if (l1.mMaxDistance != l2.mMaxDistance)
934
return l1.mMaxDistance > l2.mMaxDistance;
935
936
// Order constraints so that the ones with the smallest index go first
937
uint32 m1 = l1.GetMinVertexIndex();
938
uint32 m2 = l2.GetMinVertexIndex();
939
if (m1 != m2)
940
return m1 < m2;
941
942
return inLHS < inRHS;
943
});
944
945
// Sort the rod stretch shear constraints
946
QuickSort(group.mRodStretchShearConstraints.begin(), group.mRodStretchShearConstraints.end(), [this](uint inLHS, uint inRHS)
947
{
948
const RodStretchShear &r1 = mRodStretchShearConstraints[inLHS];
949
const RodStretchShear &r2 = mRodStretchShearConstraints[inRHS];
950
951
// First sort so that the rod with the smallest distance to a kinematic vertex comes first
952
float d1 = min(mClosestKinematic[r1.mVertex[0]].mDistance, mClosestKinematic[r1.mVertex[1]].mDistance);
953
float d2 = min(mClosestKinematic[r2.mVertex[0]].mDistance, mClosestKinematic[r2.mVertex[1]].mDistance);
954
if (d1 != d2)
955
return d1 < d2;
956
957
// Then sort on the rod that connects to the smallest kinematic vertex
958
uint32 m1 = min(mClosestKinematic[r1.mVertex[0]].mVertex, mClosestKinematic[r1.mVertex[1]].mVertex);
959
uint32 m2 = min(mClosestKinematic[r2.mVertex[0]].mVertex, mClosestKinematic[r2.mVertex[1]].mVertex);
960
if (m1 != m2)
961
return m1 < m2;
962
963
// Order the rods so that the ones with the smallest index go first (hoping to get better cache locality when we process the rods).
964
m1 = r1.GetMinVertexIndex();
965
m2 = r2.GetMinVertexIndex();
966
if (m1 != m2)
967
return m1 < m2;
968
969
return inLHS < inRHS;
970
});
971
972
// Sort the rod bend twist constraints
973
QuickSort(group.mRodBendTwistConstraints.begin(), group.mRodBendTwistConstraints.end(), [this](uint inLHS, uint inRHS)
974
{
975
const RodBendTwist &b1 = mRodBendTwistConstraints[inLHS];
976
const RodStretchShear &b1_r1 = mRodStretchShearConstraints[b1.mRod[0]];
977
const RodStretchShear &b1_r2 = mRodStretchShearConstraints[b1.mRod[1]];
978
979
const RodBendTwist &b2 = mRodBendTwistConstraints[inRHS];
980
const RodStretchShear &b2_r1 = mRodStretchShearConstraints[b2.mRod[0]];
981
const RodStretchShear &b2_r2 = mRodStretchShearConstraints[b2.mRod[1]];
982
983
// First sort so that the rod with the smallest number of hops to a kinematic vertex comes first.
984
// Note that we don't use distance because of the bilateral interleaving below.
985
uint32 m1 = min(
986
min(mClosestKinematic[b1_r1.mVertex[0]].mHops, mClosestKinematic[b1_r1.mVertex[1]].mHops),
987
min(mClosestKinematic[b1_r2.mVertex[0]].mHops, mClosestKinematic[b1_r2.mVertex[1]].mHops));
988
uint32 m2 = min(
989
min(mClosestKinematic[b2_r1.mVertex[0]].mHops, mClosestKinematic[b2_r1.mVertex[1]].mHops),
990
min(mClosestKinematic[b2_r2.mVertex[0]].mHops, mClosestKinematic[b2_r2.mVertex[1]].mHops));
991
if (m1 != m2)
992
return m1 < m2;
993
994
// Then sort on the rod that connects to the kinematic vertex with lowest index.
995
// This ensures that we consistently order the rods that are attached to other kinematic constraints.
996
// Again, this helps bilateral interleaving below.
997
m1 = min(
998
min(mClosestKinematic[b1_r1.mVertex[0]].mVertex, mClosestKinematic[b1_r1.mVertex[1]].mVertex),
999
min(mClosestKinematic[b1_r2.mVertex[0]].mVertex, mClosestKinematic[b1_r2.mVertex[1]].mVertex));
1000
m2 = min(
1001
min(mClosestKinematic[b2_r1.mVertex[0]].mVertex, mClosestKinematic[b2_r1.mVertex[1]].mVertex),
1002
min(mClosestKinematic[b2_r2.mVertex[0]].mVertex, mClosestKinematic[b2_r2.mVertex[1]].mVertex));
1003
if (m1 != m2)
1004
return m1 < m2;
1005
1006
// Finally order so that the smallest vertex index goes first
1007
m1 = min(b1_r1.GetMinVertexIndex(), b1_r2.GetMinVertexIndex());
1008
m2 = min(b2_r1.GetMinVertexIndex(), b2_r2.GetMinVertexIndex());
1009
if (m1 != m2)
1010
return m1 < m2;
1011
1012
return inLHS < inRHS;
1013
});
1014
1015
// Bilateral interleaving, see figure 4 of "Position and Orientation Based Cosserat Rods" - Kugelstadt and Schoemer - SIGGRAPH 2016
1016
// Keeping the twist constraints sorted often results in an unstable simulation
1017
for (Array<uint>::size_type i = 1, s = group.mRodBendTwistConstraints.size(), s2 = s >> 1; i < s2; i += 2)
1018
std::swap(group.mRodBendTwistConstraints[i], group.mRodBendTwistConstraints[s - i]);
1019
1020
// Sort the dihedral bend constraints
1021
QuickSort(group.mDihedralBendConstraints.begin(), group.mDihedralBendConstraints.end(), [this](uint inLHS, uint inRHS)
1022
{
1023
const DihedralBend &b1 = mDihedralBendConstraints[inLHS];
1024
const DihedralBend &b2 = mDihedralBendConstraints[inRHS];
1025
1026
// First sort so that the constraint with the smallest distance to a kinematic vertex comes first
1027
float d1 = min(
1028
min(mClosestKinematic[b1.mVertex[0]].mDistance, mClosestKinematic[b1.mVertex[1]].mDistance),
1029
min(mClosestKinematic[b1.mVertex[2]].mDistance, mClosestKinematic[b1.mVertex[3]].mDistance));
1030
float d2 = min(
1031
min(mClosestKinematic[b2.mVertex[0]].mDistance, mClosestKinematic[b2.mVertex[1]].mDistance),
1032
min(mClosestKinematic[b2.mVertex[2]].mDistance, mClosestKinematic[b2.mVertex[3]].mDistance));
1033
if (d1 != d2)
1034
return d1 < d2;
1035
1036
// Finally order so that the smallest vertex index goes first
1037
uint32 m1 = b1.GetMinVertexIndex();
1038
uint32 m2 = b2.GetMinVertexIndex();
1039
if (m1 != m2)
1040
return m1 < m2;
1041
1042
return inLHS < inRHS;
1043
});
1044
1045
// Sort the volume constraints
1046
QuickSort(group.mVolumeConstraints.begin(), group.mVolumeConstraints.end(), [this](uint inLHS, uint inRHS)
1047
{
1048
const Volume &v1 = mVolumeConstraints[inLHS];
1049
const Volume &v2 = mVolumeConstraints[inRHS];
1050
1051
// First sort so that the constraint with the smallest distance to a kinematic vertex comes first
1052
float d1 = min(
1053
min(mClosestKinematic[v1.mVertex[0]].mDistance, mClosestKinematic[v1.mVertex[1]].mDistance),
1054
min(mClosestKinematic[v1.mVertex[2]].mDistance, mClosestKinematic[v1.mVertex[3]].mDistance));
1055
float d2 = min(
1056
min(mClosestKinematic[v2.mVertex[0]].mDistance, mClosestKinematic[v2.mVertex[1]].mDistance),
1057
min(mClosestKinematic[v2.mVertex[2]].mDistance, mClosestKinematic[v2.mVertex[3]].mDistance));
1058
if (d1 != d2)
1059
return d1 < d2;
1060
1061
// Order constraints so that the ones with the smallest index go first
1062
uint32 m1 = v1.GetMinVertexIndex();
1063
uint32 m2 = v2.GetMinVertexIndex();
1064
if (m1 != m2)
1065
return m1 < m2;
1066
1067
return inLHS < inRHS;
1068
});
1069
1070
// Sort the skinned constraints
1071
QuickSort(group.mSkinnedConstraints.begin(), group.mSkinnedConstraints.end(), [this](uint inLHS, uint inRHS)
1072
{
1073
const Skinned &s1 = mSkinnedConstraints[inLHS];
1074
const Skinned &s2 = mSkinnedConstraints[inRHS];
1075
1076
// Order the skinned constraints so that the ones with the smallest index go first (hoping to get better cache locality when we process the edges).
1077
if (s1.mVertex != s2.mVertex)
1078
return s1.mVertex < s2.mVertex;
1079
1080
return inLHS < inRHS;
1081
});
1082
}
1083
1084
// Temporary store constraints as we reorder them
1085
Array<Edge> temp_edges;
1086
temp_edges.swap(mEdgeConstraints);
1087
mEdgeConstraints.reserve(temp_edges.size());
1088
outResults.mEdgeRemap.resize(temp_edges.size(), ~uint(0));
1089
1090
Array<LRA> temp_lra;
1091
temp_lra.swap(mLRAConstraints);
1092
mLRAConstraints.reserve(temp_lra.size());
1093
outResults.mLRARemap.resize(temp_lra.size(), ~uint(0));
1094
1095
Array<RodStretchShear> temp_rod_stretch_shear;
1096
temp_rod_stretch_shear.swap(mRodStretchShearConstraints);
1097
mRodStretchShearConstraints.reserve(temp_rod_stretch_shear.size());
1098
outResults.mRodStretchShearConstraintRemap.resize(temp_rod_stretch_shear.size(), ~uint(0));
1099
1100
Array<RodBendTwist> temp_rod_bend_twist;
1101
temp_rod_bend_twist.swap(mRodBendTwistConstraints);
1102
mRodBendTwistConstraints.reserve(temp_rod_bend_twist.size());
1103
outResults.mRodBendTwistConstraintRemap.resize(temp_rod_bend_twist.size(), ~uint(0));
1104
1105
Array<DihedralBend> temp_dihedral_bend;
1106
temp_dihedral_bend.swap(mDihedralBendConstraints);
1107
mDihedralBendConstraints.reserve(temp_dihedral_bend.size());
1108
outResults.mDihedralBendRemap.resize(temp_dihedral_bend.size(), ~uint(0));
1109
1110
Array<Volume> temp_volume;
1111
temp_volume.swap(mVolumeConstraints);
1112
mVolumeConstraints.reserve(temp_volume.size());
1113
outResults.mVolumeRemap.resize(temp_volume.size(), ~uint(0));
1114
1115
Array<Skinned> temp_skinned;
1116
temp_skinned.swap(mSkinnedConstraints);
1117
mSkinnedConstraints.reserve(temp_skinned.size());
1118
outResults.mSkinnedRemap.resize(temp_skinned.size(), ~uint(0));
1119
1120
// Finalize update groups
1121
for (const Group &group : groups)
1122
{
1123
// Reorder edge constraints for this group
1124
for (uint idx : group.mEdgeConstraints)
1125
{
1126
outResults.mEdgeRemap[idx] = (uint)mEdgeConstraints.size();
1127
mEdgeConstraints.push_back(temp_edges[idx]);
1128
}
1129
1130
// Reorder LRA constraints for this group
1131
for (uint idx : group.mLRAConstraints)
1132
{
1133
outResults.mLRARemap[idx] = (uint)mLRAConstraints.size();
1134
mLRAConstraints.push_back(temp_lra[idx]);
1135
}
1136
1137
// Reorder rod stretch shear constraints for this group
1138
for (uint idx : group.mRodStretchShearConstraints)
1139
{
1140
outResults.mRodStretchShearConstraintRemap[idx] = (uint)mRodStretchShearConstraints.size();
1141
mRodStretchShearConstraints.push_back(temp_rod_stretch_shear[idx]);
1142
}
1143
1144
// Reorder rod bend twist constraints for this group
1145
for (uint idx : group.mRodBendTwistConstraints)
1146
{
1147
outResults.mRodBendTwistConstraintRemap[idx] = (uint)mRodBendTwistConstraints.size();
1148
mRodBendTwistConstraints.push_back(temp_rod_bend_twist[idx]);
1149
}
1150
1151
// Reorder dihedral bend constraints for this group
1152
for (uint idx : group.mDihedralBendConstraints)
1153
{
1154
outResults.mDihedralBendRemap[idx] = (uint)mDihedralBendConstraints.size();
1155
mDihedralBendConstraints.push_back(temp_dihedral_bend[idx]);
1156
}
1157
1158
// Reorder volume constraints for this group
1159
for (uint idx : group.mVolumeConstraints)
1160
{
1161
outResults.mVolumeRemap[idx] = (uint)mVolumeConstraints.size();
1162
mVolumeConstraints.push_back(temp_volume[idx]);
1163
}
1164
1165
// Reorder skinned constraints for this group
1166
for (uint idx : group.mSkinnedConstraints)
1167
{
1168
outResults.mSkinnedRemap[idx] = (uint)mSkinnedConstraints.size();
1169
mSkinnedConstraints.push_back(temp_skinned[idx]);
1170
}
1171
1172
// Store end indices
1173
mUpdateGroups.push_back({ (uint)mEdgeConstraints.size(), (uint)mLRAConstraints.size(), (uint)mRodStretchShearConstraints.size(), (uint)mRodBendTwistConstraints.size(), (uint)mDihedralBendConstraints.size(), (uint)mVolumeConstraints.size(), (uint)mSkinnedConstraints.size() });
1174
}
1175
1176
// Remap bend twist indices because mRodStretchShearConstraints has been reordered
1177
for (RodBendTwist &r : mRodBendTwistConstraints)
1178
for (int i = 0; i < 2; ++i)
1179
r.mRod[i] = outResults.mRodStretchShearConstraintRemap[r.mRod[i]];
1180
1181
// Free closest kinematic buffer
1182
mClosestKinematic.clear();
1183
mClosestKinematic.shrink_to_fit();
1184
}
1185
1186
Ref<SoftBodySharedSettings> SoftBodySharedSettings::Clone() const
1187
{
1188
Ref<SoftBodySharedSettings> clone = new SoftBodySharedSettings;
1189
clone->mVertices = mVertices;
1190
clone->mFaces = mFaces;
1191
clone->mEdgeConstraints = mEdgeConstraints;
1192
clone->mDihedralBendConstraints = mDihedralBendConstraints;
1193
clone->mVolumeConstraints = mVolumeConstraints;
1194
clone->mSkinnedConstraints = mSkinnedConstraints;
1195
clone->mSkinnedConstraintNormals = mSkinnedConstraintNormals;
1196
clone->mInvBindMatrices = mInvBindMatrices;
1197
clone->mLRAConstraints = mLRAConstraints;
1198
clone->mRodStretchShearConstraints = mRodStretchShearConstraints;
1199
clone->mRodBendTwistConstraints = mRodBendTwistConstraints;
1200
clone->mMaterials = mMaterials;
1201
clone->mUpdateGroups = mUpdateGroups;
1202
return clone;
1203
}
1204
1205
void SoftBodySharedSettings::SaveBinaryState(StreamOut &inStream) const
1206
{
1207
inStream.Write(mVertices);
1208
inStream.Write(mFaces);
1209
inStream.Write(mEdgeConstraints);
1210
inStream.Write(mDihedralBendConstraints);
1211
inStream.Write(mVolumeConstraints);
1212
inStream.Write(mSkinnedConstraints);
1213
inStream.Write(mSkinnedConstraintNormals);
1214
inStream.Write(mLRAConstraints);
1215
inStream.Write(mUpdateGroups);
1216
1217
// Can't write mRodStretchShearConstraints directly because the class contains padding
1218
inStream.Write(mRodStretchShearConstraints, [](const RodStretchShear &inElement, StreamOut &inS) {
1219
inS.Write(inElement.mVertex);
1220
inS.Write(inElement.mLength);
1221
inS.Write(inElement.mInvMass);
1222
inS.Write(inElement.mCompliance);
1223
inS.Write(inElement.mBishop);
1224
});
1225
1226
// Can't write mRodBendTwistConstraints directly because the class contains padding
1227
inStream.Write(mRodBendTwistConstraints, [](const RodBendTwist &inElement, StreamOut &inS) {
1228
inS.Write(inElement.mRod);
1229
inS.Write(inElement.mCompliance);
1230
inS.Write(inElement.mOmega0);
1231
});
1232
1233
// Can't write mInvBindMatrices directly because the class contains padding
1234
inStream.Write(mInvBindMatrices, [](const InvBind &inElement, StreamOut &inS) {
1235
inS.Write(inElement.mJointIndex);
1236
inS.Write(inElement.mInvBind);
1237
});
1238
}
1239
1240
void SoftBodySharedSettings::RestoreBinaryState(StreamIn &inStream)
1241
{
1242
inStream.Read(mVertices);
1243
inStream.Read(mFaces);
1244
inStream.Read(mEdgeConstraints);
1245
inStream.Read(mDihedralBendConstraints);
1246
inStream.Read(mVolumeConstraints);
1247
inStream.Read(mSkinnedConstraints);
1248
inStream.Read(mSkinnedConstraintNormals);
1249
inStream.Read(mLRAConstraints);
1250
inStream.Read(mUpdateGroups);
1251
1252
inStream.Read(mRodStretchShearConstraints, [](StreamIn &inS, RodStretchShear &outElement) {
1253
inS.Read(outElement.mVertex);
1254
inS.Read(outElement.mLength);
1255
inS.Read(outElement.mInvMass);
1256
inS.Read(outElement.mCompliance);
1257
inS.Read(outElement.mBishop);
1258
});
1259
1260
inStream.Read(mRodBendTwistConstraints, [](StreamIn &inS, RodBendTwist &outElement) {
1261
inS.Read(outElement.mRod);
1262
inS.Read(outElement.mCompliance);
1263
inS.Read(outElement.mOmega0);
1264
});
1265
1266
inStream.Read(mInvBindMatrices, [](StreamIn &inS, InvBind &outElement) {
1267
inS.Read(outElement.mJointIndex);
1268
inS.Read(outElement.mInvBind);
1269
});
1270
}
1271
1272
void SoftBodySharedSettings::SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const
1273
{
1274
SharedSettingsToIDMap::const_iterator settings_iter = ioSettingsMap.find(this);
1275
if (settings_iter == ioSettingsMap.end())
1276
{
1277
// Write settings ID
1278
uint32 settings_id = ioSettingsMap.size();
1279
ioSettingsMap[this] = settings_id;
1280
inStream.Write(settings_id);
1281
1282
// Write the settings
1283
SaveBinaryState(inStream);
1284
1285
// Write materials
1286
StreamUtils::SaveObjectArray(inStream, mMaterials, &ioMaterialMap);
1287
}
1288
else
1289
{
1290
// Known settings, just write the ID
1291
inStream.Write(settings_iter->second);
1292
}
1293
}
1294
1295
SoftBodySharedSettings::SettingsResult SoftBodySharedSettings::sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap)
1296
{
1297
SettingsResult result;
1298
1299
// Read settings id
1300
uint32 settings_id;
1301
inStream.Read(settings_id);
1302
if (inStream.IsEOF() || inStream.IsFailed())
1303
{
1304
result.SetError("Failed to read settings id");
1305
return result;
1306
}
1307
1308
// Check nullptr settings
1309
if (settings_id == ~uint32(0))
1310
{
1311
result.Set(nullptr);
1312
return result;
1313
}
1314
1315
// Check if we already read this settings
1316
if (settings_id < ioSettingsMap.size())
1317
{
1318
result.Set(ioSettingsMap[settings_id]);
1319
return result;
1320
}
1321
1322
// Create new object
1323
Ref<SoftBodySharedSettings> settings = new SoftBodySharedSettings;
1324
1325
// Read state
1326
settings->RestoreBinaryState(inStream);
1327
1328
// Read materials
1329
Result mlresult = StreamUtils::RestoreObjectArray<PhysicsMaterialList>(inStream, ioMaterialMap);
1330
if (mlresult.HasError())
1331
{
1332
result.SetError(mlresult.GetError());
1333
return result;
1334
}
1335
settings->mMaterials = mlresult.Get();
1336
1337
// Add the settings to the map
1338
ioSettingsMap.push_back(settings);
1339
1340
result.Set(settings);
1341
return result;
1342
}
1343
1344
Ref<SoftBodySharedSettings> SoftBodySharedSettings::sCreateCube(uint inGridSize, float inGridSpacing)
1345
{
1346
const Vec3 cOffset = Vec3::sReplicate(-0.5f * inGridSpacing * (inGridSize - 1));
1347
1348
// Create settings
1349
SoftBodySharedSettings *settings = new SoftBodySharedSettings;
1350
for (uint z = 0; z < inGridSize; ++z)
1351
for (uint y = 0; y < inGridSize; ++y)
1352
for (uint x = 0; x < inGridSize; ++x)
1353
{
1354
SoftBodySharedSettings::Vertex v;
1355
(cOffset + Vec3::sReplicate(inGridSpacing) * Vec3(float(x), float(y), float(z))).StoreFloat3(&v.mPosition);
1356
settings->mVertices.push_back(v);
1357
}
1358
1359
// Function to get the vertex index of a point on the cube
1360
auto vertex_index = [inGridSize](uint inX, uint inY, uint inZ)
1361
{
1362
return inX + inY * inGridSize + inZ * inGridSize * inGridSize;
1363
};
1364
1365
// Create edges
1366
for (uint z = 0; z < inGridSize; ++z)
1367
for (uint y = 0; y < inGridSize; ++y)
1368
for (uint x = 0; x < inGridSize; ++x)
1369
{
1370
SoftBodySharedSettings::Edge e;
1371
e.mVertex[0] = vertex_index(x, y, z);
1372
if (x < inGridSize - 1)
1373
{
1374
e.mVertex[1] = vertex_index(x + 1, y, z);
1375
settings->mEdgeConstraints.push_back(e);
1376
}
1377
if (y < inGridSize - 1)
1378
{
1379
e.mVertex[1] = vertex_index(x, y + 1, z);
1380
settings->mEdgeConstraints.push_back(e);
1381
}
1382
if (z < inGridSize - 1)
1383
{
1384
e.mVertex[1] = vertex_index(x, y, z + 1);
1385
settings->mEdgeConstraints.push_back(e);
1386
}
1387
}
1388
settings->CalculateEdgeLengths();
1389
1390
// Tetrahedrons to fill a cube
1391
const int tetra_indices[6][4][3] = {
1392
{ {0, 0, 0}, {0, 1, 1}, {0, 0, 1}, {1, 1, 1} },
1393
{ {0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1} },
1394
{ {0, 0, 0}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1} },
1395
{ {0, 0, 0}, {1, 0, 1}, {1, 0, 0}, {1, 1, 1} },
1396
{ {0, 0, 0}, {1, 1, 0}, {0, 1, 0}, {1, 1, 1} },
1397
{ {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1} }
1398
};
1399
1400
// Create volume constraints
1401
for (uint z = 0; z < inGridSize - 1; ++z)
1402
for (uint y = 0; y < inGridSize - 1; ++y)
1403
for (uint x = 0; x < inGridSize - 1; ++x)
1404
for (uint t = 0; t < 6; ++t)
1405
{
1406
SoftBodySharedSettings::Volume v;
1407
for (uint i = 0; i < 4; ++i)
1408
v.mVertex[i] = vertex_index(x + tetra_indices[t][i][0], y + tetra_indices[t][i][1], z + tetra_indices[t][i][2]);
1409
settings->mVolumeConstraints.push_back(v);
1410
}
1411
1412
settings->CalculateVolumeConstraintVolumes();
1413
1414
// Create faces
1415
for (uint y = 0; y < inGridSize - 1; ++y)
1416
for (uint x = 0; x < inGridSize - 1; ++x)
1417
{
1418
SoftBodySharedSettings::Face f;
1419
1420
// Face 1
1421
f.mVertex[0] = vertex_index(x, y, 0);
1422
f.mVertex[1] = vertex_index(x, y + 1, 0);
1423
f.mVertex[2] = vertex_index(x + 1, y + 1, 0);
1424
settings->AddFace(f);
1425
1426
f.mVertex[1] = vertex_index(x + 1, y + 1, 0);
1427
f.mVertex[2] = vertex_index(x + 1, y, 0);
1428
settings->AddFace(f);
1429
1430
// Face 2
1431
f.mVertex[0] = vertex_index(x, y, inGridSize - 1);
1432
f.mVertex[1] = vertex_index(x + 1, y + 1, inGridSize - 1);
1433
f.mVertex[2] = vertex_index(x, y + 1, inGridSize - 1);
1434
settings->AddFace(f);
1435
1436
f.mVertex[1] = vertex_index(x + 1, y, inGridSize - 1);
1437
f.mVertex[2] = vertex_index(x + 1, y + 1, inGridSize - 1);
1438
settings->AddFace(f);
1439
1440
// Face 3
1441
f.mVertex[0] = vertex_index(x, 0, y);
1442
f.mVertex[1] = vertex_index(x + 1, 0, y + 1);
1443
f.mVertex[2] = vertex_index(x, 0, y + 1);
1444
settings->AddFace(f);
1445
1446
f.mVertex[1] = vertex_index(x + 1, 0, y);
1447
f.mVertex[2] = vertex_index(x + 1, 0, y + 1);
1448
settings->AddFace(f);
1449
1450
// Face 4
1451
f.mVertex[0] = vertex_index(x, inGridSize - 1, y);
1452
f.mVertex[1] = vertex_index(x, inGridSize - 1, y + 1);
1453
f.mVertex[2] = vertex_index(x + 1, inGridSize - 1, y + 1);
1454
settings->AddFace(f);
1455
1456
f.mVertex[1] = vertex_index(x + 1, inGridSize - 1, y + 1);
1457
f.mVertex[2] = vertex_index(x + 1, inGridSize - 1, y);
1458
settings->AddFace(f);
1459
1460
// Face 5
1461
f.mVertex[0] = vertex_index(0, x, y);
1462
f.mVertex[1] = vertex_index(0, x, y + 1);
1463
f.mVertex[2] = vertex_index(0, x + 1, y + 1);
1464
settings->AddFace(f);
1465
1466
f.mVertex[1] = vertex_index(0, x + 1, y + 1);
1467
f.mVertex[2] = vertex_index(0, x + 1, y);
1468
settings->AddFace(f);
1469
1470
// Face 6
1471
f.mVertex[0] = vertex_index(inGridSize - 1, x, y);
1472
f.mVertex[1] = vertex_index(inGridSize - 1, x + 1, y + 1);
1473
f.mVertex[2] = vertex_index(inGridSize - 1, x, y + 1);
1474
settings->AddFace(f);
1475
1476
f.mVertex[1] = vertex_index(inGridSize - 1, x + 1, y);
1477
f.mVertex[2] = vertex_index(inGridSize - 1, x + 1, y + 1);
1478
settings->AddFace(f);
1479
}
1480
1481
// Optimize the settings
1482
settings->Optimize();
1483
1484
return settings;
1485
}
1486
1487
JPH_NAMESPACE_END
1488
1489