HairSettings.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
  2. // SPDX-FileCopyrightText: 2026 Jorrit Rouwe
  3. // SPDX-License-Identifier: MIT
  4. #pragma once
  5. #include <Jolt/Core/Reference.h>
  6. #include <Jolt/Geometry/AABox.h>
  7. #include <Jolt/Geometry/IndexedTriangle.h>
  8. #include <Jolt/ObjectStream/SerializableObject.h>
  9. #include <Jolt/Compute/ComputeBuffer.h>
  10. #include <Jolt/Compute/ComputeSystem.h>
  11. #include <Jolt/Shaders/HairStructs.h>
  12. JPH_NAMESPACE_BEGIN
  13. class StreamOut;
  14. class StreamIn;
  15. /// This class defines the setup of a hair groom, it can be shared between multiple hair instances
  16. class JPH_EXPORT HairSettings : public RefTarget<HairSettings>
  17. {
  18. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, HairSettings)
  19. public:
  20. /// How much a vertex is influenced by a joint
  21. struct JPH_EXPORT SkinWeight : public JPH_HairSkinWeight
  22. {
  23. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SkinWeight)
  24. };
  25. /// Information about where a hair strand is attached to the scalp mesh
  26. struct JPH_EXPORT SkinPoint : public JPH_HairSkinPoint
  27. {
  28. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SkinPoint)
  29. };
  30. static constexpr uint32 cNoInfluence = ~uint32(0);
  31. /// Describes how a render vertex is influenced by a simulated vertex
  32. struct JPH_EXPORT SVertexInfluence : public JPH_HairSVertexInfluence
  33. {
  34. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SVertexInfluence)
  35. inline SVertexInfluence() { mVertexIndex = cNoInfluence; mRelativePosition = JPH_float3(0, 0, 0); mWeight = 0.0f; }
  36. };
  37. /// A render vertex
  38. struct JPH_EXPORT RVertex
  39. {
  40. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, RVertex)
  41. Float3 mPosition { 0, 0, 0 }; ///< Initial position of the vertex
  42. SVertexInfluence mInfluences[cHairNumSVertexInfluences]; ///< Attach to X simulated vertices (computed during Init)
  43. };
  44. /// A simulated vertex in a hair strand
  45. struct JPH_EXPORT SVertex
  46. {
  47. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SVertex)
  48. /// Constructor
  49. SVertex() = default;
  50. explicit SVertex(const Float3 &inPosition, float inInvMass = 1.0f) : mPosition(inPosition), mInvMass(inInvMass) { }
  51. Float3 mPosition { 0, 0, 0 }; ///< Initial position of the vertex in its modeled pose
  52. float mInvMass = 1.0f; ///< Inverse of the mass of the vertex
  53. float mLength = 0.0f; ///< Initial distance of this vertex to the next of the unloaded strand, computed by Init
  54. float mStrandFraction = 0.0f; ///< Fraction along the strand, 0 = start, 1 = end, computed by Init
  55. Float4 mBishop { 0, 0, 0, 1.0f }; ///< Bishop frame of the strand in its modeled pose, computed by Init
  56. Float4 mOmega0 { 0, 0, 0, 1.0f }; ///< Conjugate(Previous Bishop) * Bishop, defines the rotation difference between the previous rod and this one of the unloaded strand, computed by Init
  57. };
  58. /// A hair render strand
  59. struct JPH_EXPORT RStrand
  60. {
  61. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, RStrand)
  62. /// Constructor
  63. RStrand() = default;
  64. RStrand(uint32 inStartVtx, uint32 inEndVtx) : mStartVtx(inStartVtx), mEndVtx(inEndVtx) { }
  65. uint32 VertexCount() const { return mEndVtx - mStartVtx; }
  66. float MeasureLength(const Array<SVertex> &inVertices) const
  67. {
  68. float length = 0.0f;
  69. for (uint32 v = mStartVtx; v < mEndVtx - 1; ++v)
  70. length += (Vec3(inVertices[v + 1].mPosition) - Vec3(inVertices[v].mPosition)).Length();
  71. return length;
  72. }
  73. uint32 mStartVtx;
  74. uint32 mEndVtx;
  75. };
  76. /// A hair simulation strand
  77. struct JPH_EXPORT SStrand : public RStrand
  78. {
  79. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, SStrand)
  80. SStrand() = default;
  81. SStrand(uint32 inStartVtx, uint32 inEndVtx, uint32 inMaterialIndex) : RStrand(inStartVtx, inEndVtx), mMaterialIndex(inMaterialIndex) { }
  82. uint32 mMaterialIndex = 0; ///< Index in mMaterials
  83. };
  84. /// Gradient along a hair strand of a value, e.g. compliance, friction, etc.
  85. class JPH_EXPORT Gradient
  86. {
  87. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Gradient)
  88. public:
  89. Gradient() = default;
  90. Gradient(float inMin, float inMax, float inMinFraction = 0.0f, float inMaxFraction = 1.0f) : mMin(inMin), mMax(inMax), mMinFraction(inMinFraction), mMaxFraction(inMaxFraction) { }
  91. /// We drive a value to its target with fixed time steps using:
  92. ///
  93. /// x(t + fixed_dt) = target + (1 - k) * (x(t) - target)
  94. ///
  95. /// For varying time steps we can rewrite this to:
  96. ///
  97. /// x(t + dt) = target + (1 - k)^inTimeRatio * (x(t) - target)
  98. ///
  99. /// Where inTimeRatio is defined as dt / fixed_dt.
  100. ///
  101. /// This means k' = 1 - (1 - k)^inTimeRatio
  102. Gradient MakeStepDependent(float inTimeRatio) const
  103. {
  104. auto make_dependent = [inTimeRatio](float inValue) {
  105. return 1.0f - std::pow(1.0f - inValue, inTimeRatio);
  106. };
  107. return Gradient(make_dependent(mMin), make_dependent(mMax), mMinFraction, mMaxFraction);
  108. }
  109. /// Saves the state of this object in binary form to inStream. Doesn't store the compute buffers.
  110. void SaveBinaryState(StreamOut &inStream) const;
  111. /// Restore the state of this object from inStream.
  112. void RestoreBinaryState(StreamIn &inStream);
  113. float mMin = 0.0f; ///< Minimum value of the gradient
  114. float mMax = 1.0f; ///< Maximum value of the gradient
  115. float mMinFraction = 0.0f; ///< Fraction along the hair strand that corresponds to the minimum value
  116. float mMaxFraction = 1.0f; ///< Fraction along the hair strand that corresponds to the maximum value
  117. };
  118. class GradientSampler
  119. {
  120. public:
  121. GradientSampler() = default;
  122. explicit GradientSampler(const Gradient &inGradient) :
  123. mMultiplier((inGradient.mMax - inGradient.mMin) / (inGradient.mMaxFraction - inGradient.mMinFraction)),
  124. mOffset(inGradient.mMin - inGradient.mMinFraction * mMultiplier),
  125. mMin(min(inGradient.mMin, inGradient.mMax)),
  126. mMax(max(inGradient.mMin, inGradient.mMax))
  127. {
  128. }
  129. /// Sample the value along the strand
  130. inline float Sample(float inFraction) const
  131. {
  132. return min(mMax, max(mMin, mOffset + inFraction * mMultiplier));
  133. }
  134. inline float Sample(const SStrand &inStrand, uint32 inVertex) const
  135. {
  136. return Sample(float(inVertex - inStrand.mStartVtx) / float(inStrand.VertexCount() - 1));
  137. }
  138. /// Convert to Float4 to pass to shader
  139. inline Float4 ToFloat4() const
  140. {
  141. return Float4(mMultiplier, mOffset, mMin, mMax);
  142. }
  143. private:
  144. float mMultiplier;
  145. float mOffset;
  146. float mMin;
  147. float mMax;
  148. };
  149. /// The material determines the simulation parameters for a hair strand
  150. struct JPH_EXPORT Material
  151. {
  152. JPH_DECLARE_SERIALIZABLE_NON_VIRTUAL(JPH_EXPORT, Material)
  153. /// Returns if this material needs a density/velocity grid
  154. bool NeedsGrid() const { return mGridVelocityFactor.mMin != 0.0f || mGridVelocityFactor.mMax != 0.0f || mGridDensityForceFactor != 0.0f; }
  155. /// If this material only needs running the global pose logic
  156. bool GlobalPoseOnly() const { return !mEnableCollision && mGlobalPose.mMin == 1.0f && mGlobalPose.mMax == 1.0f; }
  157. /// Calculate the bend compliance at a fraction along the strand
  158. float GetBendCompliance(float inStrandFraction) const
  159. {
  160. float fraction = inStrandFraction * 3.0f;
  161. uint idx = min(uint(fraction), 2u);
  162. fraction = fraction - float(idx);
  163. JPH_ASSERT(fraction >= 0.0f && fraction <= 1.0f);
  164. float multiplier = mBendComplianceMultiplier[idx] * (1.0f - fraction) + mBendComplianceMultiplier[idx + 1] * fraction;
  165. return multiplier * mBendCompliance;
  166. }
  167. bool mEnableCollision = true; ///< Enable collision detection between hair strands and the environment.
  168. bool mEnableLRA = true; ///< Enable Long Range Attachments to keep hair close to the modeled pose. This prevents excessive stretching when the head moves quickly.
  169. float mLinearDamping = 2.0f; ///< Linear damping coefficient for the simulated rods.
  170. float mAngularDamping = 2.0f; ///< Angular damping coefficient for the simulated rods.
  171. float mMaxLinearVelocity = 10.0f; ///< Maximum linear velocity of a vertex.
  172. float mMaxAngularVelocity = 50.0f; ///< Maximum angular velocity of a vertex.
  173. Gradient mGravityFactor { 0.1f, 1.0f, 0.2f, 0.8f }; ///< How much gravity affects the hair along its length, 0 = no gravity, 1 = full gravity. Can be used to reduce the effect of gravity.
  174. float mFriction = 0.2f; ///< Collision friction coefficient. Usually in the range [0, 1]. 0 = no friction.
  175. float mBendCompliance = 1.0e-7f; ///< Compliance for bend constraints: 1 / stiffness.
  176. Float4 mBendComplianceMultiplier = { 1.0f, 100.0f, 100.0f, 1.0f }; ///< Multiplier for bend compliance at 0%, 33%, 66% and 100% of the strand length.
  177. float mStretchCompliance = 1.0e-8f; ///< Compliance for stretch constraints: 1 / stiffness.
  178. float mInertiaMultiplier = 10.0f; ///< Multiplier applied to the mass of a rod to calculate its inertia.
  179. Gradient mHairRadius = { 0.001f, 0.001f }; ///< Radius of the hair strand along its length, used for collision detection.
  180. Gradient mWorldTransformInfluence { 0.0f, 1.0f }; ///< How much rotating the head influences the hair, 0 = not at all, the hair will move with the head as if it had no inertia. 1 = hair stays in place as the head moves and is correctly simulated. This can be used to reduce the effect of turning the head towards the root of strands.
  181. Gradient mGridVelocityFactor { 0.05f, 0.01f }; ///< Every iteration this fraction of the grid velocity will be applied to the vertex velocity. Defined at cDefaultIterationsPerSecond, if this changes, the value will internally be adjusted to result in the same behavior.
  182. float mGridDensityForceFactor = 0.0f; ///< This factor will try to push the density of the hair towards the neutral density defined in the density grid. Note that can result in artifacts so defaults to 0.
  183. Gradient mGlobalPose { 0.01f, 0, 0.0f, 0.3f }; ///< Every iteration this fraction of the neutral pose will be applied to the vertex position. Defined at cDefaultIterationsPerSecond, if this changes, the value will internally be adjusted to result in the same behavior.
  184. Gradient mSkinGlobalPose { 1.0f, 0.0f, 0.0f, 0.1f }; ///< How much the global pose follows the skin of the scalp. 0 is not following, 1 is fully following.
  185. float mSimulationStrandsFraction = 0.1f; ///< Used by InitRenderAndSimulationStrands only. Indicates the fraction of strands that should be simulated.
  186. float mGravityPreloadFactor = 0.0f; ///< Note: Not fully functional yet! This controls how much of the gravity we will remove from the modeled pose when initializing. A value of 1 fully removes gravity and should result in no sagging when the simulation starts. A value of 0 doesn't remove gravity.
  187. };
  188. /// Split the supplied render strands into render and simulation strands and calculate connections between them.
  189. /// When this function returns mSimVertices, mSimStrands, mRenderVertices and mRenderStrands are overwritten.
  190. /// @param inVertices Vertices for the strands.
  191. /// @param inStrands The strands that this instance should have.
  192. void InitRenderAndSimulationStrands(const Array<SVertex> &inVertices, const Array<SStrand> &inStrands);
  193. /// Resample the hairs to a new fixed number of vertices per strand. Must be called prior to Init if desired.
  194. static void sResample(Array<SVertex> &ioVertices, Array<SStrand> &ioStrands, uint32 inNumVerticesPerStrand);
  195. /// Initialize the structure, calculating simulation bounds and vertex properties
  196. /// @param outMaxDistSqHairToScalp Maximum distance^2 the root vertex of a hair is from the scalp, can be used to check if the hair matched the scalp correctly
  197. void Init(float &outMaxDistSqHairToScalp);
  198. /// Must be called after Init to setup the compute buffers
  199. void InitCompute(ComputeSystem *inComputeSystem);
  200. /// Sample the neutral density at a grid position
  201. float GetNeutralDensity(uint32 inX, uint32 inY, uint32 inZ) const
  202. {
  203. JPH_ASSERT(inX < mGridSize.GetX() && inY < mGridSize.GetY() && inZ < mGridSize.GetZ());
  204. return mNeutralDensity[inX + inY * mGridSize.GetX() + inZ * mGridSize.GetX() * mGridSize.GetY()];
  205. }
  206. /// Get the number of vertices in the vertex buffers padded to a multiple of mMaxVerticesPerStrand.
  207. inline uint32 GetNumVerticesPadded() const
  208. {
  209. return uint32(mSimStrands.size()) * mMaxVerticesPerStrand;
  210. }
  211. /// @brief Calculates the pose used for skinning the scalp
  212. /// @param inJointToHair Transform to bring the model space joint matrices to the hair local space
  213. /// @param inJointMatrices Model space joint matrices of the joints in the face
  214. /// @param outJointMatrices Joint matrices combined with the inverse bind pose
  215. void PrepareForScalpSkinning(Mat44Arg inJointToHair, const Mat44 *inJointMatrices, Mat44 *outJointMatrices) const;
  216. /// Skin the scalp mesh to the given joint matrices and output the skinned scalp vertices
  217. /// @param inJointToHair Transform to bring the model space joint matrices to the hair local space
  218. /// @param inJointMatrices Model space joint matrices of the joints in the face
  219. /// @param outVertices Returns skinned vertices
  220. void SkinScalpVertices(Mat44Arg inJointToHair, const Mat44 *inJointMatrices, Array<Vec3> &outVertices) const;
  221. /// Saves the state of this object in binary form to inStream. Doesn't store the compute buffers.
  222. void SaveBinaryState(StreamOut &inStream) const;
  223. /// Restore the state of this object from inStream.
  224. void RestoreBinaryState(StreamIn &inStream);
  225. class GridSampler
  226. {
  227. public:
  228. inline explicit GridSampler(const HairSettings *inSettings) :
  229. mGridSizeMin2(inSettings->mGridSize - UVec4::sReplicate(2)),
  230. mGridSizeMin1((inSettings->mGridSize - UVec4::sReplicate(1)).ToFloat()),
  231. mGridStride(1, inSettings->mGridSize.GetX(), inSettings->mGridSize.GetX() * inSettings->mGridSize.GetY(), 0),
  232. mOffset(inSettings->mSimulationBounds.mMin),
  233. mScale(Vec3(inSettings->mGridSize.ToFloat()) / inSettings->mSimulationBounds.GetSize())
  234. {
  235. }
  236. /// Convert a position in hair space to a grid index and fraction
  237. inline void PositionToIndexAndFraction(Vec3Arg inPosition, UVec4 &outIndex, Vec3 &outFraction) const
  238. {
  239. // Get position in grid space
  240. Vec3 grid_pos = Vec3::sMin(Vec3::sMax(inPosition - mOffset, Vec3::sZero()) * mScale, mGridSizeMin1);
  241. outIndex = UVec4::sMin(Vec4(grid_pos).ToInt(), mGridSizeMin2);
  242. outFraction = grid_pos - Vec3(outIndex.ToFloat());
  243. }
  244. template <typename F>
  245. inline void Sample(UVec4Arg inIndex, Vec3Arg inFraction, const F &inFunc) const
  246. {
  247. Vec3 fraction[] = { Vec3::sReplicate(1.0f) - inFraction, inFraction };
  248. // Sample the grid
  249. for (uint32 z = 0; z < 2; ++z)
  250. for (uint32 y = 0; y < 2; ++y)
  251. for (uint32 x = 0; x < 2; ++x)
  252. {
  253. uint32 index = mGridStride.Dot(inIndex + UVec4(x, y, z, 0));
  254. float combined_fraction = fraction[x].GetX() * fraction[y].GetY() * fraction[z].GetZ();
  255. inFunc(index, combined_fraction);
  256. }
  257. }
  258. template <typename F>
  259. inline void Sample(Vec3Arg inPosition, const F &inFunc) const
  260. {
  261. UVec4 index;
  262. Vec3 fraction;
  263. PositionToIndexAndFraction(inPosition, index, fraction);
  264. Sample(index, fraction, inFunc);
  265. }
  266. UVec4 mGridSizeMin2;
  267. Vec3 mGridSizeMin1;
  268. UVec4 mGridStride;
  269. Vec3 mOffset;
  270. Vec3 mScale;
  271. };
  272. static constexpr uint32 cDefaultIterationsPerSecond = 360;
  273. Array<SVertex> mSimVertices; ///< Simulated vertices. Used by mSimStrands.
  274. Array<SStrand> mSimStrands; ///< Defines the start and end of each simulated strand.
  275. Array<RVertex> mRenderVertices; ///< Rendered vertices. Used by mRenderStrands.
  276. Array<RStrand> mRenderStrands; ///< Defines the start and end of each rendered strand.
  277. Array<Float3> mScalpVertices; ///< Vertices of the scalp mesh, used to attach hairs. Note that the hair vertices mSimVertices must be in the same space as these vertices.
  278. Array<IndexedTriangleNoMaterial> mScalpTriangles; ///< Triangles of the scalp mesh.
  279. Array<Mat44> mScalpInverseBindPose; ///< Inverse bind pose of the scalp mesh, joints are in model space
  280. Array<SkinWeight> mScalpSkinWeights; ///< Skin weights of the scalp mesh, for each vertex we have mScalpNumSkinWeightsPerVertex entries
  281. uint mScalpNumSkinWeightsPerVertex = 0; ///< Number of skin weights per vertex
  282. uint32 mNumIterationsPerSecond = cDefaultIterationsPerSecond;
  283. float mMaxDeltaTime = 1.0f / 30.0f; ///< Maximum delta time for the simulation step (to avoid running an excessively long step, note that this will effectively slow down time)
  284. UVec4 mGridSize { 32, 32, 32, 0 }; ///< Number of grid cells used to simulate the hair. W unused.
  285. Vec3 mSimulationBoundsPadding = Vec3::sReplicate(0.1f); ///< Padding around the simulation bounds to ensure that the grid is large enough and that we detect collisions with the hairs. This is added on all sides after calculating the bounds in the neutral pose.
  286. Vec3 mInitialGravity { 0, -9.81f, 0 }; ///< Initial gravity in local space of the hair, used to calculate the unloaded rest pose
  287. Array<Material> mMaterials; ///< Materials used by the hair strands
  288. // Values computed by Init
  289. Array<SkinPoint> mSkinPoints; ///< For each simulated vertex, where it is attached to the scalp mesh
  290. AABox mSimulationBounds { Vec3::sZero(), 1.0f }; ///< Bounds that the simulation is supposed to fit in
  291. Array<float> mNeutralDensity; ///< Neutral density grid used to apply forces to keep the hair in place
  292. float mDensityScale = 0.0f; ///< Highest density value in the neutral density grid, used to scale the density for rendering
  293. uint32 mMaxVerticesPerStrand = 0; ///< Maximum number of vertices per strand, used for padding the compute buffers
  294. // Compute data
  295. Ref<ComputeBuffer> mScalpVerticesCB;
  296. Ref<ComputeBuffer> mScalpTrianglesCB;
  297. Ref<ComputeBuffer> mScalpSkinWeightsCB;
  298. Ref<ComputeBuffer> mSkinPointsCB;
  299. Ref<ComputeBuffer> mVerticesFixedCB;
  300. Ref<ComputeBuffer> mVerticesPositionCB;
  301. Ref<ComputeBuffer> mVerticesBishopCB;
  302. Ref<ComputeBuffer> mVerticesOmega0CB;
  303. Ref<ComputeBuffer> mVerticesLengthCB;
  304. Ref<ComputeBuffer> mVerticesStrandFractionCB;
  305. Ref<ComputeBuffer> mStrandVertexCountsCB;
  306. Ref<ComputeBuffer> mStrandMaterialIndexCB;
  307. Ref<ComputeBuffer> mNeutralDensityCB;
  308. Ref<ComputeBuffer> mSVertexInfluencesCB;
  309. };
  310. JPH_NAMESPACE_END