SoftBodySharedSettings.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2023 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/SoftBody/SoftBodySharedSettings.h>
  6. #include <Jolt/Physics/SoftBody/SoftBodyUpdateContext.h>
  7. #include <Jolt/ObjectStream/TypeDeclarations.h>
  8. #include <Jolt/Core/StreamIn.h>
  9. #include <Jolt/Core/StreamOut.h>
  10. #include <Jolt/Core/QuickSort.h>
  11. #include <Jolt/Core/UnorderedMap.h>
  12. #include <Jolt/Core/UnorderedSet.h>
  13. JPH_SUPPRESS_WARNINGS_STD_BEGIN
  14. #include <queue>
  15. JPH_SUPPRESS_WARNINGS_STD_END
  16. JPH_NAMESPACE_BEGIN
  17. template<class T, class Container = Array<T>, class Compare = std::less<typename Container::value_type>> using PriorityQueue = std::priority_queue<T, Container, Compare>;
  18. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Vertex)
  19. {
  20. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mPosition)
  21. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mVelocity)
  22. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Vertex, mInvMass)
  23. }
  24. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Face)
  25. {
  26. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mVertex)
  27. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Face, mMaterialIndex)
  28. }
  29. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Edge)
  30. {
  31. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mVertex)
  32. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mRestLength)
  33. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Edge, mCompliance)
  34. }
  35. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::DihedralBend)
  36. {
  37. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mVertex)
  38. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mCompliance)
  39. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::DihedralBend, mInitialAngle)
  40. }
  41. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Volume)
  42. {
  43. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mVertex)
  44. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mSixRestVolume)
  45. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Volume, mCompliance)
  46. }
  47. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::InvBind)
  48. {
  49. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mJointIndex)
  50. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::InvBind, mInvBind)
  51. }
  52. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::SkinWeight)
  53. {
  54. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mInvBindIndex)
  55. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::SkinWeight, mWeight)
  56. }
  57. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::Skinned)
  58. {
  59. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mVertex)
  60. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mWeights)
  61. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mMaxDistance)
  62. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopDistance)
  63. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::Skinned, mBackStopRadius)
  64. }
  65. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings::LRA)
  66. {
  67. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mVertex)
  68. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings::LRA, mMaxDistance)
  69. }
  70. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(SoftBodySharedSettings)
  71. {
  72. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertices)
  73. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mFaces)
  74. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeConstraints)
  75. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mEdgeGroupEndIndices)
  76. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mDihedralBendConstraints)
  77. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVolumeConstraints)
  78. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mSkinnedConstraints)
  79. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mInvBindMatrices)
  80. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mLRAConstraints)
  81. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mMaterials)
  82. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius)
  83. }
  84. void SoftBodySharedSettings::CalculateClosestKinematic()
  85. {
  86. // Check if we already calculated this
  87. if (!mClosestKinematic.empty())
  88. return;
  89. // Reserve output size
  90. mClosestKinematic.resize(mVertices.size());
  91. // Create a list of connected vertices
  92. Array<Array<uint32>> connectivity;
  93. connectivity.resize(mVertices.size());
  94. for (const Edge &e : mEdgeConstraints)
  95. {
  96. connectivity[e.mVertex[0]].push_back(e.mVertex[1]);
  97. connectivity[e.mVertex[1]].push_back(e.mVertex[0]);
  98. }
  99. // Use Dijkstra's algorithm to find the closest kinematic vertex for each vertex
  100. // See: https://en.wikipedia.org/wiki/Dijkstra's_algorithm
  101. //
  102. // An element in the open list
  103. struct Open
  104. {
  105. // Order so that we get the shortest distance first
  106. bool operator < (const Open &inRHS) const
  107. {
  108. return mDistance > inRHS.mDistance;
  109. }
  110. uint32 mVertex;
  111. float mDistance;
  112. };
  113. // Start with all kinematic elements
  114. PriorityQueue<Open> to_visit;
  115. for (uint32 v = 0; v < mVertices.size(); ++v)
  116. if (mVertices[v].mInvMass == 0.0f)
  117. {
  118. mClosestKinematic[v].mVertex = v;
  119. mClosestKinematic[v].mDistance = 0.0f;
  120. to_visit.push({ v, 0.0f });
  121. }
  122. // Visit all vertices remembering the closest kinematic vertex and its distance
  123. while (!to_visit.empty())
  124. {
  125. // Pop element from the open list
  126. Open current = to_visit.top();
  127. to_visit.pop();
  128. // Loop through all of its connected vertices
  129. for (uint32 v : connectivity[current.mVertex])
  130. {
  131. // Calculate distance from the current vertex to this target vertex and check if it is smaller
  132. float new_distance = current.mDistance + (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current.mVertex].mPosition)).Length();
  133. if (new_distance < mClosestKinematic[v].mDistance)
  134. {
  135. // Remember new closest vertex
  136. mClosestKinematic[v].mVertex = mClosestKinematic[current.mVertex].mVertex;
  137. mClosestKinematic[v].mDistance = new_distance;
  138. to_visit.push({ v, new_distance });
  139. }
  140. }
  141. }
  142. }
  143. void SoftBodySharedSettings::CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType, float inAngleTolerance)
  144. {
  145. struct EdgeHelper
  146. {
  147. uint32 mVertex[2];
  148. uint32 mEdgeIdx;
  149. };
  150. // Create list of all edges
  151. Array<EdgeHelper> edges;
  152. edges.reserve(mFaces.size() * 3);
  153. for (const Face &f : mFaces)
  154. for (int i = 0; i < 3; ++i)
  155. {
  156. uint32 v0 = f.mVertex[i];
  157. uint32 v1 = f.mVertex[(i + 1) % 3];
  158. EdgeHelper e;
  159. e.mVertex[0] = min(v0, v1);
  160. e.mVertex[1] = max(v0, v1);
  161. e.mEdgeIdx = uint32(&f - mFaces.data()) * 3 + i;
  162. edges.push_back(e);
  163. }
  164. // Sort the edges
  165. 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]); });
  166. // Only add edges if one of the vertices is movable
  167. auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inCompliance1, float inCompliance2) {
  168. if ((mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f)
  169. && inCompliance1 < FLT_MAX && inCompliance2 < FLT_MAX)
  170. {
  171. Edge temp_edge;
  172. temp_edge.mVertex[0] = inVtx1;
  173. temp_edge.mVertex[1] = inVtx2;
  174. temp_edge.mCompliance = 0.5f * (inCompliance1 + inCompliance2);
  175. temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length();
  176. JPH_ASSERT(temp_edge.mRestLength > 0.0f);
  177. mEdgeConstraints.push_back(temp_edge);
  178. }
  179. };
  180. // Helper function to get the attributes of a vertex
  181. auto attr = [inVertexAttributes, inVertexAttributesLength](uint32 inVertex) {
  182. return inVertexAttributes[min(inVertex, inVertexAttributesLength - 1)];
  183. };
  184. // Create the constraints
  185. float sq_sin_tolerance = Square(Sin(inAngleTolerance));
  186. float sq_cos_tolerance = Square(Cos(inAngleTolerance));
  187. mEdgeConstraints.clear();
  188. mEdgeConstraints.reserve(edges.size());
  189. for (Array<EdgeHelper>::size_type i = 0; i < edges.size(); ++i)
  190. {
  191. const EdgeHelper &e0 = edges[i];
  192. // Get attributes for the vertices of the edge
  193. const VertexAttributes &a0 = attr(e0.mVertex[0]);
  194. const VertexAttributes &a1 = attr(e0.mVertex[1]);
  195. // 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)
  196. bool is_shear = false;
  197. // Test if there are any shared edges
  198. for (Array<EdgeHelper>::size_type j = i + 1; j < edges.size(); ++j)
  199. {
  200. const EdgeHelper &e1 = edges[j];
  201. if (e0.mVertex[0] == e1.mVertex[0] && e0.mVertex[1] == e1.mVertex[1])
  202. {
  203. // Get opposing vertices
  204. const Face &f0 = mFaces[e0.mEdgeIdx / 3];
  205. const Face &f1 = mFaces[e1.mEdgeIdx / 3];
  206. uint32 vopposite0 = f0.mVertex[(e0.mEdgeIdx + 2) % 3];
  207. uint32 vopposite1 = f1.mVertex[(e1.mEdgeIdx + 2) % 3];
  208. const VertexAttributes &a_opposite0 = attr(vopposite0);
  209. const VertexAttributes &a_opposite1 = attr(vopposite1);
  210. // Faces should be roughly in a plane
  211. 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));
  212. 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));
  213. if (Square(n0.Dot(n1)) > sq_cos_tolerance * n0.LengthSq() * n1.LengthSq())
  214. {
  215. // Faces should approximately form a quad
  216. Vec3 e0_dir = Vec3(mVertices[vopposite0].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
  217. Vec3 e1_dir = Vec3(mVertices[vopposite1].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
  218. if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq())
  219. {
  220. // Shear constraint
  221. add_edge(vopposite0, vopposite1, a_opposite0.mShearCompliance, a_opposite1.mShearCompliance);
  222. is_shear = true;
  223. }
  224. }
  225. // Bend constraint
  226. switch (inBendType)
  227. {
  228. case EBendType::None:
  229. // Do nothing
  230. break;
  231. case EBendType::Distance:
  232. // Create an edge constraint to represent the bend constraint
  233. // Use the bend compliance of the shared edge
  234. if (!is_shear)
  235. add_edge(vopposite0, vopposite1, a0.mBendCompliance, a1.mBendCompliance);
  236. break;
  237. case EBendType::Dihedral:
  238. // Test if both opposite vertices are free to move
  239. if ((mVertices[vopposite0].mInvMass > 0.0f || mVertices[vopposite1].mInvMass > 0.0f)
  240. && a0.mBendCompliance < FLT_MAX && a1.mBendCompliance < FLT_MAX)
  241. {
  242. // Create a bend constraint
  243. // Use the bend compliance of the shared edge
  244. mDihedralBendConstraints.emplace_back(e0.mVertex[0], e0.mVertex[1], vopposite0, vopposite1, 0.5f * (a0.mBendCompliance + a1.mBendCompliance));
  245. }
  246. break;
  247. }
  248. }
  249. else
  250. {
  251. // Start iterating from the first non-shared edge
  252. i = j - 1;
  253. break;
  254. }
  255. }
  256. // Create a edge constraint for the current edge
  257. add_edge(e0.mVertex[0], e0.mVertex[1], is_shear? a0.mShearCompliance : a0.mCompliance, is_shear? a1.mShearCompliance : a1.mCompliance);
  258. }
  259. mEdgeConstraints.shrink_to_fit();
  260. // Calculate the initial angle for all bend constraints
  261. CalculateBendConstraintConstants();
  262. // Check if any vertices have LRA constraints
  263. bool has_lra_constraints = false;
  264. for (const VertexAttributes *va = inVertexAttributes; va < inVertexAttributes + inVertexAttributesLength; ++va)
  265. if (va->mLRAType != ELRAType::None)
  266. {
  267. has_lra_constraints = true;
  268. break;
  269. }
  270. if (has_lra_constraints)
  271. {
  272. // Ensure we have calculated the closest kinematic vertex for each vertex
  273. CalculateClosestKinematic();
  274. // Find non-kinematic vertices
  275. for (uint32 v = 0; v < (uint32)mVertices.size(); ++v)
  276. if (mVertices[v].mInvMass > 0.0f)
  277. {
  278. // Check if a closest vertex was found
  279. uint32 closest = mClosestKinematic[v].mVertex;
  280. if (closest != 0xffffffff)
  281. {
  282. // Check which LRA constraint to create
  283. const VertexAttributes &va = attr(v);
  284. switch (va.mLRAType)
  285. {
  286. case ELRAType::None:
  287. break;
  288. case ELRAType::EuclideanDistance:
  289. mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * (Vec3(mVertices[closest].mPosition) - Vec3(mVertices[v].mPosition)).Length());
  290. break;
  291. case ELRAType::GeodesicDistance:
  292. mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * mClosestKinematic[v].mDistance);
  293. break;
  294. }
  295. }
  296. }
  297. }
  298. }
  299. void SoftBodySharedSettings::CalculateEdgeLengths()
  300. {
  301. for (Edge &e : mEdgeConstraints)
  302. {
  303. e.mRestLength = (Vec3(mVertices[e.mVertex[1]].mPosition) - Vec3(mVertices[e.mVertex[0]].mPosition)).Length();
  304. JPH_ASSERT(e.mRestLength > 0.0f);
  305. }
  306. }
  307. void SoftBodySharedSettings::CalculateLRALengths(float inMaxDistanceMultiplier)
  308. {
  309. for (LRA &l : mLRAConstraints)
  310. {
  311. l.mMaxDistance = inMaxDistanceMultiplier * (Vec3(mVertices[l.mVertex[1]].mPosition) - Vec3(mVertices[l.mVertex[0]].mPosition)).Length();
  312. JPH_ASSERT(l.mMaxDistance > 0.0f);
  313. }
  314. }
  315. void SoftBodySharedSettings::CalculateBendConstraintConstants()
  316. {
  317. for (DihedralBend &b : mDihedralBendConstraints)
  318. {
  319. // Get positions
  320. Vec3 x0 = Vec3(mVertices[b.mVertex[0]].mPosition);
  321. Vec3 x1 = Vec3(mVertices[b.mVertex[1]].mPosition);
  322. Vec3 x2 = Vec3(mVertices[b.mVertex[2]].mPosition);
  323. Vec3 x3 = Vec3(mVertices[b.mVertex[3]].mPosition);
  324. /*
  325. x2
  326. e1/ \e3
  327. / \
  328. x0----x1
  329. \ e0 /
  330. e2\ /e4
  331. x3
  332. */
  333. // Calculate edges
  334. Vec3 e0 = x1 - x0;
  335. Vec3 e1 = x2 - x0;
  336. Vec3 e2 = x3 - x0;
  337. // Normals of both triangles
  338. Vec3 n1 = e0.Cross(e1);
  339. Vec3 n2 = e2.Cross(e0);
  340. float denom = sqrt(n1.LengthSq() * n2.LengthSq());
  341. if (denom < 1.0e-12f)
  342. b.mInitialAngle = 0.0f;
  343. else
  344. {
  345. float sign = Sign(n2.Cross(n1).Dot(e0));
  346. b.mInitialAngle = sign * ACos(n1.Dot(n2) / denom);
  347. }
  348. }
  349. }
  350. void SoftBodySharedSettings::CalculateVolumeConstraintVolumes()
  351. {
  352. for (Volume &v : mVolumeConstraints)
  353. {
  354. Vec3 x1(mVertices[v.mVertex[0]].mPosition);
  355. Vec3 x2(mVertices[v.mVertex[1]].mPosition);
  356. Vec3 x3(mVertices[v.mVertex[2]].mPosition);
  357. Vec3 x4(mVertices[v.mVertex[3]].mPosition);
  358. Vec3 x1x2 = x2 - x1;
  359. Vec3 x1x3 = x3 - x1;
  360. Vec3 x1x4 = x4 - x1;
  361. v.mSixRestVolume = abs(x1x2.Cross(x1x3).Dot(x1x4));
  362. }
  363. }
  364. void SoftBodySharedSettings::CalculateSkinnedConstraintNormals()
  365. {
  366. // Clear any previous results
  367. mSkinnedConstraintNormals.clear();
  368. // If there are no skinned constraints, we're done
  369. if (mSkinnedConstraints.empty())
  370. return;
  371. // First collect all vertices that are skinned
  372. UnorderedSet<uint32> skinned_vertices;
  373. skinned_vertices.reserve(mSkinnedConstraints.size());
  374. for (const Skinned &s : mSkinnedConstraints)
  375. skinned_vertices.insert(s.mVertex);
  376. // Now collect all faces that connect only to skinned vertices
  377. UnorderedMap<uint32, UnorderedSet<uint32>> connected_faces;
  378. connected_faces.reserve(mVertices.size());
  379. for (const Face &f : mFaces)
  380. {
  381. // Must connect to only skinned vertices
  382. bool valid = true;
  383. for (uint32 v : f.mVertex)
  384. valid &= skinned_vertices.find(v) != skinned_vertices.end();
  385. if (!valid)
  386. continue;
  387. // Store faces that connect to vertices
  388. for (uint32 v : f.mVertex)
  389. connected_faces[v].insert(uint32(&f - mFaces.data()));
  390. }
  391. // Populate the list of connecting faces per skinned vertex
  392. mSkinnedConstraintNormals.reserve(mFaces.size());
  393. for (Skinned &s : mSkinnedConstraints)
  394. {
  395. uint32 start = uint32(mSkinnedConstraintNormals.size());
  396. JPH_ASSERT((start >> 24) == 0);
  397. const UnorderedSet<uint32> &faces = connected_faces[s.mVertex];
  398. uint32 num = uint32(faces.size());
  399. JPH_ASSERT(num < 256);
  400. mSkinnedConstraintNormals.insert(mSkinnedConstraintNormals.end(), faces.begin(), faces.end());
  401. QuickSort(mSkinnedConstraintNormals.begin() + start, mSkinnedConstraintNormals.begin() + start + num);
  402. s.mNormalInfo = start + (num << 24);
  403. }
  404. mSkinnedConstraintNormals.shrink_to_fit();
  405. }
  406. void SoftBodySharedSettings::Optimize(OptimizationResults &outResults)
  407. {
  408. const uint cMaxNumGroups = 32;
  409. const uint cNonParallelGroupIdx = cMaxNumGroups - 1;
  410. const uint cMinimumSize = 2 * SoftBodyUpdateContext::cEdgeConstraintBatch; // There should be at least 2 batches, otherwise there's no point in parallelizing
  411. // Assign edges to non-overlapping groups
  412. Array<uint32> masks;
  413. masks.resize(mVertices.size(), 0);
  414. Array<uint> edge_groups[cMaxNumGroups];
  415. for (const Edge &e : mEdgeConstraints)
  416. {
  417. uint32 &mask1 = masks[e.mVertex[0]];
  418. uint32 &mask2 = masks[e.mVertex[1]];
  419. uint group = min(CountTrailingZeros((~mask1) & (~mask2)), cNonParallelGroupIdx);
  420. uint32 mask = uint32(1U << group);
  421. mask1 |= mask;
  422. mask2 |= mask;
  423. edge_groups[group].push_back(uint(&e - mEdgeConstraints.data()));
  424. }
  425. // Merge groups that are too small into the non-parallel group
  426. for (uint i = 0; i < cNonParallelGroupIdx; ++i)
  427. if (edge_groups[i].size() < cMinimumSize)
  428. {
  429. edge_groups[cNonParallelGroupIdx].insert(edge_groups[cNonParallelGroupIdx].end(), edge_groups[i].begin(), edge_groups[i].end());
  430. edge_groups[i].clear();
  431. }
  432. // Make sure we know the closest kinematic vertex so we can sort
  433. CalculateClosestKinematic();
  434. // Sort the edge constraints
  435. for (Array<uint> &group : edge_groups)
  436. QuickSort(group.begin(), group.end(), [this](uint inLHS, uint inRHS)
  437. {
  438. const Edge &e1 = mEdgeConstraints[inLHS];
  439. const Edge &e2 = mEdgeConstraints[inRHS];
  440. // First sort so that the edge with the smallest distance to a kinematic vertex comes first
  441. float d1 = min(mClosestKinematic[e1.mVertex[0]].mDistance, mClosestKinematic[e1.mVertex[1]].mDistance);
  442. float d2 = min(mClosestKinematic[e2.mVertex[0]].mDistance, mClosestKinematic[e2.mVertex[1]].mDistance);
  443. if (d1 != d2)
  444. return d1 < d2;
  445. // Order the edges so that the ones with the smallest index go first (hoping to get better cache locality when we process the edges).
  446. // Note we could also re-order the vertices but that would be much more of a burden to the end user
  447. return e1.GetMinVertexIndex() < e2.GetMinVertexIndex();
  448. });
  449. // Assign the edges to groups and reorder them
  450. Array<Edge> temp_edges;
  451. temp_edges.swap(mEdgeConstraints);
  452. mEdgeConstraints.reserve(temp_edges.size());
  453. for (const Array<uint> &group : edge_groups)
  454. if (!group.empty())
  455. {
  456. for (uint idx : group)
  457. {
  458. mEdgeConstraints.push_back(temp_edges[idx]);
  459. outResults.mEdgeRemap.push_back(idx);
  460. }
  461. mEdgeGroupEndIndices.push_back((uint)mEdgeConstraints.size());
  462. }
  463. // If there is no non-parallel group then add an empty group at the end
  464. if (edge_groups[cNonParallelGroupIdx].empty())
  465. mEdgeGroupEndIndices.push_back((uint)mEdgeConstraints.size());
  466. // Sort the bend constraints
  467. outResults.mDihedralBendRemap.resize(mDihedralBendConstraints.size());
  468. for (int i = 0; i < (int)mDihedralBendConstraints.size(); ++i)
  469. outResults.mDihedralBendRemap[i] = i;
  470. QuickSort(outResults.mDihedralBendRemap.begin(), outResults.mDihedralBendRemap.end(), [this](uint inLHS, uint inRHS)
  471. {
  472. const DihedralBend &b1 = mDihedralBendConstraints[inLHS];
  473. const DihedralBend &b2 = mDihedralBendConstraints[inRHS];
  474. // First sort so that the constraint with the smallest distance to a kinematic vertex comes first
  475. float d1 = min(
  476. min(mClosestKinematic[b1.mVertex[0]].mDistance, mClosestKinematic[b1.mVertex[1]].mDistance),
  477. min(mClosestKinematic[b1.mVertex[2]].mDistance, mClosestKinematic[b1.mVertex[3]].mDistance));
  478. float d2 = min(
  479. min(mClosestKinematic[b2.mVertex[0]].mDistance, mClosestKinematic[b2.mVertex[1]].mDistance),
  480. min(mClosestKinematic[b2.mVertex[2]].mDistance, mClosestKinematic[b2.mVertex[3]].mDistance));
  481. if (d1 != d2)
  482. return d1 < d2;
  483. // Order constraints so that the ones with the smallest index go first
  484. return b1.GetMinVertexIndex() < b2.GetMinVertexIndex();
  485. });
  486. // Reorder the bend constraints
  487. Array<DihedralBend> temp_bends;
  488. temp_bends.swap(mDihedralBendConstraints);
  489. mDihedralBendConstraints.reserve(temp_bends.size());
  490. for (uint idx : outResults.mDihedralBendRemap)
  491. mDihedralBendConstraints.push_back(temp_bends[idx]);
  492. // Free closest kinematic buffer
  493. mClosestKinematic.clear();
  494. mClosestKinematic.shrink_to_fit();
  495. }
  496. Ref<SoftBodySharedSettings> SoftBodySharedSettings::Clone() const
  497. {
  498. Ref<SoftBodySharedSettings> clone = new SoftBodySharedSettings;
  499. clone->mVertices = mVertices;
  500. clone->mFaces = mFaces;
  501. clone->mEdgeConstraints = mEdgeConstraints;
  502. clone->mEdgeGroupEndIndices = mEdgeGroupEndIndices;
  503. clone->mDihedralBendConstraints = mDihedralBendConstraints;
  504. clone->mVolumeConstraints = mVolumeConstraints;
  505. clone->mSkinnedConstraints = mSkinnedConstraints;
  506. clone->mSkinnedConstraintNormals = mSkinnedConstraintNormals;
  507. clone->mInvBindMatrices = mInvBindMatrices;
  508. clone->mLRAConstraints = mLRAConstraints;
  509. clone->mMaterials = mMaterials;
  510. clone->mVertexRadius = mVertexRadius;
  511. return clone;
  512. }
  513. void SoftBodySharedSettings::SaveBinaryState(StreamOut &inStream) const
  514. {
  515. inStream.Write(mVertices);
  516. inStream.Write(mFaces);
  517. inStream.Write(mEdgeConstraints);
  518. inStream.Write(mEdgeGroupEndIndices);
  519. inStream.Write(mDihedralBendConstraints);
  520. inStream.Write(mVolumeConstraints);
  521. inStream.Write(mSkinnedConstraints);
  522. inStream.Write(mSkinnedConstraintNormals);
  523. inStream.Write(mLRAConstraints);
  524. inStream.Write(mVertexRadius);
  525. // Can't write mInvBindMatrices directly because the class contains padding
  526. inStream.Write(mInvBindMatrices, [](const InvBind &inElement, StreamOut &inS) {
  527. inS.Write(inElement.mJointIndex);
  528. inS.Write(inElement.mInvBind);
  529. });
  530. }
  531. void SoftBodySharedSettings::RestoreBinaryState(StreamIn &inStream)
  532. {
  533. inStream.Read(mVertices);
  534. inStream.Read(mFaces);
  535. inStream.Read(mEdgeConstraints);
  536. inStream.Read(mEdgeGroupEndIndices);
  537. inStream.Read(mDihedralBendConstraints);
  538. inStream.Read(mVolumeConstraints);
  539. inStream.Read(mSkinnedConstraints);
  540. inStream.Read(mSkinnedConstraintNormals);
  541. inStream.Read(mLRAConstraints);
  542. inStream.Read(mVertexRadius);
  543. inStream.Read(mInvBindMatrices, [](StreamIn &inS, InvBind &outElement) {
  544. inS.Read(outElement.mJointIndex);
  545. inS.Read(outElement.mInvBind);
  546. });
  547. }
  548. void SoftBodySharedSettings::SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const
  549. {
  550. SharedSettingsToIDMap::const_iterator settings_iter = ioSettingsMap.find(this);
  551. if (settings_iter == ioSettingsMap.end())
  552. {
  553. // Write settings ID
  554. uint32 settings_id = (uint32)ioSettingsMap.size();
  555. ioSettingsMap[this] = settings_id;
  556. inStream.Write(settings_id);
  557. // Write the settings
  558. SaveBinaryState(inStream);
  559. // Write materials
  560. StreamUtils::SaveObjectArray(inStream, mMaterials, &ioMaterialMap);
  561. }
  562. else
  563. {
  564. // Known settings, just write the ID
  565. inStream.Write(settings_iter->second);
  566. }
  567. }
  568. SoftBodySharedSettings::SettingsResult SoftBodySharedSettings::sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap)
  569. {
  570. SettingsResult result;
  571. // Read settings id
  572. uint32 settings_id;
  573. inStream.Read(settings_id);
  574. if (inStream.IsEOF() || inStream.IsFailed())
  575. {
  576. result.SetError("Failed to read settings id");
  577. return result;
  578. }
  579. // Check nullptr settings
  580. if (settings_id == ~uint32(0))
  581. {
  582. result.Set(nullptr);
  583. return result;
  584. }
  585. // Check if we already read this settings
  586. if (settings_id < ioSettingsMap.size())
  587. {
  588. result.Set(ioSettingsMap[settings_id]);
  589. return result;
  590. }
  591. // Create new object
  592. Ref<SoftBodySharedSettings> settings = new SoftBodySharedSettings;
  593. // Read state
  594. settings->RestoreBinaryState(inStream);
  595. // Read materials
  596. Result mlresult = StreamUtils::RestoreObjectArray<PhysicsMaterialList>(inStream, ioMaterialMap);
  597. if (mlresult.HasError())
  598. {
  599. result.SetError(mlresult.GetError());
  600. return result;
  601. }
  602. settings->mMaterials = mlresult.Get();
  603. // Add the settings to the map
  604. ioSettingsMap.push_back(settings);
  605. result.Set(settings);
  606. return result;
  607. }
  608. JPH_NAMESPACE_END