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
9912 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::DihedralBend)
40
{
41
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mVertex)
42
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mCompliance)
43
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mInitialAngle)
44
}
45
46
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Volume)
47
{
48
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mVertex)
49
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mSixRestVolume)
50
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mCompliance)
51
}
52
53
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind)
54
{
55
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mJointIndex)
56
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mInvBind)
57
}
58
59
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::SkinWeight)
60
{
61
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mInvBindIndex)
62
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mWeight)
63
}
64
65
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Skinned)
66
{
67
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mVertex)
68
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mWeights)
69
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mMaxDistance)
70
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopDistance)
71
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopRadius)
72
}
73
74
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::LRA)
75
{
76
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mVertex)
77
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mMaxDistance)
78
}
79
80
JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings)
81
{
82
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertices)
83
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mFaces)
84
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeConstraints)
85
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mDihedralBendConstraints)
86
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVolumeConstraints)
87
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mSkinnedConstraints)
88
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mInvBindMatrices)
89
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mLRAConstraints)
90
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mMaterials)
91
JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius)
92
}
93
94
void SoftBodySharedSettings::CalculateClosestKinematic()
95
{
96
// Check if we already calculated this
97
if (!mClosestKinematic.empty())
98
return;
99
100
// Reserve output size
101
mClosestKinematic.resize(mVertices.size());
102
103
// Create a list of connected vertices
104
Array<Array<uint32>> connectivity;
105
connectivity.resize(mVertices.size());
106
for (const Edge &e : mEdgeConstraints)
107
{
108
connectivity[e.mVertex[0]].push_back(e.mVertex[1]);
109
connectivity[e.mVertex[1]].push_back(e.mVertex[0]);
110
}
111
112
// Use Dijkstra's algorithm to find the closest kinematic vertex for each vertex
113
// See: https://en.wikipedia.org/wiki/Dijkstra's_algorithm
114
//
115
// An element in the open list
116
struct Open
117
{
118
// Order so that we get the shortest distance first
119
bool operator < (const Open &inRHS) const
120
{
121
return mDistance > inRHS.mDistance;
122
}
123
124
uint32 mVertex;
125
float mDistance;
126
};
127
128
// Start with all kinematic elements
129
Array<Open> to_visit;
130
for (uint32 v = 0; v < mVertices.size(); ++v)
131
if (mVertices[v].mInvMass == 0.0f)
132
{
133
mClosestKinematic[v].mVertex = v;
134
mClosestKinematic[v].mDistance = 0.0f;
135
to_visit.push_back({ v, 0.0f });
136
BinaryHeapPush(to_visit.begin(), to_visit.end(), std::less<Open> { });
137
}
138
139
// Visit all vertices remembering the closest kinematic vertex and its distance
140
JPH_IF_ENABLE_ASSERTS(float last_closest = 0.0f;)
141
while (!to_visit.empty())
142
{
143
// Pop element from the open list
144
BinaryHeapPop(to_visit.begin(), to_visit.end(), std::less<Open> { });
145
Open current = to_visit.back();
146
to_visit.pop_back();
147
JPH_ASSERT(current.mDistance >= last_closest);
148
JPH_IF_ENABLE_ASSERTS(last_closest = current.mDistance;)
149
150
// Loop through all of its connected vertices
151
for (uint32 v : connectivity[current.mVertex])
152
{
153
// Calculate distance from the current vertex to this target vertex and check if it is smaller
154
float new_distance = current.mDistance + (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current.mVertex].mPosition)).Length();
155
if (new_distance < mClosestKinematic[v].mDistance)
156
{
157
// Remember new closest vertex
158
mClosestKinematic[v].mVertex = mClosestKinematic[current.mVertex].mVertex;
159
mClosestKinematic[v].mDistance = new_distance;
160
to_visit.push_back({ v, new_distance });
161
BinaryHeapPush(to_visit.begin(), to_visit.end(), std::less<Open> { });
162
}
163
}
164
}
165
}
166
167
void SoftBodySharedSettings::CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType, float inAngleTolerance)
168
{
169
struct EdgeHelper
170
{
171
uint32 mVertex[2];
172
uint32 mEdgeIdx;
173
};
174
175
// Create list of all edges
176
Array<EdgeHelper> edges;
177
edges.reserve(mFaces.size() * 3);
178
for (const Face &f : mFaces)
179
for (int i = 0; i < 3; ++i)
180
{
181
uint32 v0 = f.mVertex[i];
182
uint32 v1 = f.mVertex[(i + 1) % 3];
183
184
EdgeHelper e;
185
e.mVertex[0] = min(v0, v1);
186
e.mVertex[1] = max(v0, v1);
187
e.mEdgeIdx = uint32(&f - mFaces.data()) * 3 + i;
188
edges.push_back(e);
189
}
190
191
// Sort the edges
192
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]); });
193
194
// Only add edges if one of the vertices is movable
195
auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inCompliance1, float inCompliance2) {
196
if ((mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f)
197
&& inCompliance1 < FLT_MAX && inCompliance2 < FLT_MAX)
198
{
199
Edge temp_edge;
200
temp_edge.mVertex[0] = inVtx1;
201
temp_edge.mVertex[1] = inVtx2;
202
temp_edge.mCompliance = 0.5f * (inCompliance1 + inCompliance2);
203
temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length();
204
JPH_ASSERT(temp_edge.mRestLength > 0.0f);
205
mEdgeConstraints.push_back(temp_edge);
206
}
207
};
208
209
// Helper function to get the attributes of a vertex
210
auto attr = [inVertexAttributes, inVertexAttributesLength](uint32 inVertex) {
211
return inVertexAttributes[min(inVertex, inVertexAttributesLength - 1)];
212
};
213
214
// Create the constraints
215
float sq_sin_tolerance = Square(Sin(inAngleTolerance));
216
float sq_cos_tolerance = Square(Cos(inAngleTolerance));
217
mEdgeConstraints.clear();
218
mEdgeConstraints.reserve(edges.size());
219
for (Array<EdgeHelper>::size_type i = 0; i < edges.size(); ++i)
220
{
221
const EdgeHelper &e0 = edges[i];
222
223
// Get attributes for the vertices of the edge
224
const VertexAttributes &a0 = attr(e0.mVertex[0]);
225
const VertexAttributes &a1 = attr(e0.mVertex[1]);
226
227
// 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)
228
bool is_shear = false;
229
230
// Test if there are any shared edges
231
for (Array<EdgeHelper>::size_type j = i + 1; j < edges.size(); ++j)
232
{
233
const EdgeHelper &e1 = edges[j];
234
if (e0.mVertex[0] == e1.mVertex[0] && e0.mVertex[1] == e1.mVertex[1])
235
{
236
// Get opposing vertices
237
const Face &f0 = mFaces[e0.mEdgeIdx / 3];
238
const Face &f1 = mFaces[e1.mEdgeIdx / 3];
239
uint32 vopposite0 = f0.mVertex[(e0.mEdgeIdx + 2) % 3];
240
uint32 vopposite1 = f1.mVertex[(e1.mEdgeIdx + 2) % 3];
241
const VertexAttributes &a_opposite0 = attr(vopposite0);
242
const VertexAttributes &a_opposite1 = attr(vopposite1);
243
244
// Faces should be roughly in a plane
245
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));
246
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));
247
if (Square(n0.Dot(n1)) > sq_cos_tolerance * n0.LengthSq() * n1.LengthSq())
248
{
249
// Faces should approximately form a quad
250
Vec3 e0_dir = Vec3(mVertices[vopposite0].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
251
Vec3 e1_dir = Vec3(mVertices[vopposite1].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
252
if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq())
253
{
254
// Shear constraint
255
add_edge(vopposite0, vopposite1, a_opposite0.mShearCompliance, a_opposite1.mShearCompliance);
256
is_shear = true;
257
}
258
}
259
260
// Bend constraint
261
switch (inBendType)
262
{
263
case EBendType::None:
264
// Do nothing
265
break;
266
267
case EBendType::Distance:
268
// Create an edge constraint to represent the bend constraint
269
// Use the bend compliance of the shared edge
270
if (!is_shear)
271
add_edge(vopposite0, vopposite1, a0.mBendCompliance, a1.mBendCompliance);
272
break;
273
274
case EBendType::Dihedral:
275
// Test if both opposite vertices are free to move
276
if ((mVertices[vopposite0].mInvMass > 0.0f || mVertices[vopposite1].mInvMass > 0.0f)
277
&& a0.mBendCompliance < FLT_MAX && a1.mBendCompliance < FLT_MAX)
278
{
279
// Create a bend constraint
280
// Use the bend compliance of the shared edge
281
mDihedralBendConstraints.emplace_back(e0.mVertex[0], e0.mVertex[1], vopposite0, vopposite1, 0.5f * (a0.mBendCompliance + a1.mBendCompliance));
282
}
283
break;
284
}
285
}
286
else
287
{
288
// Start iterating from the first non-shared edge
289
i = j - 1;
290
break;
291
}
292
}
293
294
// Create a edge constraint for the current edge
295
add_edge(e0.mVertex[0], e0.mVertex[1], is_shear? a0.mShearCompliance : a0.mCompliance, is_shear? a1.mShearCompliance : a1.mCompliance);
296
}
297
mEdgeConstraints.shrink_to_fit();
298
299
// Calculate the initial angle for all bend constraints
300
CalculateBendConstraintConstants();
301
302
// Check if any vertices have LRA constraints
303
bool has_lra_constraints = false;
304
for (const VertexAttributes *va = inVertexAttributes; va < inVertexAttributes + inVertexAttributesLength; ++va)
305
if (va->mLRAType != ELRAType::None)
306
{
307
has_lra_constraints = true;
308
break;
309
}
310
if (has_lra_constraints)
311
{
312
// Ensure we have calculated the closest kinematic vertex for each vertex
313
CalculateClosestKinematic();
314
315
// Find non-kinematic vertices
316
for (uint32 v = 0; v < (uint32)mVertices.size(); ++v)
317
if (mVertices[v].mInvMass > 0.0f)
318
{
319
// Check if a closest vertex was found
320
uint32 closest = mClosestKinematic[v].mVertex;
321
if (closest != 0xffffffff)
322
{
323
// Check which LRA constraint to create
324
const VertexAttributes &va = attr(v);
325
switch (va.mLRAType)
326
{
327
case ELRAType::None:
328
break;
329
330
case ELRAType::EuclideanDistance:
331
mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * (Vec3(mVertices[closest].mPosition) - Vec3(mVertices[v].mPosition)).Length());
332
break;
333
334
case ELRAType::GeodesicDistance:
335
mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * mClosestKinematic[v].mDistance);
336
break;
337
}
338
}
339
}
340
}
341
}
342
343
void SoftBodySharedSettings::CalculateEdgeLengths()
344
{
345
for (Edge &e : mEdgeConstraints)
346
{
347
e.mRestLength = (Vec3(mVertices[e.mVertex[1]].mPosition) - Vec3(mVertices[e.mVertex[0]].mPosition)).Length();
348
JPH_ASSERT(e.mRestLength > 0.0f);
349
}
350
}
351
352
void SoftBodySharedSettings::CalculateLRALengths(float inMaxDistanceMultiplier)
353
{
354
for (LRA &l : mLRAConstraints)
355
{
356
l.mMaxDistance = inMaxDistanceMultiplier * (Vec3(mVertices[l.mVertex[1]].mPosition) - Vec3(mVertices[l.mVertex[0]].mPosition)).Length();
357
JPH_ASSERT(l.mMaxDistance > 0.0f);
358
}
359
}
360
361
void SoftBodySharedSettings::CalculateBendConstraintConstants()
362
{
363
for (DihedralBend &b : mDihedralBendConstraints)
364
{
365
// Get positions
366
Vec3 x0 = Vec3(mVertices[b.mVertex[0]].mPosition);
367
Vec3 x1 = Vec3(mVertices[b.mVertex[1]].mPosition);
368
Vec3 x2 = Vec3(mVertices[b.mVertex[2]].mPosition);
369
Vec3 x3 = Vec3(mVertices[b.mVertex[3]].mPosition);
370
371
/*
372
x2
373
e1/ \e3
374
/ \
375
x0----x1
376
\ e0 /
377
e2\ /e4
378
x3
379
*/
380
381
// Calculate edges
382
Vec3 e0 = x1 - x0;
383
Vec3 e1 = x2 - x0;
384
Vec3 e2 = x3 - x0;
385
386
// Normals of both triangles
387
Vec3 n1 = e0.Cross(e1);
388
Vec3 n2 = e2.Cross(e0);
389
float denom = sqrt(n1.LengthSq() * n2.LengthSq());
390
if (denom < 1.0e-12f)
391
b.mInitialAngle = 0.0f;
392
else
393
{
394
float sign = Sign(n2.Cross(n1).Dot(e0));
395
b.mInitialAngle = sign * ACosApproximate(n1.Dot(n2) / denom); // Runtime uses the approximation too
396
}
397
}
398
}
399
400
void SoftBodySharedSettings::CalculateVolumeConstraintVolumes()
401
{
402
for (Volume &v : mVolumeConstraints)
403
{
404
Vec3 x1(mVertices[v.mVertex[0]].mPosition);
405
Vec3 x2(mVertices[v.mVertex[1]].mPosition);
406
Vec3 x3(mVertices[v.mVertex[2]].mPosition);
407
Vec3 x4(mVertices[v.mVertex[3]].mPosition);
408
409
Vec3 x1x2 = x2 - x1;
410
Vec3 x1x3 = x3 - x1;
411
Vec3 x1x4 = x4 - x1;
412
413
v.mSixRestVolume = abs(x1x2.Cross(x1x3).Dot(x1x4));
414
}
415
}
416
417
void SoftBodySharedSettings::CalculateSkinnedConstraintNormals()
418
{
419
// Clear any previous results
420
mSkinnedConstraintNormals.clear();
421
422
// If there are no skinned constraints, we're done
423
if (mSkinnedConstraints.empty())
424
return;
425
426
// First collect all vertices that are skinned
427
using VertexIndexSet = UnorderedSet<uint32>;
428
VertexIndexSet skinned_vertices;
429
skinned_vertices.reserve(VertexIndexSet::size_type(mSkinnedConstraints.size()));
430
for (const Skinned &s : mSkinnedConstraints)
431
skinned_vertices.insert(s.mVertex);
432
433
// Now collect all faces that connect only to skinned vertices
434
using ConnectedFacesMap = UnorderedMap<uint32, VertexIndexSet>;
435
ConnectedFacesMap connected_faces;
436
connected_faces.reserve(ConnectedFacesMap::size_type(mVertices.size()));
437
for (const Face &f : mFaces)
438
{
439
// Must connect to only skinned vertices
440
bool valid = true;
441
for (uint32 v : f.mVertex)
442
valid &= skinned_vertices.find(v) != skinned_vertices.end();
443
if (!valid)
444
continue;
445
446
// Store faces that connect to vertices
447
for (uint32 v : f.mVertex)
448
connected_faces[v].insert(uint32(&f - mFaces.data()));
449
}
450
451
// Populate the list of connecting faces per skinned vertex
452
mSkinnedConstraintNormals.reserve(mFaces.size());
453
for (Skinned &s : mSkinnedConstraints)
454
{
455
uint32 start = uint32(mSkinnedConstraintNormals.size());
456
JPH_ASSERT((start >> 24) == 0);
457
ConnectedFacesMap::const_iterator connected_faces_it = connected_faces.find(s.mVertex);
458
if (connected_faces_it != connected_faces.cend())
459
{
460
const VertexIndexSet &faces = connected_faces_it->second;
461
uint32 num = uint32(faces.size());
462
JPH_ASSERT(num < 256);
463
mSkinnedConstraintNormals.insert(mSkinnedConstraintNormals.end(), faces.begin(), faces.end());
464
QuickSort(mSkinnedConstraintNormals.begin() + start, mSkinnedConstraintNormals.begin() + start + num);
465
s.mNormalInfo = start + (num << 24);
466
}
467
else
468
s.mNormalInfo = 0;
469
}
470
mSkinnedConstraintNormals.shrink_to_fit();
471
}
472
473
void SoftBodySharedSettings::Optimize(OptimizationResults &outResults)
474
{
475
// Clear any previous results
476
mUpdateGroups.clear();
477
478
// Create a list of connected vertices
479
struct Connection
480
{
481
uint32 mVertex;
482
uint32 mCount;
483
};
484
Array<Array<Connection>> connectivity;
485
connectivity.resize(mVertices.size());
486
auto add_connection = [&connectivity](uint inV1, uint inV2) {
487
for (int i = 0; i < 2; ++i)
488
{
489
bool found = false;
490
for (Connection &c : connectivity[inV1])
491
if (c.mVertex == inV2)
492
{
493
c.mCount++;
494
found = true;
495
break;
496
}
497
if (!found)
498
connectivity[inV1].push_back({ inV2, 1 });
499
500
std::swap(inV1, inV2);
501
}
502
};
503
for (const Edge &c : mEdgeConstraints)
504
add_connection(c.mVertex[0], c.mVertex[1]);
505
for (const LRA &c : mLRAConstraints)
506
add_connection(c.mVertex[0], c.mVertex[1]);
507
for (const DihedralBend &c : mDihedralBendConstraints)
508
{
509
add_connection(c.mVertex[0], c.mVertex[1]);
510
add_connection(c.mVertex[0], c.mVertex[2]);
511
add_connection(c.mVertex[0], c.mVertex[3]);
512
add_connection(c.mVertex[1], c.mVertex[2]);
513
add_connection(c.mVertex[1], c.mVertex[3]);
514
add_connection(c.mVertex[2], c.mVertex[3]);
515
}
516
for (const Volume &c : mVolumeConstraints)
517
{
518
add_connection(c.mVertex[0], c.mVertex[1]);
519
add_connection(c.mVertex[0], c.mVertex[2]);
520
add_connection(c.mVertex[0], c.mVertex[3]);
521
add_connection(c.mVertex[1], c.mVertex[2]);
522
add_connection(c.mVertex[1], c.mVertex[3]);
523
add_connection(c.mVertex[2], c.mVertex[3]);
524
}
525
// Skinned constraints only update 1 vertex, so we don't need special logic here
526
527
// Maps each of the vertices to a group index
528
Array<int> group_idx;
529
group_idx.resize(mVertices.size(), -1);
530
531
// Which group we are currently filling and its vertices
532
int current_group_idx = 0;
533
Array<uint> current_group;
534
535
// Start greedy algorithm to group vertices
536
for (;;)
537
{
538
// Find the bounding box of the ungrouped vertices
539
AABox bounds;
540
for (uint i = 0; i < (uint)mVertices.size(); ++i)
541
if (group_idx[i] == -1)
542
bounds.Encapsulate(Vec3(mVertices[i].mPosition));
543
544
// If the bounds are invalid, it means that there were no ungrouped vertices
545
if (!bounds.IsValid())
546
break;
547
548
// Determine longest and shortest axis
549
Vec3 bounds_size = bounds.GetSize();
550
uint max_axis = bounds_size.GetHighestComponentIndex();
551
uint min_axis = bounds_size.GetLowestComponentIndex();
552
if (min_axis == max_axis)
553
min_axis = (min_axis + 1) % 3;
554
uint mid_axis = 3 - min_axis - max_axis;
555
556
// Find the vertex that has the lowest value on the axis with the largest extent
557
uint current_vertex = UINT_MAX;
558
Float3 current_vertex_position { FLT_MAX, FLT_MAX, FLT_MAX };
559
for (uint i = 0; i < (uint)mVertices.size(); ++i)
560
if (group_idx[i] == -1)
561
{
562
const Float3 &vertex_position = mVertices[i].mPosition;
563
float max_axis_value = vertex_position[max_axis];
564
float mid_axis_value = vertex_position[mid_axis];
565
float min_axis_value = vertex_position[min_axis];
566
567
if (max_axis_value < current_vertex_position[max_axis]
568
|| (max_axis_value == current_vertex_position[max_axis]
569
&& (mid_axis_value < current_vertex_position[mid_axis]
570
|| (mid_axis_value == current_vertex_position[mid_axis]
571
&& min_axis_value < current_vertex_position[min_axis]))))
572
{
573
current_vertex_position = mVertices[i].mPosition;
574
current_vertex = i;
575
}
576
}
577
if (current_vertex == UINT_MAX)
578
break;
579
580
// Initialize the current group with 1 vertex
581
current_group.push_back(current_vertex);
582
group_idx[current_vertex] = current_group_idx;
583
584
// Fill up the group
585
for (;;)
586
{
587
// Find the vertex that is most connected to the current group
588
uint best_vertex = UINT_MAX;
589
uint best_num_connections = 0;
590
float best_dist_sq = FLT_MAX;
591
for (uint i = 0; i < (uint)current_group.size(); ++i) // For all vertices in the current group
592
for (const Connection &c : connectivity[current_group[i]]) // For all connections to other vertices
593
{
594
uint v = c.mVertex;
595
if (group_idx[v] == -1) // Ungrouped vertices only
596
{
597
// Count the number of connections to this group
598
uint num_connections = 0;
599
for (const Connection &v2 : connectivity[v])
600
if (group_idx[v2.mVertex] == current_group_idx)
601
num_connections += v2.mCount;
602
603
// Calculate distance to group centroid
604
float dist_sq = (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current_group.front()].mPosition)).LengthSq();
605
606
if (best_vertex == UINT_MAX
607
|| num_connections > best_num_connections
608
|| (num_connections == best_num_connections && dist_sq < best_dist_sq))
609
{
610
best_vertex = v;
611
best_num_connections = num_connections;
612
best_dist_sq = dist_sq;
613
}
614
}
615
}
616
617
// Add the best vertex to the current group
618
if (best_vertex != UINT_MAX)
619
{
620
current_group.push_back(best_vertex);
621
group_idx[best_vertex] = current_group_idx;
622
}
623
624
// Create a new group?
625
if (current_group.size() >= SoftBodyUpdateContext::cVertexConstraintBatch // If full, yes
626
|| (current_group.size() > SoftBodyUpdateContext::cVertexConstraintBatch / 2 && best_vertex == UINT_MAX)) // If half full and we found no connected vertex, yes
627
{
628
current_group.clear();
629
current_group_idx++;
630
break;
631
}
632
633
// If we didn't find a connected vertex, we need to find a new starting vertex
634
if (best_vertex == UINT_MAX)
635
break;
636
}
637
}
638
639
// 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
640
if (current_group.size() > SoftBodyUpdateContext::cVertexConstraintBatch / 2)
641
++current_group_idx;
642
643
// We no longer need the current group array, free the memory
644
current_group.clear();
645
current_group.shrink_to_fit();
646
647
// We're done with the connectivity list, free the memory
648
connectivity.clear();
649
connectivity.shrink_to_fit();
650
651
// Assign the constraints to their groups
652
struct Group
653
{
654
uint GetSize() const
655
{
656
return (uint)mEdgeConstraints.size() + (uint)mLRAConstraints.size() + (uint)mDihedralBendConstraints.size() + (uint)mVolumeConstraints.size() + (uint)mSkinnedConstraints.size();
657
}
658
659
Array<uint> mEdgeConstraints;
660
Array<uint> mLRAConstraints;
661
Array<uint> mDihedralBendConstraints;
662
Array<uint> mVolumeConstraints;
663
Array<uint> mSkinnedConstraints;
664
};
665
Array<Group> groups;
666
groups.resize(current_group_idx + 1); // + non parallel group
667
for (const Edge &e : mEdgeConstraints)
668
{
669
int g1 = group_idx[e.mVertex[0]];
670
int g2 = group_idx[e.mVertex[1]];
671
JPH_ASSERT(g1 >= 0 && g2 >= 0);
672
if (g1 == g2) // In the same group
673
groups[g1].mEdgeConstraints.push_back(uint(&e - mEdgeConstraints.data()));
674
else // In different groups -> parallel group
675
groups.back().mEdgeConstraints.push_back(uint(&e - mEdgeConstraints.data()));
676
}
677
for (const LRA &l : mLRAConstraints)
678
{
679
int g1 = group_idx[l.mVertex[0]];
680
int g2 = group_idx[l.mVertex[1]];
681
JPH_ASSERT(g1 >= 0 && g2 >= 0);
682
if (g1 == g2) // In the same group
683
groups[g1].mLRAConstraints.push_back(uint(&l - mLRAConstraints.data()));
684
else // In different groups -> parallel group
685
groups.back().mLRAConstraints.push_back(uint(&l - mLRAConstraints.data()));
686
}
687
for (const DihedralBend &d : mDihedralBendConstraints)
688
{
689
int g1 = group_idx[d.mVertex[0]];
690
int g2 = group_idx[d.mVertex[1]];
691
int g3 = group_idx[d.mVertex[2]];
692
int g4 = group_idx[d.mVertex[3]];
693
JPH_ASSERT(g1 >= 0 && g2 >= 0 && g3 >= 0 && g4 >= 0);
694
if (g1 == g2 && g1 == g3 && g1 == g4) // In the same group
695
groups[g1].mDihedralBendConstraints.push_back(uint(&d - mDihedralBendConstraints.data()));
696
else // In different groups -> parallel group
697
groups.back().mDihedralBendConstraints.push_back(uint(&d - mDihedralBendConstraints.data()));
698
}
699
for (const Volume &v : mVolumeConstraints)
700
{
701
int g1 = group_idx[v.mVertex[0]];
702
int g2 = group_idx[v.mVertex[1]];
703
int g3 = group_idx[v.mVertex[2]];
704
int g4 = group_idx[v.mVertex[3]];
705
JPH_ASSERT(g1 >= 0 && g2 >= 0 && g3 >= 0 && g4 >= 0);
706
if (g1 == g2 && g1 == g3 && g1 == g4) // In the same group
707
groups[g1].mVolumeConstraints.push_back(uint(&v - mVolumeConstraints.data()));
708
else // In different groups -> parallel group
709
groups.back().mVolumeConstraints.push_back(uint(&v - mVolumeConstraints.data()));
710
}
711
for (const Skinned &s : mSkinnedConstraints)
712
{
713
int g1 = group_idx[s.mVertex];
714
JPH_ASSERT(g1 >= 0);
715
groups[g1].mSkinnedConstraints.push_back(uint(&s - mSkinnedConstraints.data()));
716
}
717
718
// Sort the parallel groups from big to small (this means the big groups will be scheduled first and have more time to complete)
719
QuickSort(groups.begin(), groups.end() - 1, [](const Group &inLHS, const Group &inRHS) { return inLHS.GetSize() > inRHS.GetSize(); });
720
721
// Make sure we know the closest kinematic vertex so we can sort
722
CalculateClosestKinematic();
723
724
// Sort within each group
725
for (Group &group : groups)
726
{
727
// Sort the edge constraints
728
QuickSort(group.mEdgeConstraints.begin(), group.mEdgeConstraints.end(), [this](uint inLHS, uint inRHS)
729
{
730
const Edge &e1 = mEdgeConstraints[inLHS];
731
const Edge &e2 = mEdgeConstraints[inRHS];
732
733
// First sort so that the edge with the smallest distance to a kinematic vertex comes first
734
float d1 = min(mClosestKinematic[e1.mVertex[0]].mDistance, mClosestKinematic[e1.mVertex[1]].mDistance);
735
float d2 = min(mClosestKinematic[e2.mVertex[0]].mDistance, mClosestKinematic[e2.mVertex[1]].mDistance);
736
if (d1 != d2)
737
return d1 < d2;
738
739
// Order the edges so that the ones with the smallest index go first (hoping to get better cache locality when we process the edges).
740
// Note we could also re-order the vertices but that would be much more of a burden to the end user
741
uint32 m1 = e1.GetMinVertexIndex();
742
uint32 m2 = e2.GetMinVertexIndex();
743
if (m1 != m2)
744
return m1 < m2;
745
746
return inLHS < inRHS;
747
});
748
749
// Sort the LRA constraints
750
QuickSort(group.mLRAConstraints.begin(), group.mLRAConstraints.end(), [this](uint inLHS, uint inRHS)
751
{
752
const LRA &l1 = mLRAConstraints[inLHS];
753
const LRA &l2 = mLRAConstraints[inRHS];
754
755
// First sort so that the longest constraint comes first (meaning the shortest constraint has the most influence on the end result)
756
// Most of the time there will be a single LRA constraint per vertex and since the LRA constraint only modifies a single vertex,
757
// updating one constraint will not violate another constraint.
758
if (l1.mMaxDistance != l2.mMaxDistance)
759
return l1.mMaxDistance > l2.mMaxDistance;
760
761
// Order constraints so that the ones with the smallest index go first
762
uint32 m1 = l1.GetMinVertexIndex();
763
uint32 m2 = l2.GetMinVertexIndex();
764
if (m1 != m2)
765
return m1 < m2;
766
767
return inLHS < inRHS;
768
});
769
770
// Sort the dihedral bend constraints
771
QuickSort(group.mDihedralBendConstraints.begin(), group.mDihedralBendConstraints.end(), [this](uint inLHS, uint inRHS)
772
{
773
const DihedralBend &b1 = mDihedralBendConstraints[inLHS];
774
const DihedralBend &b2 = mDihedralBendConstraints[inRHS];
775
776
// First sort so that the constraint with the smallest distance to a kinematic vertex comes first
777
float d1 = min(
778
min(mClosestKinematic[b1.mVertex[0]].mDistance, mClosestKinematic[b1.mVertex[1]].mDistance),
779
min(mClosestKinematic[b1.mVertex[2]].mDistance, mClosestKinematic[b1.mVertex[3]].mDistance));
780
float d2 = min(
781
min(mClosestKinematic[b2.mVertex[0]].mDistance, mClosestKinematic[b2.mVertex[1]].mDistance),
782
min(mClosestKinematic[b2.mVertex[2]].mDistance, mClosestKinematic[b2.mVertex[3]].mDistance));
783
if (d1 != d2)
784
return d1 < d2;
785
786
// Order constraints so that the ones with the smallest index go first
787
uint32 m1 = b1.GetMinVertexIndex();
788
uint32 m2 = b2.GetMinVertexIndex();
789
if (m1 != m2)
790
return m1 < m2;
791
792
return inLHS < inRHS;
793
});
794
795
// Sort the volume constraints
796
QuickSort(group.mVolumeConstraints.begin(), group.mVolumeConstraints.end(), [this](uint inLHS, uint inRHS)
797
{
798
const Volume &v1 = mVolumeConstraints[inLHS];
799
const Volume &v2 = mVolumeConstraints[inRHS];
800
801
// First sort so that the constraint with the smallest distance to a kinematic vertex comes first
802
float d1 = min(
803
min(mClosestKinematic[v1.mVertex[0]].mDistance, mClosestKinematic[v1.mVertex[1]].mDistance),
804
min(mClosestKinematic[v1.mVertex[2]].mDistance, mClosestKinematic[v1.mVertex[3]].mDistance));
805
float d2 = min(
806
min(mClosestKinematic[v2.mVertex[0]].mDistance, mClosestKinematic[v2.mVertex[1]].mDistance),
807
min(mClosestKinematic[v2.mVertex[2]].mDistance, mClosestKinematic[v2.mVertex[3]].mDistance));
808
if (d1 != d2)
809
return d1 < d2;
810
811
// Order constraints so that the ones with the smallest index go first
812
uint32 m1 = v1.GetMinVertexIndex();
813
uint32 m2 = v2.GetMinVertexIndex();
814
if (m1 != m2)
815
return m1 < m2;
816
817
return inLHS < inRHS;
818
});
819
820
// Sort the skinned constraints
821
QuickSort(group.mSkinnedConstraints.begin(), group.mSkinnedConstraints.end(), [this](uint inLHS, uint inRHS)
822
{
823
const Skinned &s1 = mSkinnedConstraints[inLHS];
824
const Skinned &s2 = mSkinnedConstraints[inRHS];
825
826
// 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).
827
if (s1.mVertex != s2.mVertex)
828
return s1.mVertex < s2.mVertex;
829
830
return inLHS < inRHS;
831
});
832
}
833
834
// Temporary store constraints as we reorder them
835
Array<Edge> temp_edges;
836
temp_edges.swap(mEdgeConstraints);
837
mEdgeConstraints.reserve(temp_edges.size());
838
outResults.mEdgeRemap.reserve(temp_edges.size());
839
840
Array<LRA> temp_lra;
841
temp_lra.swap(mLRAConstraints);
842
mLRAConstraints.reserve(temp_lra.size());
843
outResults.mLRARemap.reserve(temp_lra.size());
844
845
Array<DihedralBend> temp_dihedral_bend;
846
temp_dihedral_bend.swap(mDihedralBendConstraints);
847
mDihedralBendConstraints.reserve(temp_dihedral_bend.size());
848
outResults.mDihedralBendRemap.reserve(temp_dihedral_bend.size());
849
850
Array<Volume> temp_volume;
851
temp_volume.swap(mVolumeConstraints);
852
mVolumeConstraints.reserve(temp_volume.size());
853
outResults.mVolumeRemap.reserve(temp_volume.size());
854
855
Array<Skinned> temp_skinned;
856
temp_skinned.swap(mSkinnedConstraints);
857
mSkinnedConstraints.reserve(temp_skinned.size());
858
outResults.mSkinnedRemap.reserve(temp_skinned.size());
859
860
// Finalize update groups
861
for (const Group &group : groups)
862
{
863
// Reorder edge constraints for this group
864
for (uint idx : group.mEdgeConstraints)
865
{
866
mEdgeConstraints.push_back(temp_edges[idx]);
867
outResults.mEdgeRemap.push_back(idx);
868
}
869
870
// Reorder LRA constraints for this group
871
for (uint idx : group.mLRAConstraints)
872
{
873
mLRAConstraints.push_back(temp_lra[idx]);
874
outResults.mLRARemap.push_back(idx);
875
}
876
877
// Reorder dihedral bend constraints for this group
878
for (uint idx : group.mDihedralBendConstraints)
879
{
880
mDihedralBendConstraints.push_back(temp_dihedral_bend[idx]);
881
outResults.mDihedralBendRemap.push_back(idx);
882
}
883
884
// Reorder volume constraints for this group
885
for (uint idx : group.mVolumeConstraints)
886
{
887
mVolumeConstraints.push_back(temp_volume[idx]);
888
outResults.mVolumeRemap.push_back(idx);
889
}
890
891
// Reorder skinned constraints for this group
892
for (uint idx : group.mSkinnedConstraints)
893
{
894
mSkinnedConstraints.push_back(temp_skinned[idx]);
895
outResults.mSkinnedRemap.push_back(idx);
896
}
897
898
// Store end indices
899
mUpdateGroups.push_back({ (uint)mEdgeConstraints.size(), (uint)mLRAConstraints.size(), (uint)mDihedralBendConstraints.size(), (uint)mVolumeConstraints.size(), (uint)mSkinnedConstraints.size() });
900
}
901
902
// Free closest kinematic buffer
903
mClosestKinematic.clear();
904
mClosestKinematic.shrink_to_fit();
905
}
906
907
Ref<SoftBodySharedSettings> SoftBodySharedSettings::Clone() const
908
{
909
Ref<SoftBodySharedSettings> clone = new SoftBodySharedSettings;
910
clone->mVertices = mVertices;
911
clone->mFaces = mFaces;
912
clone->mEdgeConstraints = mEdgeConstraints;
913
clone->mDihedralBendConstraints = mDihedralBendConstraints;
914
clone->mVolumeConstraints = mVolumeConstraints;
915
clone->mSkinnedConstraints = mSkinnedConstraints;
916
clone->mSkinnedConstraintNormals = mSkinnedConstraintNormals;
917
clone->mInvBindMatrices = mInvBindMatrices;
918
clone->mLRAConstraints = mLRAConstraints;
919
clone->mMaterials = mMaterials;
920
clone->mVertexRadius = mVertexRadius;
921
clone->mUpdateGroups = mUpdateGroups;
922
return clone;
923
}
924
925
void SoftBodySharedSettings::SaveBinaryState(StreamOut &inStream) const
926
{
927
inStream.Write(mVertices);
928
inStream.Write(mFaces);
929
inStream.Write(mEdgeConstraints);
930
inStream.Write(mDihedralBendConstraints);
931
inStream.Write(mVolumeConstraints);
932
inStream.Write(mSkinnedConstraints);
933
inStream.Write(mSkinnedConstraintNormals);
934
inStream.Write(mLRAConstraints);
935
inStream.Write(mVertexRadius);
936
inStream.Write(mUpdateGroups);
937
938
// Can't write mInvBindMatrices directly because the class contains padding
939
inStream.Write(mInvBindMatrices, [](const InvBind &inElement, StreamOut &inS) {
940
inS.Write(inElement.mJointIndex);
941
inS.Write(inElement.mInvBind);
942
});
943
}
944
945
void SoftBodySharedSettings::RestoreBinaryState(StreamIn &inStream)
946
{
947
inStream.Read(mVertices);
948
inStream.Read(mFaces);
949
inStream.Read(mEdgeConstraints);
950
inStream.Read(mDihedralBendConstraints);
951
inStream.Read(mVolumeConstraints);
952
inStream.Read(mSkinnedConstraints);
953
inStream.Read(mSkinnedConstraintNormals);
954
inStream.Read(mLRAConstraints);
955
inStream.Read(mVertexRadius);
956
inStream.Read(mUpdateGroups);
957
958
inStream.Read(mInvBindMatrices, [](StreamIn &inS, InvBind &outElement) {
959
inS.Read(outElement.mJointIndex);
960
inS.Read(outElement.mInvBind);
961
});
962
}
963
964
void SoftBodySharedSettings::SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const
965
{
966
SharedSettingsToIDMap::const_iterator settings_iter = ioSettingsMap.find(this);
967
if (settings_iter == ioSettingsMap.end())
968
{
969
// Write settings ID
970
uint32 settings_id = ioSettingsMap.size();
971
ioSettingsMap[this] = settings_id;
972
inStream.Write(settings_id);
973
974
// Write the settings
975
SaveBinaryState(inStream);
976
977
// Write materials
978
StreamUtils::SaveObjectArray(inStream, mMaterials, &ioMaterialMap);
979
}
980
else
981
{
982
// Known settings, just write the ID
983
inStream.Write(settings_iter->second);
984
}
985
}
986
987
SoftBodySharedSettings::SettingsResult SoftBodySharedSettings::sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap)
988
{
989
SettingsResult result;
990
991
// Read settings id
992
uint32 settings_id;
993
inStream.Read(settings_id);
994
if (inStream.IsEOF() || inStream.IsFailed())
995
{
996
result.SetError("Failed to read settings id");
997
return result;
998
}
999
1000
// Check nullptr settings
1001
if (settings_id == ~uint32(0))
1002
{
1003
result.Set(nullptr);
1004
return result;
1005
}
1006
1007
// Check if we already read this settings
1008
if (settings_id < ioSettingsMap.size())
1009
{
1010
result.Set(ioSettingsMap[settings_id]);
1011
return result;
1012
}
1013
1014
// Create new object
1015
Ref<SoftBodySharedSettings> settings = new SoftBodySharedSettings;
1016
1017
// Read state
1018
settings->RestoreBinaryState(inStream);
1019
1020
// Read materials
1021
Result mlresult = StreamUtils::RestoreObjectArray<PhysicsMaterialList>(inStream, ioMaterialMap);
1022
if (mlresult.HasError())
1023
{
1024
result.SetError(mlresult.GetError());
1025
return result;
1026
}
1027
settings->mMaterials = mlresult.Get();
1028
1029
// Add the settings to the map
1030
ioSettingsMap.push_back(settings);
1031
1032
result.Set(settings);
1033
return result;
1034
}
1035
1036
Ref<SoftBodySharedSettings> SoftBodySharedSettings::sCreateCube(uint inGridSize, float inGridSpacing)
1037
{
1038
const Vec3 cOffset = Vec3::sReplicate(-0.5f * inGridSpacing * (inGridSize - 1));
1039
1040
// Create settings
1041
SoftBodySharedSettings *settings = new SoftBodySharedSettings;
1042
for (uint z = 0; z < inGridSize; ++z)
1043
for (uint y = 0; y < inGridSize; ++y)
1044
for (uint x = 0; x < inGridSize; ++x)
1045
{
1046
SoftBodySharedSettings::Vertex v;
1047
(cOffset + Vec3::sReplicate(inGridSpacing) * Vec3(float(x), float(y), float(z))).StoreFloat3(&v.mPosition);
1048
settings->mVertices.push_back(v);
1049
}
1050
1051
// Function to get the vertex index of a point on the cube
1052
auto vertex_index = [inGridSize](uint inX, uint inY, uint inZ)
1053
{
1054
return inX + inY * inGridSize + inZ * inGridSize * inGridSize;
1055
};
1056
1057
// Create edges
1058
for (uint z = 0; z < inGridSize; ++z)
1059
for (uint y = 0; y < inGridSize; ++y)
1060
for (uint x = 0; x < inGridSize; ++x)
1061
{
1062
SoftBodySharedSettings::Edge e;
1063
e.mVertex[0] = vertex_index(x, y, z);
1064
if (x < inGridSize - 1)
1065
{
1066
e.mVertex[1] = vertex_index(x + 1, y, z);
1067
settings->mEdgeConstraints.push_back(e);
1068
}
1069
if (y < inGridSize - 1)
1070
{
1071
e.mVertex[1] = vertex_index(x, y + 1, z);
1072
settings->mEdgeConstraints.push_back(e);
1073
}
1074
if (z < inGridSize - 1)
1075
{
1076
e.mVertex[1] = vertex_index(x, y, z + 1);
1077
settings->mEdgeConstraints.push_back(e);
1078
}
1079
}
1080
settings->CalculateEdgeLengths();
1081
1082
// Tetrahedrons to fill a cube
1083
const int tetra_indices[6][4][3] = {
1084
{ {0, 0, 0}, {0, 1, 1}, {0, 0, 1}, {1, 1, 1} },
1085
{ {0, 0, 0}, {0, 1, 0}, {0, 1, 1}, {1, 1, 1} },
1086
{ {0, 0, 0}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1} },
1087
{ {0, 0, 0}, {1, 0, 1}, {1, 0, 0}, {1, 1, 1} },
1088
{ {0, 0, 0}, {1, 1, 0}, {0, 1, 0}, {1, 1, 1} },
1089
{ {0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {1, 1, 1} }
1090
};
1091
1092
// Create volume constraints
1093
for (uint z = 0; z < inGridSize - 1; ++z)
1094
for (uint y = 0; y < inGridSize - 1; ++y)
1095
for (uint x = 0; x < inGridSize - 1; ++x)
1096
for (uint t = 0; t < 6; ++t)
1097
{
1098
SoftBodySharedSettings::Volume v;
1099
for (uint i = 0; i < 4; ++i)
1100
v.mVertex[i] = vertex_index(x + tetra_indices[t][i][0], y + tetra_indices[t][i][1], z + tetra_indices[t][i][2]);
1101
settings->mVolumeConstraints.push_back(v);
1102
}
1103
1104
settings->CalculateVolumeConstraintVolumes();
1105
1106
// Create faces
1107
for (uint y = 0; y < inGridSize - 1; ++y)
1108
for (uint x = 0; x < inGridSize - 1; ++x)
1109
{
1110
SoftBodySharedSettings::Face f;
1111
1112
// Face 1
1113
f.mVertex[0] = vertex_index(x, y, 0);
1114
f.mVertex[1] = vertex_index(x, y + 1, 0);
1115
f.mVertex[2] = vertex_index(x + 1, y + 1, 0);
1116
settings->AddFace(f);
1117
1118
f.mVertex[1] = vertex_index(x + 1, y + 1, 0);
1119
f.mVertex[2] = vertex_index(x + 1, y, 0);
1120
settings->AddFace(f);
1121
1122
// Face 2
1123
f.mVertex[0] = vertex_index(x, y, inGridSize - 1);
1124
f.mVertex[1] = vertex_index(x + 1, y + 1, inGridSize - 1);
1125
f.mVertex[2] = vertex_index(x, y + 1, inGridSize - 1);
1126
settings->AddFace(f);
1127
1128
f.mVertex[1] = vertex_index(x + 1, y, inGridSize - 1);
1129
f.mVertex[2] = vertex_index(x + 1, y + 1, inGridSize - 1);
1130
settings->AddFace(f);
1131
1132
// Face 3
1133
f.mVertex[0] = vertex_index(x, 0, y);
1134
f.mVertex[1] = vertex_index(x + 1, 0, y + 1);
1135
f.mVertex[2] = vertex_index(x, 0, y + 1);
1136
settings->AddFace(f);
1137
1138
f.mVertex[1] = vertex_index(x + 1, 0, y);
1139
f.mVertex[2] = vertex_index(x + 1, 0, y + 1);
1140
settings->AddFace(f);
1141
1142
// Face 4
1143
f.mVertex[0] = vertex_index(x, inGridSize - 1, y);
1144
f.mVertex[1] = vertex_index(x, inGridSize - 1, y + 1);
1145
f.mVertex[2] = vertex_index(x + 1, inGridSize - 1, y + 1);
1146
settings->AddFace(f);
1147
1148
f.mVertex[1] = vertex_index(x + 1, inGridSize - 1, y + 1);
1149
f.mVertex[2] = vertex_index(x + 1, inGridSize - 1, y);
1150
settings->AddFace(f);
1151
1152
// Face 5
1153
f.mVertex[0] = vertex_index(0, x, y);
1154
f.mVertex[1] = vertex_index(0, x, y + 1);
1155
f.mVertex[2] = vertex_index(0, x + 1, y + 1);
1156
settings->AddFace(f);
1157
1158
f.mVertex[1] = vertex_index(0, x + 1, y + 1);
1159
f.mVertex[2] = vertex_index(0, x + 1, y);
1160
settings->AddFace(f);
1161
1162
// Face 6
1163
f.mVertex[0] = vertex_index(inGridSize - 1, x, y);
1164
f.mVertex[1] = vertex_index(inGridSize - 1, x + 1, y + 1);
1165
f.mVertex[2] = vertex_index(inGridSize - 1, x, y + 1);
1166
settings->AddFace(f);
1167
1168
f.mVertex[1] = vertex_index(inGridSize - 1, x + 1, y);
1169
f.mVertex[2] = vertex_index(inGridSize - 1, x + 1, y + 1);
1170
settings->AddFace(f);
1171
}
1172
1173
// Optimize the settings
1174
settings->Optimize();
1175
1176
return settings;
1177
}
1178
1179
JPH_NAMESPACE_END
1180
1181