2
0

HairSettings.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2026 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #include <Jolt/Jolt.h>
  5. #include <Jolt/Physics/Hair/HairSettings.h>
  6. #include <Jolt/ObjectStream/TypeDeclarations.h>
  7. #include <Jolt/Geometry/ClosestPoint.h>
  8. #include <Jolt/TriangleSplitter/TriangleSplitterBinning.h>
  9. #include <Jolt/AABBTree/AABBTreeBuilder.h>
  10. #include <Jolt/Core/QuickSort.h>
  11. #include <Jolt/Core/StreamIn.h>
  12. #include <Jolt/Core/StreamOut.h>
  13. JPH_NAMESPACE_BEGIN
  14. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings)
  15. {
  16. JPH_ADD_ATTRIBUTE(HairSettings, mSimVertices)
  17. JPH_ADD_ATTRIBUTE(HairSettings, mSimStrands)
  18. JPH_ADD_ATTRIBUTE(HairSettings, mRenderVertices)
  19. JPH_ADD_ATTRIBUTE(HairSettings, mRenderStrands)
  20. JPH_ADD_ATTRIBUTE(HairSettings, mScalpVertices)
  21. JPH_ADD_ATTRIBUTE(HairSettings, mScalpTriangles)
  22. JPH_ADD_ATTRIBUTE(HairSettings, mScalpInverseBindPose)
  23. JPH_ADD_ATTRIBUTE(HairSettings, mScalpSkinWeights)
  24. JPH_ADD_ATTRIBUTE(HairSettings, mScalpNumSkinWeightsPerVertex)
  25. JPH_ADD_ATTRIBUTE(HairSettings, mNumIterationsPerSecond)
  26. JPH_ADD_ATTRIBUTE(HairSettings, mMaxDeltaTime)
  27. JPH_ADD_ATTRIBUTE(HairSettings, mGridSize)
  28. JPH_ADD_ATTRIBUTE(HairSettings, mSimulationBoundsPadding)
  29. JPH_ADD_ATTRIBUTE(HairSettings, mInitialGravity)
  30. JPH_ADD_ATTRIBUTE(HairSettings, mMaterials)
  31. }
  32. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::SkinWeight)
  33. {
  34. JPH_ADD_ATTRIBUTE(HairSettings::SkinWeight, mJointIdx)
  35. JPH_ADD_ATTRIBUTE(HairSettings::SkinWeight, mWeight)
  36. }
  37. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::SkinPoint)
  38. {
  39. JPH_ADD_ATTRIBUTE(HairSettings::SkinPoint, mTriangleIndex)
  40. JPH_ADD_ATTRIBUTE(HairSettings::SkinPoint, mU)
  41. JPH_ADD_ATTRIBUTE(HairSettings::SkinPoint, mV)
  42. }
  43. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::SVertexInfluence)
  44. {
  45. JPH_ADD_ATTRIBUTE(HairSettings::SVertexInfluence, mVertexIndex)
  46. JPH_ADD_ATTRIBUTE(HairSettings::SVertexInfluence, mRelativePosition)
  47. JPH_ADD_ATTRIBUTE(HairSettings::SVertexInfluence, mWeight)
  48. }
  49. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::RVertex)
  50. {
  51. JPH_ADD_ATTRIBUTE(HairSettings::RVertex, mPosition)
  52. JPH_ADD_ATTRIBUTE(HairSettings::RVertex, mInfluences)
  53. }
  54. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::SVertex)
  55. {
  56. JPH_ADD_ATTRIBUTE(HairSettings::SVertex, mPosition)
  57. JPH_ADD_ATTRIBUTE(HairSettings::SVertex, mInvMass)
  58. }
  59. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::RStrand)
  60. {
  61. JPH_ADD_ATTRIBUTE(HairSettings::RStrand, mStartVtx)
  62. JPH_ADD_ATTRIBUTE(HairSettings::RStrand, mEndVtx)
  63. }
  64. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::SStrand)
  65. {
  66. JPH_ADD_BASE_CLASS(HairSettings::SStrand, HairSettings::RStrand)
  67. JPH_ADD_ATTRIBUTE(HairSettings::SStrand, mMaterialIndex)
  68. }
  69. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::Gradient)
  70. {
  71. JPH_ADD_ATTRIBUTE(HairSettings::Gradient, mMin)
  72. JPH_ADD_ATTRIBUTE(HairSettings::Gradient, mMax)
  73. JPH_ADD_ATTRIBUTE(HairSettings::Gradient, mMinFraction)
  74. JPH_ADD_ATTRIBUTE(HairSettings::Gradient, mMaxFraction)
  75. }
  76. JPH_IMPLEMENT_SERIALIZABLE_NON_VIRTUAL(HairSettings::Material)
  77. {
  78. JPH_ADD_ATTRIBUTE(HairSettings::Material, mEnableCollision)
  79. JPH_ADD_ATTRIBUTE(HairSettings::Material, mEnableLRA)
  80. JPH_ADD_ATTRIBUTE(HairSettings::Material, mLinearDamping)
  81. JPH_ADD_ATTRIBUTE(HairSettings::Material, mAngularDamping)
  82. JPH_ADD_ATTRIBUTE(HairSettings::Material, mMaxLinearVelocity)
  83. JPH_ADD_ATTRIBUTE(HairSettings::Material, mMaxAngularVelocity)
  84. JPH_ADD_ATTRIBUTE(HairSettings::Material, mGravityFactor)
  85. JPH_ADD_ATTRIBUTE(HairSettings::Material, mFriction)
  86. JPH_ADD_ATTRIBUTE(HairSettings::Material, mBendCompliance)
  87. JPH_ADD_ATTRIBUTE(HairSettings::Material, mBendComplianceMultiplier)
  88. JPH_ADD_ATTRIBUTE(HairSettings::Material, mStretchCompliance)
  89. JPH_ADD_ATTRIBUTE(HairSettings::Material, mInertiaMultiplier)
  90. JPH_ADD_ATTRIBUTE(HairSettings::Material, mHairRadius)
  91. JPH_ADD_ATTRIBUTE(HairSettings::Material, mWorldTransformInfluence)
  92. JPH_ADD_ATTRIBUTE(HairSettings::Material, mGridVelocityFactor)
  93. JPH_ADD_ATTRIBUTE(HairSettings::Material, mGridDensityForceFactor)
  94. JPH_ADD_ATTRIBUTE(HairSettings::Material, mGlobalPose)
  95. JPH_ADD_ATTRIBUTE(HairSettings::Material, mSkinGlobalPose)
  96. JPH_ADD_ATTRIBUTE(HairSettings::Material, mSimulationStrandsFraction)
  97. JPH_ADD_ATTRIBUTE(HairSettings::Material, mGravityPreloadFactor)
  98. }
  99. void HairSettings::Gradient::SaveBinaryState(StreamOut &inStream) const
  100. {
  101. inStream.Write(mMin);
  102. inStream.Write(mMax);
  103. inStream.Write(mMinFraction);
  104. inStream.Write(mMaxFraction);
  105. }
  106. void HairSettings::Gradient::RestoreBinaryState(StreamIn &inStream)
  107. {
  108. inStream.Read(mMin);
  109. inStream.Read(mMax);
  110. inStream.Read(mMinFraction);
  111. inStream.Read(mMaxFraction);
  112. }
  113. void HairSettings::InitRenderAndSimulationStrands(const Array<SVertex> &inVertices, const Array<SStrand> &inStrands)
  114. {
  115. // Copy original strands to render strands
  116. mRenderVertices.resize(inVertices.size());
  117. for (uint i = 0, n = uint(inVertices.size()); i < n; ++i)
  118. mRenderVertices[i].mPosition = inVertices[i].mPosition;
  119. mRenderStrands.resize(inStrands.size());
  120. for (uint i = 0, n = uint(inStrands.size()); i < n; ++i)
  121. mRenderStrands[i] = RStrand(inStrands[i].mStartVtx, inStrands[i].mEndVtx);
  122. // Create buffer that holds indices to the strands
  123. Array<uint> indices_shuffle;
  124. indices_shuffle.resize(inStrands.size());
  125. for (uint i = 0, n = uint(inStrands.size()); i < n; ++i)
  126. indices_shuffle[i] = i;
  127. // Order on material index
  128. QuickSort(indices_shuffle.begin(), indices_shuffle.end(), [&inStrands](uint inLHS, uint inRHS) {
  129. return inStrands[inLHS].mMaterialIndex < inStrands[inRHS].mMaterialIndex;
  130. });
  131. // Loop over all materials
  132. Array<uint>::iterator begin_material = indices_shuffle.begin();
  133. while (begin_material < indices_shuffle.end())
  134. {
  135. uint32 material_index = inStrands[*begin_material].mMaterialIndex;
  136. // Find end of this material
  137. Array<uint>::iterator end_material = begin_material;
  138. do
  139. ++end_material;
  140. while (end_material < indices_shuffle.end() && inStrands[*end_material].mMaterialIndex == material_index);
  141. // Select X% random strands to simulate
  142. std::mt19937 random;
  143. std::shuffle(begin_material, end_material, random);
  144. size_t num_simulated = max<size_t>(size_t(ceil(double(mMaterials[material_index].mSimulationStrandsFraction) * double(end_material - begin_material))), 1);
  145. Array<uint>::iterator end_simulation = begin_material + num_simulated;
  146. QuickSort(begin_material, end_simulation, std::less<uint>()); // Sort simulated strands back to original order
  147. for (Array<uint>::const_iterator idx = begin_material; idx < end_simulation; ++idx)
  148. {
  149. // Add simulation strand
  150. const HairSettings::SStrand &sim_strand = inStrands[*idx];
  151. mSimStrands.push_back(HairSettings::SStrand((uint32)mSimVertices.size(), (uint32)mSimVertices.size() + sim_strand.VertexCount(), sim_strand.mMaterialIndex));
  152. for (uint32 v = sim_strand.mStartVtx; v < sim_strand.mEndVtx; ++v)
  153. {
  154. // Link render vertex to simulation vertex
  155. mRenderVertices[v].mInfluences[0].mVertexIndex = uint32(mSimVertices.size());
  156. // Add simulation vertex
  157. mSimVertices.push_back(inVertices[v]);
  158. }
  159. }
  160. // Get influences for remaining strands
  161. for (Array<uint>::const_iterator idx = end_simulation; idx < end_material; ++idx)
  162. {
  163. const HairSettings::SStrand &render_strand = inStrands[*idx];
  164. // Find closest simulation strand
  165. float closest_d_sq = FLT_MAX;
  166. uint closest_strand_idx = 0;
  167. for (const HairSettings::SStrand &sim_strand : mSimStrands)
  168. if (sim_strand.mMaterialIndex == render_strand.mMaterialIndex)
  169. {
  170. // Get the first 2 vertices of the simulation strand
  171. uint32 v_max = sim_strand.mEndVtx - 1;
  172. uint32 v = sim_strand.mStartVtx, v_next = min(v + 1, v_max);
  173. Vec3 v_pos(mSimVertices[v].mPosition), v_next_pos(mSimVertices[v_next].mPosition);
  174. // Track total error when selecting this sim strand as parent for the render strand
  175. float d_sq_total = 0.0f;
  176. // Loop over the render strand
  177. for (uint32 rv = render_strand.mStartVtx; rv < render_strand.mEndVtx; ++rv)
  178. {
  179. Vec3 rv_pos(mRenderVertices[rv].mPosition);
  180. // Find closest simulated vertex (note that we assume that the strands do not loop back
  181. // on themselves so that an earlier vertex in the strand could be the closest)
  182. float d_sq = (rv_pos - v_pos).LengthSq();
  183. float d_sq_next = (rv_pos - v_next_pos).LengthSq();
  184. while (d_sq_next < d_sq)
  185. {
  186. // Get the next vertex of the simulation strand
  187. v = v_next;
  188. v_next = min(v + 1, v_max);
  189. v_pos = v_next_pos;
  190. v_next_pos = Vec3(mSimVertices[v_next].mPosition);
  191. // Update distance to render vertex
  192. d_sq = d_sq_next;
  193. d_sq_next = (rv_pos - v_next_pos).LengthSq();
  194. }
  195. // Accumulate total error
  196. d_sq_total += d_sq;
  197. // No point in continuing the search if our result is worse already
  198. if (d_sq_total > closest_d_sq)
  199. break;
  200. }
  201. // If this is the smallest error, accept
  202. if (d_sq_total < closest_d_sq)
  203. {
  204. closest_d_sq = d_sq_total;
  205. closest_strand_idx = uint(&sim_strand - mSimStrands.data());
  206. }
  207. }
  208. const HairSettings::SStrand &closest_strand = mSimStrands[closest_strand_idx];
  209. // Link render vertices to simulation vertices
  210. for (uint32 v = render_strand.mStartVtx; v < render_strand.mEndVtx; ++v)
  211. {
  212. HairSettings::RVertex &rv = mRenderVertices[v];
  213. // Find closest simulated vertex
  214. closest_d_sq = FLT_MAX;
  215. for (uint32 cv = closest_strand.mStartVtx; cv < closest_strand.mEndVtx; ++cv)
  216. {
  217. float d_sq = (Vec3(mSimVertices[cv].mPosition) - Vec3(rv.mPosition)).LengthSq();
  218. if (d_sq < closest_d_sq)
  219. {
  220. closest_d_sq = d_sq;
  221. rv.mInfluences[0].mVertexIndex = cv;
  222. }
  223. }
  224. }
  225. }
  226. // Next material
  227. begin_material = end_material;
  228. }
  229. }
  230. void HairSettings::sResample(Array<SVertex> &ioVertices, Array<SStrand> &ioStrands, uint32 inNumVerticesPerStrand)
  231. {
  232. Array<SVertex> vertices;
  233. ioVertices.swap(vertices);
  234. Array<SStrand> strands;
  235. ioStrands.swap(strands);
  236. for (const SStrand &strand : strands)
  237. {
  238. // Determine output strand
  239. SStrand out_strand;
  240. out_strand.mStartVtx = (uint32)ioVertices.size();
  241. out_strand.mEndVtx = out_strand.mStartVtx + inNumVerticesPerStrand;
  242. out_strand.mMaterialIndex = strand.mMaterialIndex;
  243. ioStrands.push_back(out_strand);
  244. // Measure length of the strand
  245. float length = strand.MeasureLength(vertices);
  246. // Add the first vertex of the strand
  247. ioVertices.push_back(vertices[strand.mStartVtx]);
  248. // Resample the strand
  249. float cur_length = 0.0f;
  250. const SVertex *v0 = &vertices[strand.mStartVtx];
  251. const SVertex *v1 = &vertices[strand.mStartVtx + 1];
  252. float segment_length = (Vec3(v1->mPosition) - Vec3(v0->mPosition)).Length();
  253. for (uint32 resampled_point = 1; resampled_point < inNumVerticesPerStrand - 1; ++resampled_point)
  254. {
  255. float desired_len = resampled_point * length / (inNumVerticesPerStrand - 1);
  256. while (cur_length + segment_length < desired_len)
  257. {
  258. cur_length += segment_length;
  259. ++v0;
  260. ++v1;
  261. JPH_ASSERT(uint32(v1 - vertices.data()) < strand.mEndVtx);
  262. segment_length = (Vec3(v1->mPosition) - Vec3(v0->mPosition)).Length();
  263. }
  264. SVertex out_v = *v0;
  265. float fraction = (desired_len - cur_length) / segment_length;
  266. (Vec3(v0->mPosition) + (Vec3(v1->mPosition) - Vec3(v0->mPosition)) * fraction).StoreFloat3(&out_v.mPosition);
  267. out_v.mInvMass = v0->mInvMass + (v1->mInvMass - v0->mInvMass) * fraction < 0.5f? 0.0f : 1.0f;
  268. ioVertices.push_back(out_v);
  269. }
  270. // Add the last vertex of the strand
  271. ioVertices.push_back(vertices[strand.mEndVtx - 1]);
  272. JPH_ASSERT(uint32(ioVertices.size()) == out_strand.mEndVtx);
  273. }
  274. }
  275. static void sHairSettingsFindClosestTriangle(Vec3Arg inPoint, const AABBTreeBuilder &inBuilder, const AABBTreeBuilder::Node *inNode, Array<Float3> &inScalpVertices, float &ioClosestDistSq, HairSettings::SkinPoint &outSkinPoint)
  276. {
  277. if (inNode->HasChildren())
  278. {
  279. // Get children
  280. const AABBTreeBuilder::Node *child0 = inNode->GetChild(0, inBuilder.GetNodes());
  281. const AABBTreeBuilder::Node *child1 = inNode->GetChild(1, inBuilder.GetNodes());
  282. // Order so that the first one is closest
  283. float dist_sq0 = child0 != nullptr? child0->mBounds.GetSqDistanceTo(inPoint) : FLT_MAX;
  284. float dist_sq1 = child1 != nullptr? child1->mBounds.GetSqDistanceTo(inPoint) : FLT_MAX;
  285. if (dist_sq1 < dist_sq0)
  286. {
  287. std::swap(child0, child1);
  288. std::swap(dist_sq0, dist_sq1);
  289. }
  290. // Visit in order of closeness
  291. if (dist_sq0 < ioClosestDistSq)
  292. sHairSettingsFindClosestTriangle(inPoint, inBuilder, child0, inScalpVertices, ioClosestDistSq, outSkinPoint);
  293. if (dist_sq1 < ioClosestDistSq)
  294. sHairSettingsFindClosestTriangle(inPoint, inBuilder, child1, inScalpVertices, ioClosestDistSq, outSkinPoint);
  295. }
  296. else
  297. {
  298. // Loop over the triangles
  299. for (const IndexedTriangle *t = inBuilder.GetTriangles().data() + inNode->mTrianglesBegin, *t_end = t + inNode->mNumTriangles; t < t_end; ++t)
  300. {
  301. Vec3 v0 = Vec3(inScalpVertices[t->mIdx[0]]) - inPoint;
  302. Vec3 v1 = Vec3(inScalpVertices[t->mIdx[1]]) - inPoint;
  303. Vec3 v2 = Vec3(inScalpVertices[t->mIdx[2]]) - inPoint;
  304. // Check if it is the closest triangle
  305. uint32 set;
  306. Vec3 closest_point = ClosestPoint::GetClosestPointOnTriangle(v0, v1, v2, set);
  307. float dist_sq = closest_point.LengthSq();
  308. if (dist_sq < ioClosestDistSq)
  309. {
  310. ioClosestDistSq = dist_sq;
  311. outSkinPoint.mTriangleIndex = t->mMaterialIndex;
  312. // Get barycentric coordinates of attachment point
  313. float w;
  314. ClosestPoint::GetBaryCentricCoordinates(v0, v1, v2, outSkinPoint.mU, outSkinPoint.mV, w);
  315. }
  316. }
  317. }
  318. }
  319. void HairSettings::Init(float &outMaxDistSqHairToScalp)
  320. {
  321. outMaxDistSqHairToScalp = 0.0f;
  322. if (!mScalpTriangles.empty())
  323. {
  324. // Build a tree for all scalp triangles
  325. IndexedTriangleList triangles;
  326. triangles.reserve(mScalpTriangles.size());
  327. for (const IndexedTriangleNoMaterial &t : mScalpTriangles)
  328. triangles.push_back(IndexedTriangle(t.mIdx[0], t.mIdx[1], t.mIdx[2], uint32(&t - mScalpTriangles.data())));
  329. TriangleSplitterBinning splitter(mScalpVertices, triangles);
  330. AABBTreeBuilder builder(splitter, 8);
  331. AABBTreeBuilderStats builder_stats;
  332. const AABBTreeBuilder::Node *root = builder.Build(builder_stats);
  333. mSkinPoints.reserve(mSimStrands.size());
  334. for (const SStrand &strand : mSimStrands)
  335. {
  336. SkinPoint sp;
  337. sp.mTriangleIndex = 0;
  338. sp.mU = 0.0f;
  339. sp.mV = 0.0f;
  340. // Get root position
  341. Vec3 p = Vec3(mSimVertices[strand.mStartVtx].mPosition);
  342. // Find closest triangle on scalp
  343. float closest_dist_sq = FLT_MAX;
  344. sHairSettingsFindClosestTriangle(p, builder, root, mScalpVertices, closest_dist_sq, sp);
  345. outMaxDistSqHairToScalp = max(outMaxDistSqHairToScalp, closest_dist_sq);
  346. // Project root to the triangle as we will during simulation.
  347. // This ensures that we calculate the Bishop frame for the root correctly.
  348. const IndexedTriangleNoMaterial &t = mScalpTriangles[sp.mTriangleIndex];
  349. Vec3 v0 = Vec3(mScalpVertices[t.mIdx[0]]);
  350. Vec3 v1 = Vec3(mScalpVertices[t.mIdx[1]]);
  351. Vec3 v2 = Vec3(mScalpVertices[t.mIdx[2]]);
  352. p = sp.mU * v0 + sp.mV * v1 + (1.0f - sp.mU - sp.mV) * v2;
  353. p.StoreFloat3(&mSimVertices[strand.mStartVtx].mPosition);
  354. mSkinPoints.push_back(sp);
  355. }
  356. }
  357. Array<Vec3> r; // Outside loop to avoid reallocations
  358. Array<Vec3> x;
  359. Array<Vec3> k; // (bend_compliance, bend_compliance, stretch_compliance)
  360. Array<Vec3> g;
  361. Array<Quat> bishop;
  362. mMaxVerticesPerStrand = 0;
  363. for (const SStrand &strand : mSimStrands)
  364. {
  365. // Calculate max number of vertices per strand
  366. uint32 vertex_count = strand.VertexCount();
  367. mMaxVerticesPerStrand = max(mMaxVerticesPerStrand, vertex_count);
  368. // Calculate strand fraction for each vertex
  369. float total_length = strand.MeasureLength(mSimVertices);
  370. float cur_length = 0.0f;
  371. for (uint32 i = strand.mStartVtx; i < strand.mEndVtx - 1; ++i)
  372. {
  373. SVertex &v = mSimVertices[i];
  374. v.mStrandFraction = cur_length / total_length;
  375. cur_length += (Vec3(mSimVertices[i + 1].mPosition) - Vec3(v.mPosition)).Length();
  376. }
  377. mSimVertices[strand.mEndVtx - 1].mStrandFraction = 1.0f;
  378. // Particles
  379. // i=0 1 2
  380. // +------>+------>+
  381. // x1 x2
  382. //
  383. // Let r_i be the edge between particle i - 1 and i in the rest pose
  384. // Let x_i be the edge between particle i - 1 and i in the deformed pose
  385. //
  386. // The force on particle i is:
  387. // f_i = k_i * (r_i - x_i) - k_{i+1} * (r_{i+1} - x_{i+1})
  388. // Where k_i = 1 / compliance_i
  389. //
  390. // We want to counter gravity, so:
  391. // f_i = -m_i * g
  392. //
  393. // Rearranging gives:
  394. // x_{i+1} * k_{i+1} - x_i * k_i = k_{i+1} * r_{i+1} - k_i * r_i + m_i * g
  395. //
  396. // Solving this with Gauss Seidel iteration:
  397. // x_i = (k_i * r_i - k_{i+1} * (r_{i+1} - x_{i+1}) - m_i * g) / k_i
  398. r.resize(vertex_count); // Rest edge
  399. x.resize(vertex_count); // Deformed edge
  400. k.resize(vertex_count); // Spring constant
  401. g.resize(vertex_count); // Gravity
  402. bishop.resize(vertex_count);
  403. // First element unused
  404. x[0] = r[0] = g[0] = k[0] = Vec3::sNaN();
  405. const HairSettings::Material &material = mMaterials[strand.mMaterialIndex];
  406. HairSettings::GradientSampler gravity_sampler(material.mGravityFactor);
  407. for (uint32 i = 1; i < vertex_count; ++i)
  408. {
  409. const SVertex &v1 = mSimVertices[strand.mStartVtx + i - 1];
  410. const SVertex &v2 = mSimVertices[strand.mStartVtx + i];
  411. x[i] = r[i] = Vec3(v2.mPosition) - Vec3(v1.mPosition);
  412. constexpr float cMinCompliance = 1.0e-10f;
  413. float bend_compliance = 1.0f / max(cMinCompliance, material.GetBendCompliance(v2.mStrandFraction));
  414. float stretch_compliance = 1.0f / max(cMinCompliance, material.mStretchCompliance);
  415. k[i] = Vec3(bend_compliance, bend_compliance, stretch_compliance);
  416. g[i] = v2.mInvMass > 0.0f? (material.mGravityPreloadFactor / v2.mInvMass) * mInitialGravity * gravity_sampler.Sample(v2.mStrandFraction) : Vec3::sZero();
  417. }
  418. // Solve for x
  419. if (material.mGravityPreloadFactor > 0.0f)
  420. for (int iteration = 0; iteration < 10; ++iteration)
  421. {
  422. // Don't modify the 1st vertex since it's fixed
  423. // Loop backwards so that we can use the latest value of x[i + 1]
  424. for (uint32 i = vertex_count - 1; i >= 1; --i)
  425. {
  426. // Calculate reference frame for this edge
  427. Vec3 frame_x = x[i].Normalized();
  428. Vec3 frame_y = frame_x.GetNormalizedPerpendicular();
  429. Vec3 frame_z = frame_x.Cross(frame_y);
  430. Mat44 frame(Vec4(frame_y, 0), Vec4(frame_z, 0), Vec4(frame_x, 0), Vec4(0, 0, 0, 1));
  431. // Gauss Seidel iteration
  432. // Note that we take all quantities to local space so that we can separate bend and stretch compliance and apply those as a simple vector multiplication
  433. Vec3 x_local = k[i] * frame.Multiply3x3Transposed(r[i]) - frame.Multiply3x3Transposed(g[i]);
  434. if (i < vertex_count - 1)
  435. x_local -= k[i + 1] * frame.Multiply3x3Transposed(r[i + 1] - x[i + 1]);
  436. x[i] = frame.Multiply3x3(x_local / k[i]);
  437. }
  438. }
  439. // Calculate the Bishop frame for the first rod in the strand
  440. {
  441. SVertex &v1 = mSimVertices[strand.mStartVtx];
  442. Vec3 tangent = x[1];
  443. v1.mLength = tangent.Length();
  444. JPH_ASSERT(v1.mLength > 0.0f, "Rods of zero length are not supported!");
  445. tangent /= v1.mLength;
  446. Vec3 normal = tangent.GetNormalizedPerpendicular();
  447. Vec3 binormal = tangent.Cross(normal);
  448. bishop[0] = Mat44(Vec4(normal, 0), Vec4(binormal, 0), Vec4(tangent, 0), Vec4(0, 0, 0, 1)).GetQuaternion().Normalized();
  449. bishop[0].StoreFloat4(&v1.mBishop);
  450. }
  451. // Calculate the Bishop frames for the rest of the rods in the strand
  452. for (uint32 i = 1; i < vertex_count - 1; ++i)
  453. {
  454. SVertex &v1 = mSimVertices[strand.mStartVtx + i];
  455. const SVertex &v2 = mSimVertices[strand.mStartVtx + i + 1];
  456. // Get the normal and tangent of the first rod's Bishop frame (that was already calculated)
  457. Mat44 r1_frame = Mat44::sRotation(bishop[i - 1]);
  458. Vec3 tangent1 = r1_frame.GetAxisZ();
  459. Vec3 normal1 = r1_frame.GetAxisX();
  460. // Calculate the Bishop frame for the 2nd rod
  461. Vec3 tangent2 = x[i + 1];
  462. v1.mLength = tangent2.Length();
  463. JPH_ASSERT(v1.mLength > 0.0f, "Rods of zero length are not supported!");
  464. tangent2 /= v1.mLength;
  465. Vec3 t1_cross_t2 = tangent1.Cross(tangent2);
  466. float sin_angle = t1_cross_t2.Length();
  467. Vec3 normal2 = normal1;
  468. if (sin_angle > 1.0e-6f)
  469. {
  470. // Rotate normal2
  471. t1_cross_t2 /= sin_angle;
  472. normal2 = Quat::sRotation(t1_cross_t2, ASin(sin_angle)) * normal2;
  473. // Ensure normal2 is perpendicular to tangent2
  474. normal2 -= normal2.Dot(tangent2) * tangent2;
  475. normal2 = normal2.Normalized();
  476. }
  477. Vec3 binormal2 = tangent2.Cross(normal2);
  478. bishop[i] = Mat44(Vec4(normal2, 0), Vec4(binormal2, 0), Vec4(tangent2, 0), Vec4(0, 0, 0, 1)).GetQuaternion().Normalized();
  479. // Calculate the delta, used in simulation
  480. (bishop[i - 1].Conjugated() * bishop[i]).Normalized().StoreFloat4(&v1.mOmega0);
  481. // Calculate the Bishop frame in the modeled pose for initializing the simulation
  482. Vec3 modeled_tangent2 = (Vec3(v2.mPosition) - Vec3(v1.mPosition)).Normalized();
  483. Quat modeled_bishop = Quat::sFromTo(tangent2, modeled_tangent2) * bishop[i];
  484. modeled_bishop.StoreFloat4(&v1.mBishop);
  485. }
  486. // Copy Bishop frame to the last vertex
  487. mSimVertices[strand.mEndVtx - 1].mBishop = mSimVertices[strand.mEndVtx - 2].mBishop;
  488. }
  489. // Finalize skin points by calculating how to go from triangle frame to Bishop frame
  490. for (SkinPoint &sp : mSkinPoints)
  491. {
  492. const IndexedTriangleNoMaterial &t = mScalpTriangles[sp.mTriangleIndex];
  493. Vec3 v0 = Vec3(mScalpVertices[t.mIdx[0]]);
  494. Vec3 v1 = Vec3(mScalpVertices[t.mIdx[1]]);
  495. Vec3 v2 = Vec3(mScalpVertices[t.mIdx[2]]);
  496. // Get tangent vector
  497. Vec3 tangent = (v1 - v0).Normalized();
  498. // Get normal of the triangle
  499. Vec3 normal = tangent.Cross(v2 - v0).Normalized();
  500. // Calculate basis for the triangle
  501. Vec3 binormal = tangent.Cross(normal);
  502. Quat triangle_basis = Mat44(Vec4(normal, 0), Vec4(binormal, 0), Vec4(tangent, 0), Vec4(0, 0, 0, 1)).GetQuaternion();
  503. // Calculate how to rotate from the triangle basis to the Bishop frame of the root
  504. Quat to_bishop = triangle_basis.Conjugated() * Quat(mSimVertices[mSimStrands[&sp - mSkinPoints.data()].mStartVtx].mBishop);
  505. sp.mToBishop = to_bishop.CompressUnitQuat();
  506. }
  507. // Calculate the grid size
  508. mSimulationBounds = {};
  509. for (const SVertex &v : mSimVertices)
  510. mSimulationBounds.Encapsulate(Vec3(v.mPosition));
  511. mSimulationBounds.ExpandBy(mSimulationBoundsPadding);
  512. // Prepare neutral density grid
  513. mNeutralDensity.resize(mGridSize.GetX() * mGridSize.GetY() * mGridSize.GetZ(), 0.0f);
  514. GridSampler sampler(this);
  515. for (const SVertex &v : mSimVertices)
  516. if (v.mInvMass > 0.0f)
  517. {
  518. sampler.Sample(Vec3(v.mPosition), [this, &v](uint32 inIndex, float inFraction) {
  519. mNeutralDensity[inIndex] += inFraction / v.mInvMass;
  520. });
  521. }
  522. // Calculate density scale for drawing the grid
  523. mDensityScale = 0.0f;
  524. for (float density : mNeutralDensity)
  525. mDensityScale = max(mDensityScale, density);
  526. if (mDensityScale > 0.0f)
  527. mDensityScale = 1.0f / mDensityScale;
  528. // Prepare render vertices
  529. for (RVertex &v : mRenderVertices)
  530. {
  531. Vec3 render_pos(v.mPosition);
  532. float total_weight = 0.0f;
  533. for (SVertexInfluence &inf : v.mInfluences)
  534. if (inf.mVertexIndex != cNoInfluence)
  535. {
  536. const SVertex &simulated_vertex = mSimVertices[inf.mVertexIndex];
  537. Vec3 simulated_pos(simulated_vertex.mPosition);
  538. Vec3 local_position = Quat(simulated_vertex.mBishop).InverseRotate(render_pos - simulated_pos);
  539. local_position.StoreFloat3(&inf.mRelativePosition);
  540. // Weigh according to inverse distance to the simulated vertex
  541. inf.mWeight = 1.0f / (local_position.Length() + 1.0e-6f);
  542. total_weight += inf.mWeight;
  543. }
  544. else
  545. inf.mWeight = 0.0f;
  546. // Normalize weights
  547. if (total_weight > 0.0f)
  548. for (SVertexInfluence &a : v.mInfluences)
  549. if (a.mVertexIndex != cNoInfluence)
  550. a.mWeight /= total_weight;
  551. // Order so that largest weight comes first
  552. QuickSort(std::begin(v.mInfluences), std::end(v.mInfluences), [](const SVertexInfluence &inLHS, const SVertexInfluence &inRHS) {
  553. return inLHS.mWeight > inRHS.mWeight;
  554. });
  555. }
  556. }
  557. void HairSettings::InitCompute(ComputeSystem *inComputeSystem)
  558. {
  559. // Optional: We can attach the roots of the hairs to the scalp
  560. if (!mScalpTriangles.empty() && !mSkinPoints.empty())
  561. {
  562. mScalpTrianglesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, mScalpTriangles.size() * 3, sizeof(uint32), mScalpTriangles.data()).Get();
  563. mSkinPointsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, mSkinPoints.size(), sizeof(SkinPoint), mSkinPoints.data()).Get();
  564. // We can skin the scalp or the skinned vertices can be provided externally
  565. if (!mScalpVertices.empty() && !mScalpInverseBindPose.empty() && !mScalpSkinWeights.empty())
  566. {
  567. mScalpVerticesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, mScalpVertices.size(), sizeof(Float3), mScalpVertices.data()).Get();
  568. mScalpSkinWeightsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, mScalpSkinWeights.size(), sizeof(JPH_HairSkinWeight), mScalpSkinWeights.data()).Get();
  569. }
  570. }
  571. // Calculate the number of vertices for every strand
  572. Array<uint8> strand_vertex_counts;
  573. strand_vertex_counts.resize((mSimStrands.size() + sizeof(uint32) - 1) & ~(sizeof(uint32) - 1), 0); // Make size multiple of sizeof(uint32)
  574. for (size_t i = 0, n = mSimStrands.size(); i < n; ++i)
  575. {
  576. uint32 count = mSimStrands[i].VertexCount();
  577. JPH_ASSERT(count < 256);
  578. strand_vertex_counts[i] = (uint8)count;
  579. }
  580. // Calculate material index for every strand
  581. Array<uint8> strand_material_indices;
  582. strand_material_indices.resize((mSimStrands.size() + sizeof(uint32) - 1) & ~(sizeof(uint32) - 1), 0); // Make size multiple of sizeof(uint32)
  583. for (size_t i = 0, n = mSimStrands.size(); i < n; ++i)
  584. {
  585. uint32 material_index = mSimStrands[i].mMaterialIndex;
  586. JPH_ASSERT(material_index < 256);
  587. strand_material_indices[i] = (uint8)material_index;
  588. }
  589. // Create buffers that contain information about the rest pose of the hair
  590. // Rearrange vertices so that the first vertices of all strands are grouped together, then the second vertices, etc.
  591. uint num_vertices = uint(mMaxVerticesPerStrand * mSimStrands.size());
  592. Array<Float3> vertices_position;
  593. vertices_position.resize(num_vertices);
  594. Array<uint32> vertices_bishop;
  595. vertices_bishop.resize(num_vertices);
  596. Array<uint32> vertices_omega0;
  597. vertices_omega0.resize(num_vertices);
  598. Array<uint32> vertices_fixed;
  599. vertices_fixed.resize((num_vertices + 31) / 32, 0);
  600. Array<float> vertices_length;
  601. vertices_length.resize(num_vertices);
  602. Array<uint32> vertices_strand_fraction;
  603. vertices_strand_fraction.resize((num_vertices + 3) / 4, 0);
  604. for (size_t s = 0, ns = mSimStrands.size(); s < ns; ++s)
  605. {
  606. const SStrand &strand = mSimStrands[s];
  607. for (uint32 v = 0, nv = strand.VertexCount(); v < nv; ++v)
  608. {
  609. const SVertex &in_v = mSimVertices[strand.mStartVtx + v];
  610. size_t idx = v * mSimStrands.size() + s;
  611. vertices_position[idx] = in_v.mPosition;
  612. vertices_bishop[idx] = Vec4::sLoadFloat4(&in_v.mBishop).CompressUnitVector();
  613. vertices_omega0[idx] = Vec4::sLoadFloat4(&in_v.mOmega0).CompressUnitVector();
  614. vertices_length[idx] = in_v.mLength;
  615. if (in_v.mInvMass <= 0.0f)
  616. vertices_fixed[idx >> 5] |= uint32(1 << (idx & 31));
  617. vertices_strand_fraction[idx >> 2] |= uint32(in_v.mStrandFraction * 255.0f) << ((idx & 3) << 3);
  618. }
  619. }
  620. // Calculate a map from render vertex to strand index
  621. Array<uint32> simulation_vertex_to_strand_idx;
  622. simulation_vertex_to_strand_idx.resize(mSimVertices.size(), ~uint32(0));
  623. for (const SStrand &strand : mSimStrands)
  624. for (uint v = strand.mStartVtx; v < strand.mEndVtx; ++v)
  625. simulation_vertex_to_strand_idx[v] = uint32(&strand - mSimStrands.data());
  626. // Create buffer for simulated vertex influences
  627. Array<JPH_HairSVertexInfluence> svertex_influences;
  628. svertex_influences.resize(mRenderVertices.size() * cHairNumSVertexInfluences);
  629. for (size_t v = 0, n = mRenderVertices.size(); v < n; ++v)
  630. for (uint a = 0; a < cHairNumSVertexInfluences; ++a)
  631. {
  632. JPH_HairSVertexInfluence &inf = svertex_influences[v * cHairNumSVertexInfluences + a];
  633. inf = static_cast<const JPH_HairSVertexInfluence &>(mRenderVertices[v].mInfluences[a]);
  634. // Remap vertex index to reflect the transposing of the position buffer
  635. if (inf.mVertexIndex != cNoInfluence)
  636. {
  637. uint32 strand_idx = simulation_vertex_to_strand_idx[inf.mVertexIndex];
  638. uint32 start_vtx = mSimStrands[strand_idx].mStartVtx;
  639. inf.mVertexIndex = strand_idx + (inf.mVertexIndex - start_vtx) * uint32(mSimStrands.size());
  640. }
  641. else
  642. {
  643. // The shader doesn't check if weight is zero, it just takes the vertex. Make sure the index points to something.
  644. inf.mVertexIndex = 0;
  645. }
  646. }
  647. mVerticesPositionCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, vertices_position.size(), sizeof(Float3), vertices_position.data()).Get();
  648. mVerticesBishopCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, vertices_bishop.size(), sizeof(uint32), vertices_bishop.data()).Get();
  649. mVerticesOmega0CB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, vertices_omega0.size(), sizeof(uint32), vertices_omega0.data()).Get();
  650. mVerticesLengthCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, vertices_length.size(), sizeof(float), vertices_length.data()).Get();
  651. mVerticesFixedCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, vertices_fixed.size(), sizeof(uint32), vertices_fixed.data()).Get();
  652. mVerticesStrandFractionCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, vertices_strand_fraction.size(), sizeof(uint32), vertices_strand_fraction.data()).Get();
  653. mStrandVertexCountsCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, strand_vertex_counts.size() / sizeof(uint32), sizeof(uint32), strand_vertex_counts.data()).Get();
  654. mStrandMaterialIndexCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, strand_material_indices.size() / sizeof(uint32), sizeof(uint32), strand_material_indices.data()).Get();
  655. mNeutralDensityCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, mNeutralDensity.size(), sizeof(float), mNeutralDensity.data()).Get();
  656. mSVertexInfluencesCB = inComputeSystem->CreateComputeBuffer(ComputeBuffer::EType::Buffer, mRenderVertices.size() * cHairNumSVertexInfluences, sizeof(JPH_HairSVertexInfluence), svertex_influences.data()).Get();
  657. }
  658. void HairSettings::SaveBinaryState(StreamOut &inStream) const
  659. {
  660. inStream.Write(mSimVertices);
  661. inStream.Write(mSimStrands);
  662. inStream.Write(mRenderVertices);
  663. inStream.Write(mRenderStrands);
  664. inStream.Write(mScalpVertices);
  665. inStream.Write(mScalpTriangles);
  666. inStream.Write(mScalpInverseBindPose);
  667. inStream.Write(mScalpSkinWeights);
  668. inStream.Write(mScalpNumSkinWeightsPerVertex);
  669. inStream.Write(mNumIterationsPerSecond);
  670. inStream.Write(mMaxDeltaTime);
  671. inStream.Write(mGridSize);
  672. inStream.Write(mSimulationBoundsPadding);
  673. inStream.Write(mInitialGravity);
  674. inStream.Write(mMaterials, [](const Material &inElement, StreamOut &inS) {
  675. inS.Write(inElement.mEnableCollision);
  676. inS.Write(inElement.mEnableLRA);
  677. inS.Write(inElement.mLinearDamping);
  678. inS.Write(inElement.mAngularDamping);
  679. inS.Write(inElement.mMaxLinearVelocity);
  680. inS.Write(inElement.mMaxAngularVelocity);
  681. inElement.mGravityFactor.SaveBinaryState(inS);
  682. inS.Write(inElement.mFriction);
  683. inS.Write(inElement.mBendCompliance);
  684. inS.Write(inElement.mBendComplianceMultiplier);
  685. inS.Write(inElement.mStretchCompliance);
  686. inS.Write(inElement.mInertiaMultiplier);
  687. inElement.mHairRadius.SaveBinaryState(inS);
  688. inElement.mWorldTransformInfluence.SaveBinaryState(inS);
  689. inElement.mGridVelocityFactor.SaveBinaryState(inS);
  690. inS.Write(inElement.mGridDensityForceFactor);
  691. inElement.mGlobalPose.SaveBinaryState(inS);
  692. inElement.mSkinGlobalPose.SaveBinaryState(inS);
  693. inS.Write(inElement.mSimulationStrandsFraction);
  694. inS.Write(inElement.mGravityPreloadFactor);
  695. });
  696. inStream.Write(mSkinPoints);
  697. inStream.Write(mSimulationBounds);
  698. inStream.Write(mNeutralDensity);
  699. inStream.Write(mDensityScale);
  700. inStream.Write(mMaxVerticesPerStrand);
  701. }
  702. void HairSettings::RestoreBinaryState(StreamIn &inStream)
  703. {
  704. inStream.Read(mSimVertices);
  705. inStream.Read(mSimStrands);
  706. inStream.Read(mRenderVertices);
  707. inStream.Read(mRenderStrands);
  708. inStream.Read(mScalpVertices);
  709. inStream.Read(mScalpTriangles);
  710. inStream.Read(mScalpInverseBindPose);
  711. inStream.Read(mScalpSkinWeights);
  712. inStream.Read(mScalpNumSkinWeightsPerVertex);
  713. inStream.Read(mNumIterationsPerSecond);
  714. inStream.Read(mMaxDeltaTime);
  715. inStream.Read(mGridSize);
  716. inStream.Read(mSimulationBoundsPadding);
  717. inStream.Read(mInitialGravity);
  718. inStream.Read(mMaterials, [](StreamIn &inS, Material &outElement) {
  719. inS.Read(outElement.mEnableCollision);
  720. inS.Read(outElement.mEnableLRA);
  721. inS.Read(outElement.mLinearDamping);
  722. inS.Read(outElement.mAngularDamping);
  723. inS.Read(outElement.mMaxLinearVelocity);
  724. inS.Read(outElement.mMaxAngularVelocity);
  725. outElement.mGravityFactor.RestoreBinaryState(inS);
  726. inS.Read(outElement.mFriction);
  727. inS.Read(outElement.mBendCompliance);
  728. inS.Read(outElement.mBendComplianceMultiplier);
  729. inS.Read(outElement.mStretchCompliance);
  730. inS.Read(outElement.mInertiaMultiplier);
  731. outElement.mHairRadius.RestoreBinaryState(inS);
  732. outElement.mWorldTransformInfluence.RestoreBinaryState(inS);
  733. outElement.mGridVelocityFactor.RestoreBinaryState(inS);
  734. inS.Read(outElement.mGridDensityForceFactor);
  735. outElement.mGlobalPose.RestoreBinaryState(inS);
  736. outElement.mSkinGlobalPose.RestoreBinaryState(inS);
  737. inS.Read(outElement.mSimulationStrandsFraction);
  738. inS.Read(outElement.mGravityPreloadFactor);
  739. });
  740. inStream.Read(mSkinPoints);
  741. inStream.Read(mSimulationBounds);
  742. inStream.Read(mNeutralDensity);
  743. inStream.Read(mDensityScale);
  744. inStream.Read(mMaxVerticesPerStrand);
  745. }
  746. void HairSettings::PrepareForScalpSkinning(Mat44Arg inJointToHair, const Mat44 *inJointMatrices, Mat44 *outJointMatrices) const
  747. {
  748. for (uint32 i = 0, n = (uint32)mScalpInverseBindPose.size(); i < n; ++i)
  749. outJointMatrices[i] = inJointToHair * inJointMatrices[i] * mScalpInverseBindPose[i];
  750. }
  751. void HairSettings::SkinScalpVertices(Mat44Arg inJointToHair, const Mat44 *inJointMatrices, Array<Vec3> &outVertices) const
  752. {
  753. outVertices.resize(mScalpVertices.size());
  754. // Pre transform all joint matrices
  755. Array<Mat44> joint_matrices;
  756. joint_matrices.resize((uint32)mScalpInverseBindPose.size());
  757. PrepareForScalpSkinning(inJointToHair, inJointMatrices, joint_matrices.data());
  758. // Skin all vertices
  759. for (uint32 i = 0; i < (uint32)mScalpVertices.size(); ++i)
  760. {
  761. Vec3 &v = outVertices[i];
  762. v = Vec3::sZero();
  763. for (const SkinWeight *w = mScalpSkinWeights.data() + i * mScalpNumSkinWeightsPerVertex, *w_end = w + mScalpNumSkinWeightsPerVertex; w < w_end; ++w)
  764. if (w->mWeight > 0.0f)
  765. v += w->mWeight * joint_matrices[w->mJointIdx] * Vec3(mScalpVertices[i]);
  766. }
  767. }
  768. JPH_NAMESPACE_END