SoftBodySharedSettings.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  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, mDihedralBendConstraints)
  76. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVolumeConstraints)
  77. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mSkinnedConstraints)
  78. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mInvBindMatrices)
  79. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mLRAConstraints)
  80. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mMaterials)
  81. JPH_ADD_ATTRIBUTE(SoftBodySharedSettings, mVertexRadius)
  82. }
  83. void SoftBodySharedSettings::CalculateClosestKinematic()
  84. {
  85. // Check if we already calculated this
  86. if (!mClosestKinematic.empty())
  87. return;
  88. // Reserve output size
  89. mClosestKinematic.resize(mVertices.size());
  90. // Create a list of connected vertices
  91. Array<Array<uint32>> connectivity;
  92. connectivity.resize(mVertices.size());
  93. for (const Edge &e : mEdgeConstraints)
  94. {
  95. connectivity[e.mVertex[0]].push_back(e.mVertex[1]);
  96. connectivity[e.mVertex[1]].push_back(e.mVertex[0]);
  97. }
  98. // Use Dijkstra's algorithm to find the closest kinematic vertex for each vertex
  99. // See: https://en.wikipedia.org/wiki/Dijkstra's_algorithm
  100. //
  101. // An element in the open list
  102. struct Open
  103. {
  104. // Order so that we get the shortest distance first
  105. bool operator < (const Open &inRHS) const
  106. {
  107. return mDistance > inRHS.mDistance;
  108. }
  109. uint32 mVertex;
  110. float mDistance;
  111. };
  112. // Start with all kinematic elements
  113. PriorityQueue<Open> to_visit;
  114. for (uint32 v = 0; v < mVertices.size(); ++v)
  115. if (mVertices[v].mInvMass == 0.0f)
  116. {
  117. mClosestKinematic[v].mVertex = v;
  118. mClosestKinematic[v].mDistance = 0.0f;
  119. to_visit.push({ v, 0.0f });
  120. }
  121. // Visit all vertices remembering the closest kinematic vertex and its distance
  122. while (!to_visit.empty())
  123. {
  124. // Pop element from the open list
  125. Open current = to_visit.top();
  126. to_visit.pop();
  127. // Loop through all of its connected vertices
  128. for (uint32 v : connectivity[current.mVertex])
  129. {
  130. // Calculate distance from the current vertex to this target vertex and check if it is smaller
  131. float new_distance = current.mDistance + (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current.mVertex].mPosition)).Length();
  132. if (new_distance < mClosestKinematic[v].mDistance)
  133. {
  134. // Remember new closest vertex
  135. mClosestKinematic[v].mVertex = mClosestKinematic[current.mVertex].mVertex;
  136. mClosestKinematic[v].mDistance = new_distance;
  137. to_visit.push({ v, new_distance });
  138. }
  139. }
  140. }
  141. }
  142. void SoftBodySharedSettings::CreateConstraints(const VertexAttributes *inVertexAttributes, uint inVertexAttributesLength, EBendType inBendType, float inAngleTolerance)
  143. {
  144. struct EdgeHelper
  145. {
  146. uint32 mVertex[2];
  147. uint32 mEdgeIdx;
  148. };
  149. // Create list of all edges
  150. Array<EdgeHelper> edges;
  151. edges.reserve(mFaces.size() * 3);
  152. for (const Face &f : mFaces)
  153. for (int i = 0; i < 3; ++i)
  154. {
  155. uint32 v0 = f.mVertex[i];
  156. uint32 v1 = f.mVertex[(i + 1) % 3];
  157. EdgeHelper e;
  158. e.mVertex[0] = min(v0, v1);
  159. e.mVertex[1] = max(v0, v1);
  160. e.mEdgeIdx = uint32(&f - mFaces.data()) * 3 + i;
  161. edges.push_back(e);
  162. }
  163. // Sort the edges
  164. 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]); });
  165. // Only add edges if one of the vertices is movable
  166. auto add_edge = [this](uint32 inVtx1, uint32 inVtx2, float inCompliance1, float inCompliance2) {
  167. if ((mVertices[inVtx1].mInvMass > 0.0f || mVertices[inVtx2].mInvMass > 0.0f)
  168. && inCompliance1 < FLT_MAX && inCompliance2 < FLT_MAX)
  169. {
  170. Edge temp_edge;
  171. temp_edge.mVertex[0] = inVtx1;
  172. temp_edge.mVertex[1] = inVtx2;
  173. temp_edge.mCompliance = 0.5f * (inCompliance1 + inCompliance2);
  174. temp_edge.mRestLength = (Vec3(mVertices[inVtx2].mPosition) - Vec3(mVertices[inVtx1].mPosition)).Length();
  175. JPH_ASSERT(temp_edge.mRestLength > 0.0f);
  176. mEdgeConstraints.push_back(temp_edge);
  177. }
  178. };
  179. // Helper function to get the attributes of a vertex
  180. auto attr = [inVertexAttributes, inVertexAttributesLength](uint32 inVertex) {
  181. return inVertexAttributes[min(inVertex, inVertexAttributesLength - 1)];
  182. };
  183. // Create the constraints
  184. float sq_sin_tolerance = Square(Sin(inAngleTolerance));
  185. float sq_cos_tolerance = Square(Cos(inAngleTolerance));
  186. mEdgeConstraints.clear();
  187. mEdgeConstraints.reserve(edges.size());
  188. for (Array<EdgeHelper>::size_type i = 0; i < edges.size(); ++i)
  189. {
  190. const EdgeHelper &e0 = edges[i];
  191. // Get attributes for the vertices of the edge
  192. const VertexAttributes &a0 = attr(e0.mVertex[0]);
  193. const VertexAttributes &a1 = attr(e0.mVertex[1]);
  194. // 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)
  195. bool is_shear = false;
  196. // Test if there are any shared edges
  197. for (Array<EdgeHelper>::size_type j = i + 1; j < edges.size(); ++j)
  198. {
  199. const EdgeHelper &e1 = edges[j];
  200. if (e0.mVertex[0] == e1.mVertex[0] && e0.mVertex[1] == e1.mVertex[1])
  201. {
  202. // Get opposing vertices
  203. const Face &f0 = mFaces[e0.mEdgeIdx / 3];
  204. const Face &f1 = mFaces[e1.mEdgeIdx / 3];
  205. uint32 vopposite0 = f0.mVertex[(e0.mEdgeIdx + 2) % 3];
  206. uint32 vopposite1 = f1.mVertex[(e1.mEdgeIdx + 2) % 3];
  207. const VertexAttributes &a_opposite0 = attr(vopposite0);
  208. const VertexAttributes &a_opposite1 = attr(vopposite1);
  209. // Faces should be roughly in a plane
  210. 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));
  211. 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));
  212. if (Square(n0.Dot(n1)) > sq_cos_tolerance * n0.LengthSq() * n1.LengthSq())
  213. {
  214. // Faces should approximately form a quad
  215. Vec3 e0_dir = Vec3(mVertices[vopposite0].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
  216. Vec3 e1_dir = Vec3(mVertices[vopposite1].mPosition) - Vec3(mVertices[e0.mVertex[0]].mPosition);
  217. if (Square(e0_dir.Dot(e1_dir)) < sq_sin_tolerance * e0_dir.LengthSq() * e1_dir.LengthSq())
  218. {
  219. // Shear constraint
  220. add_edge(vopposite0, vopposite1, a_opposite0.mShearCompliance, a_opposite1.mShearCompliance);
  221. is_shear = true;
  222. }
  223. }
  224. // Bend constraint
  225. switch (inBendType)
  226. {
  227. case EBendType::None:
  228. // Do nothing
  229. break;
  230. case EBendType::Distance:
  231. // Create an edge constraint to represent the bend constraint
  232. // Use the bend compliance of the shared edge
  233. if (!is_shear)
  234. add_edge(vopposite0, vopposite1, a0.mBendCompliance, a1.mBendCompliance);
  235. break;
  236. case EBendType::Dihedral:
  237. // Test if both opposite vertices are free to move
  238. if ((mVertices[vopposite0].mInvMass > 0.0f || mVertices[vopposite1].mInvMass > 0.0f)
  239. && a0.mBendCompliance < FLT_MAX && a1.mBendCompliance < FLT_MAX)
  240. {
  241. // Create a bend constraint
  242. // Use the bend compliance of the shared edge
  243. mDihedralBendConstraints.emplace_back(e0.mVertex[0], e0.mVertex[1], vopposite0, vopposite1, 0.5f * (a0.mBendCompliance + a1.mBendCompliance));
  244. }
  245. break;
  246. }
  247. }
  248. else
  249. {
  250. // Start iterating from the first non-shared edge
  251. i = j - 1;
  252. break;
  253. }
  254. }
  255. // Create a edge constraint for the current edge
  256. add_edge(e0.mVertex[0], e0.mVertex[1], is_shear? a0.mShearCompliance : a0.mCompliance, is_shear? a1.mShearCompliance : a1.mCompliance);
  257. }
  258. mEdgeConstraints.shrink_to_fit();
  259. // Calculate the initial angle for all bend constraints
  260. CalculateBendConstraintConstants();
  261. // Check if any vertices have LRA constraints
  262. bool has_lra_constraints = false;
  263. for (const VertexAttributes *va = inVertexAttributes; va < inVertexAttributes + inVertexAttributesLength; ++va)
  264. if (va->mLRAType != ELRAType::None)
  265. {
  266. has_lra_constraints = true;
  267. break;
  268. }
  269. if (has_lra_constraints)
  270. {
  271. // Ensure we have calculated the closest kinematic vertex for each vertex
  272. CalculateClosestKinematic();
  273. // Find non-kinematic vertices
  274. for (uint32 v = 0; v < (uint32)mVertices.size(); ++v)
  275. if (mVertices[v].mInvMass > 0.0f)
  276. {
  277. // Check if a closest vertex was found
  278. uint32 closest = mClosestKinematic[v].mVertex;
  279. if (closest != 0xffffffff)
  280. {
  281. // Check which LRA constraint to create
  282. const VertexAttributes &va = attr(v);
  283. switch (va.mLRAType)
  284. {
  285. case ELRAType::None:
  286. break;
  287. case ELRAType::EuclideanDistance:
  288. mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * (Vec3(mVertices[closest].mPosition) - Vec3(mVertices[v].mPosition)).Length());
  289. break;
  290. case ELRAType::GeodesicDistance:
  291. mLRAConstraints.emplace_back(closest, v, va.mLRAMaxDistanceMultiplier * mClosestKinematic[v].mDistance);
  292. break;
  293. }
  294. }
  295. }
  296. }
  297. }
  298. void SoftBodySharedSettings::CalculateEdgeLengths()
  299. {
  300. for (Edge &e : mEdgeConstraints)
  301. {
  302. e.mRestLength = (Vec3(mVertices[e.mVertex[1]].mPosition) - Vec3(mVertices[e.mVertex[0]].mPosition)).Length();
  303. JPH_ASSERT(e.mRestLength > 0.0f);
  304. }
  305. }
  306. void SoftBodySharedSettings::CalculateLRALengths(float inMaxDistanceMultiplier)
  307. {
  308. for (LRA &l : mLRAConstraints)
  309. {
  310. l.mMaxDistance = inMaxDistanceMultiplier * (Vec3(mVertices[l.mVertex[1]].mPosition) - Vec3(mVertices[l.mVertex[0]].mPosition)).Length();
  311. JPH_ASSERT(l.mMaxDistance > 0.0f);
  312. }
  313. }
  314. void SoftBodySharedSettings::CalculateBendConstraintConstants()
  315. {
  316. for (DihedralBend &b : mDihedralBendConstraints)
  317. {
  318. // Get positions
  319. Vec3 x0 = Vec3(mVertices[b.mVertex[0]].mPosition);
  320. Vec3 x1 = Vec3(mVertices[b.mVertex[1]].mPosition);
  321. Vec3 x2 = Vec3(mVertices[b.mVertex[2]].mPosition);
  322. Vec3 x3 = Vec3(mVertices[b.mVertex[3]].mPosition);
  323. /*
  324. x2
  325. e1/ \e3
  326. / \
  327. x0----x1
  328. \ e0 /
  329. e2\ /e4
  330. x3
  331. */
  332. // Calculate edges
  333. Vec3 e0 = x1 - x0;
  334. Vec3 e1 = x2 - x0;
  335. Vec3 e2 = x3 - x0;
  336. // Normals of both triangles
  337. Vec3 n1 = e0.Cross(e1);
  338. Vec3 n2 = e2.Cross(e0);
  339. float denom = sqrt(n1.LengthSq() * n2.LengthSq());
  340. if (denom < 1.0e-12f)
  341. b.mInitialAngle = 0.0f;
  342. else
  343. {
  344. float sign = Sign(n2.Cross(n1).Dot(e0));
  345. b.mInitialAngle = sign * ACosApproximate(n1.Dot(n2) / denom); // Runtime uses the approximation too
  346. }
  347. }
  348. }
  349. void SoftBodySharedSettings::CalculateVolumeConstraintVolumes()
  350. {
  351. for (Volume &v : mVolumeConstraints)
  352. {
  353. Vec3 x1(mVertices[v.mVertex[0]].mPosition);
  354. Vec3 x2(mVertices[v.mVertex[1]].mPosition);
  355. Vec3 x3(mVertices[v.mVertex[2]].mPosition);
  356. Vec3 x4(mVertices[v.mVertex[3]].mPosition);
  357. Vec3 x1x2 = x2 - x1;
  358. Vec3 x1x3 = x3 - x1;
  359. Vec3 x1x4 = x4 - x1;
  360. v.mSixRestVolume = abs(x1x2.Cross(x1x3).Dot(x1x4));
  361. }
  362. }
  363. void SoftBodySharedSettings::CalculateSkinnedConstraintNormals()
  364. {
  365. // Clear any previous results
  366. mSkinnedConstraintNormals.clear();
  367. // If there are no skinned constraints, we're done
  368. if (mSkinnedConstraints.empty())
  369. return;
  370. // First collect all vertices that are skinned
  371. UnorderedSet<uint32> skinned_vertices;
  372. skinned_vertices.reserve(mSkinnedConstraints.size());
  373. for (const Skinned &s : mSkinnedConstraints)
  374. skinned_vertices.insert(s.mVertex);
  375. // Now collect all faces that connect only to skinned vertices
  376. UnorderedMap<uint32, UnorderedSet<uint32>> connected_faces;
  377. connected_faces.reserve(mVertices.size());
  378. for (const Face &f : mFaces)
  379. {
  380. // Must connect to only skinned vertices
  381. bool valid = true;
  382. for (uint32 v : f.mVertex)
  383. valid &= skinned_vertices.find(v) != skinned_vertices.end();
  384. if (!valid)
  385. continue;
  386. // Store faces that connect to vertices
  387. for (uint32 v : f.mVertex)
  388. connected_faces[v].insert(uint32(&f - mFaces.data()));
  389. }
  390. // Populate the list of connecting faces per skinned vertex
  391. mSkinnedConstraintNormals.reserve(mFaces.size());
  392. for (Skinned &s : mSkinnedConstraints)
  393. {
  394. uint32 start = uint32(mSkinnedConstraintNormals.size());
  395. JPH_ASSERT((start >> 24) == 0);
  396. const UnorderedSet<uint32> &faces = connected_faces[s.mVertex];
  397. uint32 num = uint32(faces.size());
  398. JPH_ASSERT(num < 256);
  399. mSkinnedConstraintNormals.insert(mSkinnedConstraintNormals.end(), faces.begin(), faces.end());
  400. QuickSort(mSkinnedConstraintNormals.begin() + start, mSkinnedConstraintNormals.begin() + start + num);
  401. s.mNormalInfo = start + (num << 24);
  402. }
  403. mSkinnedConstraintNormals.shrink_to_fit();
  404. }
  405. void SoftBodySharedSettings::Optimize(OptimizationResults &outResults)
  406. {
  407. // Clear any previous results
  408. mUpdateGroups.clear();
  409. // Create a list of connected vertices
  410. struct Connection
  411. {
  412. uint32 mVertex;
  413. uint32 mCount;
  414. };
  415. Array<Array<Connection>> connectivity;
  416. connectivity.resize(mVertices.size());
  417. auto add_connection = [&connectivity](uint inV1, uint inV2) {
  418. for (int i = 0; i < 2; ++i)
  419. {
  420. bool found = false;
  421. for (Connection &c : connectivity[inV1])
  422. if (c.mVertex == inV2)
  423. {
  424. c.mCount++;
  425. found = true;
  426. break;
  427. }
  428. if (!found)
  429. connectivity[inV1].push_back({ inV2, 1 });
  430. swap(inV1, inV2);
  431. }
  432. };
  433. for (const Edge &c : mEdgeConstraints)
  434. add_connection(c.mVertex[0], c.mVertex[1]);
  435. for (const LRA &c : mLRAConstraints)
  436. add_connection(c.mVertex[0], c.mVertex[1]);
  437. for (const DihedralBend &c : mDihedralBendConstraints)
  438. {
  439. add_connection(c.mVertex[0], c.mVertex[1]);
  440. add_connection(c.mVertex[0], c.mVertex[2]);
  441. add_connection(c.mVertex[0], c.mVertex[3]);
  442. add_connection(c.mVertex[1], c.mVertex[2]);
  443. add_connection(c.mVertex[1], c.mVertex[3]);
  444. add_connection(c.mVertex[2], c.mVertex[3]);
  445. }
  446. for (const Volume &c : mVolumeConstraints)
  447. {
  448. add_connection(c.mVertex[0], c.mVertex[1]);
  449. add_connection(c.mVertex[0], c.mVertex[2]);
  450. add_connection(c.mVertex[0], c.mVertex[3]);
  451. add_connection(c.mVertex[1], c.mVertex[2]);
  452. add_connection(c.mVertex[1], c.mVertex[3]);
  453. add_connection(c.mVertex[2], c.mVertex[3]);
  454. }
  455. // Skinned constraints only update 1 vertex, so we don't need special logic here
  456. // Maps each of the vertices to a group index
  457. Array<int> group_idx;
  458. group_idx.resize(mVertices.size(), -1);
  459. // Which group we are currently filling and its vertices
  460. int current_group_idx = 0;
  461. Array<uint> current_group;
  462. // Start greedy algorithm to group vertices
  463. for (;;)
  464. {
  465. // Find the bounding box of the ungrouped vertices
  466. AABox bounds;
  467. for (uint i = 0; i < (uint)mVertices.size(); ++i)
  468. if (group_idx[i] == -1)
  469. bounds.Encapsulate(Vec3(mVertices[i].mPosition));
  470. // Determine longest and shortest axis
  471. Vec3 bounds_size = bounds.GetSize();
  472. uint max_axis = bounds_size.GetHighestComponentIndex();
  473. uint min_axis = bounds_size.GetLowestComponentIndex();
  474. if (min_axis == max_axis)
  475. min_axis = (min_axis + 1) % 3;
  476. uint mid_axis = 3 - min_axis - max_axis;
  477. // Find the vertex that has the lowest value on the axis with the largest extent
  478. uint current_vertex = UINT_MAX;
  479. Float3 current_vertex_position { FLT_MAX, FLT_MAX, FLT_MAX };
  480. for (uint i = 0; i < (uint)mVertices.size(); ++i)
  481. if (group_idx[i] == -1)
  482. {
  483. const Float3 &vertex_position = mVertices[i].mPosition;
  484. float max_axis_value = vertex_position[max_axis];
  485. float mid_axis_value = vertex_position[mid_axis];
  486. float min_axis_value = vertex_position[min_axis];
  487. if (max_axis_value < current_vertex_position[max_axis]
  488. || (max_axis_value == current_vertex_position[max_axis]
  489. && (mid_axis_value < current_vertex_position[mid_axis]
  490. || (mid_axis_value == current_vertex_position[mid_axis]
  491. && min_axis_value < current_vertex_position[min_axis]))))
  492. {
  493. current_vertex_position = mVertices[i].mPosition;
  494. current_vertex = i;
  495. }
  496. }
  497. if (current_vertex == UINT_MAX)
  498. break;
  499. // Initialize the current group with 1 vertex
  500. current_group.push_back(current_vertex);
  501. group_idx[current_vertex] = current_group_idx;
  502. // Fill up the group
  503. for (;;)
  504. {
  505. // Find the vertex that is most connected to the current group
  506. uint best_vertex = UINT_MAX;
  507. uint best_num_connections = 0;
  508. float best_dist_sq = FLT_MAX;
  509. for (uint i = 0; i < (uint)current_group.size(); ++i) // For all vertices in the current group
  510. for (const Connection &c : connectivity[current_group[i]]) // For all connections to other vertices
  511. {
  512. uint v = c.mVertex;
  513. if (group_idx[v] == -1) // Ungrouped vertices only
  514. {
  515. // Count the number of connections to this group
  516. uint num_connections = 0;
  517. for (const Connection &v2 : connectivity[v])
  518. if (group_idx[v2.mVertex] == current_group_idx)
  519. num_connections += v2.mCount;
  520. // Calculate distance to group centroid
  521. float dist_sq = (Vec3(mVertices[v].mPosition) - Vec3(mVertices[current_group.front()].mPosition)).LengthSq();
  522. if (best_vertex == UINT_MAX
  523. || num_connections > best_num_connections
  524. || (num_connections == best_num_connections && dist_sq < best_dist_sq))
  525. {
  526. best_vertex = v;
  527. best_num_connections = num_connections;
  528. best_dist_sq = dist_sq;
  529. }
  530. }
  531. }
  532. // Add the best vertex to the current group
  533. if (best_vertex != UINT_MAX)
  534. {
  535. current_group.push_back(best_vertex);
  536. group_idx[best_vertex] = current_group_idx;
  537. }
  538. // Create a new group?
  539. if (current_group.size() >= SoftBodyUpdateContext::cVertexConstraintBatch // If full, yes
  540. || (current_group.size() > SoftBodyUpdateContext::cVertexConstraintBatch / 2 && best_vertex == UINT_MAX)) // If half full and we found no connected vertex, yes
  541. {
  542. current_group.clear();
  543. current_group_idx++;
  544. break;
  545. }
  546. // If we didn't find a connected vertex, we need to find a new starting vertex
  547. if (best_vertex == UINT_MAX)
  548. break;
  549. }
  550. }
  551. // 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
  552. if (current_group.size() > SoftBodyUpdateContext::cVertexConstraintBatch / 2)
  553. ++current_group_idx;
  554. // We no longer need the current group array, free the memory
  555. current_group.clear();
  556. current_group.shrink_to_fit();
  557. // We're done with the connectivity list, free the memory
  558. connectivity.clear();
  559. connectivity.shrink_to_fit();
  560. // Assign the constraints to their groups
  561. struct Group
  562. {
  563. uint GetSize() const
  564. {
  565. return (uint)mEdgeConstraints.size() + (uint)mLRAConstraints.size() + (uint)mDihedralBendConstraints.size() + (uint)mVolumeConstraints.size() + (uint)mSkinnedConstraints.size();
  566. }
  567. Array<uint> mEdgeConstraints;
  568. Array<uint> mLRAConstraints;
  569. Array<uint> mDihedralBendConstraints;
  570. Array<uint> mVolumeConstraints;
  571. Array<uint> mSkinnedConstraints;
  572. };
  573. Array<Group> groups;
  574. groups.resize(current_group_idx + 1); // + non parallel group
  575. for (const Edge &e : mEdgeConstraints)
  576. {
  577. int g1 = group_idx[e.mVertex[0]];
  578. int g2 = group_idx[e.mVertex[1]];
  579. JPH_ASSERT(g1 >= 0 && g2 >= 0);
  580. if (g1 == g2) // In the same group
  581. groups[g1].mEdgeConstraints.push_back(uint(&e - mEdgeConstraints.data()));
  582. else // In different groups -> parallel group
  583. groups.back().mEdgeConstraints.push_back(uint(&e - mEdgeConstraints.data()));
  584. }
  585. for (const LRA &l : mLRAConstraints)
  586. {
  587. int g1 = group_idx[l.mVertex[0]];
  588. int g2 = group_idx[l.mVertex[1]];
  589. JPH_ASSERT(g1 >= 0 && g2 >= 0);
  590. if (g1 == g2) // In the same group
  591. groups[g1].mLRAConstraints.push_back(uint(&l - mLRAConstraints.data()));
  592. else // In different groups -> parallel group
  593. groups.back().mLRAConstraints.push_back(uint(&l - mLRAConstraints.data()));
  594. }
  595. for (const DihedralBend &d : mDihedralBendConstraints)
  596. {
  597. int g1 = group_idx[d.mVertex[0]];
  598. int g2 = group_idx[d.mVertex[1]];
  599. int g3 = group_idx[d.mVertex[2]];
  600. int g4 = group_idx[d.mVertex[3]];
  601. JPH_ASSERT(g1 >= 0 && g2 >= 0 && g3 >= 0 && g4 >= 0);
  602. if (g1 == g2 && g1 == g3 && g1 == g4) // In the same group
  603. groups[g1].mDihedralBendConstraints.push_back(uint(&d - mDihedralBendConstraints.data()));
  604. else // In different groups -> parallel group
  605. groups.back().mDihedralBendConstraints.push_back(uint(&d - mDihedralBendConstraints.data()));
  606. }
  607. for (const Volume &v : mVolumeConstraints)
  608. {
  609. int g1 = group_idx[v.mVertex[0]];
  610. int g2 = group_idx[v.mVertex[1]];
  611. int g3 = group_idx[v.mVertex[2]];
  612. int g4 = group_idx[v.mVertex[3]];
  613. JPH_ASSERT(g1 >= 0 && g2 >= 0 && g3 >= 0 && g4 >= 0);
  614. if (g1 == g2 && g1 == g3 && g1 == g4) // In the same group
  615. groups[g1].mVolumeConstraints.push_back(uint(&v - mVolumeConstraints.data()));
  616. else // In different groups -> parallel group
  617. groups.back().mVolumeConstraints.push_back(uint(&v - mVolumeConstraints.data()));
  618. }
  619. for (const Skinned &s : mSkinnedConstraints)
  620. {
  621. int g1 = group_idx[s.mVertex];
  622. JPH_ASSERT(g1 >= 0);
  623. groups[g1].mSkinnedConstraints.push_back(uint(&s - mSkinnedConstraints.data()));
  624. }
  625. // Sort the parallel groups from big to small (this means the big groups will be scheduled first and have more time to complete)
  626. QuickSort(groups.begin(), groups.end() - 1, [](const Group &inLHS, const Group &inRHS) { return inLHS.GetSize() > inRHS.GetSize(); });
  627. // Make sure we know the closest kinematic vertex so we can sort
  628. CalculateClosestKinematic();
  629. // Sort within each group
  630. for (Group &group : groups)
  631. {
  632. // Sort the edge constraints
  633. QuickSort(group.mEdgeConstraints.begin(), group.mEdgeConstraints.end(), [this](uint inLHS, uint inRHS)
  634. {
  635. const Edge &e1 = mEdgeConstraints[inLHS];
  636. const Edge &e2 = mEdgeConstraints[inRHS];
  637. // First sort so that the edge with the smallest distance to a kinematic vertex comes first
  638. float d1 = min(mClosestKinematic[e1.mVertex[0]].mDistance, mClosestKinematic[e1.mVertex[1]].mDistance);
  639. float d2 = min(mClosestKinematic[e2.mVertex[0]].mDistance, mClosestKinematic[e2.mVertex[1]].mDistance);
  640. if (d1 != d2)
  641. return d1 < d2;
  642. // Order the edges so that the ones with the smallest index go first (hoping to get better cache locality when we process the edges).
  643. // Note we could also re-order the vertices but that would be much more of a burden to the end user
  644. uint32 m1 = e1.GetMinVertexIndex();
  645. uint32 m2 = e2.GetMinVertexIndex();
  646. if (m1 != m2)
  647. return m1 < m2;
  648. return inLHS < inRHS;
  649. });
  650. // Sort the LRA constraints
  651. QuickSort(group.mLRAConstraints.begin(), group.mLRAConstraints.end(), [this](uint inLHS, uint inRHS)
  652. {
  653. const LRA &l1 = mLRAConstraints[inLHS];
  654. const LRA &l2 = mLRAConstraints[inRHS];
  655. // First sort so that the longest constraint comes first (meaning the shortest constraint has the most influence on the end result)
  656. // Most of the time there will be a single LRA constraint per vertex and since the LRA constraint only modifies a single vertex,
  657. // updating one constraint will not violate another constraint.
  658. if (l1.mMaxDistance != l2.mMaxDistance)
  659. return l1.mMaxDistance > l2.mMaxDistance;
  660. // Order constraints so that the ones with the smallest index go first
  661. uint32 m1 = l1.GetMinVertexIndex();
  662. uint32 m2 = l2.GetMinVertexIndex();
  663. if (m1 != m2)
  664. return m1 < m2;
  665. return inLHS < inRHS;
  666. });
  667. // Sort the dihedral bend constraints
  668. QuickSort(group.mDihedralBendConstraints.begin(), group.mDihedralBendConstraints.end(), [this](uint inLHS, uint inRHS)
  669. {
  670. const DihedralBend &b1 = mDihedralBendConstraints[inLHS];
  671. const DihedralBend &b2 = mDihedralBendConstraints[inRHS];
  672. // First sort so that the constraint with the smallest distance to a kinematic vertex comes first
  673. float d1 = min(
  674. min(mClosestKinematic[b1.mVertex[0]].mDistance, mClosestKinematic[b1.mVertex[1]].mDistance),
  675. min(mClosestKinematic[b1.mVertex[2]].mDistance, mClosestKinematic[b1.mVertex[3]].mDistance));
  676. float d2 = min(
  677. min(mClosestKinematic[b2.mVertex[0]].mDistance, mClosestKinematic[b2.mVertex[1]].mDistance),
  678. min(mClosestKinematic[b2.mVertex[2]].mDistance, mClosestKinematic[b2.mVertex[3]].mDistance));
  679. if (d1 != d2)
  680. return d1 < d2;
  681. // Order constraints so that the ones with the smallest index go first
  682. uint32 m1 = b1.GetMinVertexIndex();
  683. uint32 m2 = b2.GetMinVertexIndex();
  684. if (m1 != m2)
  685. return m1 < m2;
  686. return inLHS < inRHS;
  687. });
  688. // Sort the volume constraints
  689. QuickSort(group.mVolumeConstraints.begin(), group.mVolumeConstraints.end(), [this](uint inLHS, uint inRHS)
  690. {
  691. const Volume &v1 = mVolumeConstraints[inLHS];
  692. const Volume &v2 = mVolumeConstraints[inRHS];
  693. // First sort so that the constraint with the smallest distance to a kinematic vertex comes first
  694. float d1 = min(
  695. min(mClosestKinematic[v1.mVertex[0]].mDistance, mClosestKinematic[v1.mVertex[1]].mDistance),
  696. min(mClosestKinematic[v1.mVertex[2]].mDistance, mClosestKinematic[v1.mVertex[3]].mDistance));
  697. float d2 = min(
  698. min(mClosestKinematic[v2.mVertex[0]].mDistance, mClosestKinematic[v2.mVertex[1]].mDistance),
  699. min(mClosestKinematic[v2.mVertex[2]].mDistance, mClosestKinematic[v2.mVertex[3]].mDistance));
  700. if (d1 != d2)
  701. return d1 < d2;
  702. // Order constraints so that the ones with the smallest index go first
  703. uint32 m1 = v1.GetMinVertexIndex();
  704. uint32 m2 = v2.GetMinVertexIndex();
  705. if (m1 != m2)
  706. return m1 < m2;
  707. return inLHS < inRHS;
  708. });
  709. // Sort the skinned constraints
  710. QuickSort(group.mSkinnedConstraints.begin(), group.mSkinnedConstraints.end(), [this](uint inLHS, uint inRHS)
  711. {
  712. const Skinned &s1 = mSkinnedConstraints[inLHS];
  713. const Skinned &s2 = mSkinnedConstraints[inRHS];
  714. // 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).
  715. if (s1.mVertex != s2.mVertex)
  716. return s1.mVertex < s2.mVertex;
  717. return inLHS < inRHS;
  718. });
  719. }
  720. // Temporary store constraints as we reorder them
  721. Array<Edge> temp_edges;
  722. temp_edges.swap(mEdgeConstraints);
  723. mEdgeConstraints.reserve(temp_edges.size());
  724. outResults.mEdgeRemap.reserve(temp_edges.size());
  725. Array<LRA> temp_lra;
  726. temp_lra.swap(mLRAConstraints);
  727. mLRAConstraints.reserve(temp_lra.size());
  728. outResults.mLRARemap.reserve(temp_lra.size());
  729. Array<DihedralBend> temp_dihedral_bend;
  730. temp_dihedral_bend.swap(mDihedralBendConstraints);
  731. mDihedralBendConstraints.reserve(temp_dihedral_bend.size());
  732. outResults.mDihedralBendRemap.reserve(temp_dihedral_bend.size());
  733. Array<Volume> temp_volume;
  734. temp_volume.swap(mVolumeConstraints);
  735. mVolumeConstraints.reserve(temp_volume.size());
  736. outResults.mVolumeRemap.reserve(temp_volume.size());
  737. Array<Skinned> temp_skinned;
  738. temp_skinned.swap(mSkinnedConstraints);
  739. mSkinnedConstraints.reserve(temp_skinned.size());
  740. outResults.mSkinnedRemap.reserve(temp_skinned.size());
  741. // Finalize update groups
  742. for (const Group &group : groups)
  743. {
  744. // Reorder edge constraints for this group
  745. for (uint idx : group.mEdgeConstraints)
  746. {
  747. mEdgeConstraints.push_back(temp_edges[idx]);
  748. outResults.mEdgeRemap.push_back(idx);
  749. }
  750. // Reorder LRA constraints for this group
  751. for (uint idx : group.mLRAConstraints)
  752. {
  753. mLRAConstraints.push_back(temp_lra[idx]);
  754. outResults.mLRARemap.push_back(idx);
  755. }
  756. // Reorder dihedral bend constraints for this group
  757. for (uint idx : group.mDihedralBendConstraints)
  758. {
  759. mDihedralBendConstraints.push_back(temp_dihedral_bend[idx]);
  760. outResults.mDihedralBendRemap.push_back(idx);
  761. }
  762. // Reorder volume constraints for this group
  763. for (uint idx : group.mVolumeConstraints)
  764. {
  765. mVolumeConstraints.push_back(temp_volume[idx]);
  766. outResults.mVolumeRemap.push_back(idx);
  767. }
  768. // Reorder skinned constraints for this group
  769. for (uint idx : group.mSkinnedConstraints)
  770. {
  771. mSkinnedConstraints.push_back(temp_skinned[idx]);
  772. outResults.mSkinnedRemap.push_back(idx);
  773. }
  774. // Store end indices
  775. mUpdateGroups.push_back({ (uint)mEdgeConstraints.size(), (uint)mLRAConstraints.size(), (uint)mDihedralBendConstraints.size(), (uint)mVolumeConstraints.size(), (uint)mSkinnedConstraints.size() });
  776. }
  777. // Free closest kinematic buffer
  778. mClosestKinematic.clear();
  779. mClosestKinematic.shrink_to_fit();
  780. }
  781. Ref<SoftBodySharedSettings> SoftBodySharedSettings::Clone() const
  782. {
  783. Ref<SoftBodySharedSettings> clone = new SoftBodySharedSettings;
  784. clone->mVertices = mVertices;
  785. clone->mFaces = mFaces;
  786. clone->mEdgeConstraints = mEdgeConstraints;
  787. clone->mDihedralBendConstraints = mDihedralBendConstraints;
  788. clone->mVolumeConstraints = mVolumeConstraints;
  789. clone->mSkinnedConstraints = mSkinnedConstraints;
  790. clone->mSkinnedConstraintNormals = mSkinnedConstraintNormals;
  791. clone->mInvBindMatrices = mInvBindMatrices;
  792. clone->mLRAConstraints = mLRAConstraints;
  793. clone->mMaterials = mMaterials;
  794. clone->mVertexRadius = mVertexRadius;
  795. clone->mUpdateGroups = mUpdateGroups;
  796. return clone;
  797. }
  798. void SoftBodySharedSettings::SaveBinaryState(StreamOut &inStream) const
  799. {
  800. inStream.Write(mVertices);
  801. inStream.Write(mFaces);
  802. inStream.Write(mEdgeConstraints);
  803. inStream.Write(mDihedralBendConstraints);
  804. inStream.Write(mVolumeConstraints);
  805. inStream.Write(mSkinnedConstraints);
  806. inStream.Write(mSkinnedConstraintNormals);
  807. inStream.Write(mLRAConstraints);
  808. inStream.Write(mVertexRadius);
  809. inStream.Write(mUpdateGroups);
  810. // Can't write mInvBindMatrices directly because the class contains padding
  811. inStream.Write(mInvBindMatrices, [](const InvBind &inElement, StreamOut &inS) {
  812. inS.Write(inElement.mJointIndex);
  813. inS.Write(inElement.mInvBind);
  814. });
  815. }
  816. void SoftBodySharedSettings::RestoreBinaryState(StreamIn &inStream)
  817. {
  818. inStream.Read(mVertices);
  819. inStream.Read(mFaces);
  820. inStream.Read(mEdgeConstraints);
  821. inStream.Read(mDihedralBendConstraints);
  822. inStream.Read(mVolumeConstraints);
  823. inStream.Read(mSkinnedConstraints);
  824. inStream.Read(mSkinnedConstraintNormals);
  825. inStream.Read(mLRAConstraints);
  826. inStream.Read(mVertexRadius);
  827. inStream.Read(mUpdateGroups);
  828. inStream.Read(mInvBindMatrices, [](StreamIn &inS, InvBind &outElement) {
  829. inS.Read(outElement.mJointIndex);
  830. inS.Read(outElement.mInvBind);
  831. });
  832. }
  833. void SoftBodySharedSettings::SaveWithMaterials(StreamOut &inStream, SharedSettingsToIDMap &ioSettingsMap, MaterialToIDMap &ioMaterialMap) const
  834. {
  835. SharedSettingsToIDMap::const_iterator settings_iter = ioSettingsMap.find(this);
  836. if (settings_iter == ioSettingsMap.end())
  837. {
  838. // Write settings ID
  839. uint32 settings_id = (uint32)ioSettingsMap.size();
  840. ioSettingsMap[this] = settings_id;
  841. inStream.Write(settings_id);
  842. // Write the settings
  843. SaveBinaryState(inStream);
  844. // Write materials
  845. StreamUtils::SaveObjectArray(inStream, mMaterials, &ioMaterialMap);
  846. }
  847. else
  848. {
  849. // Known settings, just write the ID
  850. inStream.Write(settings_iter->second);
  851. }
  852. }
  853. SoftBodySharedSettings::SettingsResult SoftBodySharedSettings::sRestoreWithMaterials(StreamIn &inStream, IDToSharedSettingsMap &ioSettingsMap, IDToMaterialMap &ioMaterialMap)
  854. {
  855. SettingsResult result;
  856. // Read settings id
  857. uint32 settings_id;
  858. inStream.Read(settings_id);
  859. if (inStream.IsEOF() || inStream.IsFailed())
  860. {
  861. result.SetError("Failed to read settings id");
  862. return result;
  863. }
  864. // Check nullptr settings
  865. if (settings_id == ~uint32(0))
  866. {
  867. result.Set(nullptr);
  868. return result;
  869. }
  870. // Check if we already read this settings
  871. if (settings_id < ioSettingsMap.size())
  872. {
  873. result.Set(ioSettingsMap[settings_id]);
  874. return result;
  875. }
  876. // Create new object
  877. Ref<SoftBodySharedSettings> settings = new SoftBodySharedSettings;
  878. // Read state
  879. settings->RestoreBinaryState(inStream);
  880. // Read materials
  881. Result mlresult = StreamUtils::RestoreObjectArray<PhysicsMaterialList>(inStream, ioMaterialMap);
  882. if (mlresult.HasError())
  883. {
  884. result.SetError(mlresult.GetError());
  885. return result;
  886. }
  887. settings->mMaterials = mlresult.Get();
  888. // Add the settings to the map
  889. ioSettingsMap.push_back(settings);
  890. result.Set(settings);
  891. return result;
  892. }
  893. JPH_NAMESPACE_END