MeshCurvature.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/Matrix2x2.h>
  9. #include <Mathematics/Matrix3x3.h>
  10. // The MeshCurvature class estimates principal curvatures and principal
  11. // directions at the vertices of a manifold triangle mesh. The algorithm
  12. // is described in
  13. // https://www.geometrictools.com/Documentation/MeshDifferentialGeometry.pdf
  14. namespace WwiseGTE
  15. {
  16. template <typename Real>
  17. class MeshCurvature
  18. {
  19. public:
  20. MeshCurvature() = default;
  21. // The input to operator() is a triangle mesh with the specified
  22. // vertex buffer and index buffer. The number of elements of
  23. // 'indices' must be a multiple of 3, each triple of indices
  24. // (3*t, 3*t+1, 3*t+2) representing the triangle with vertices
  25. // (vertices[3*t], vertices[3*t+1], vertices[3*t+2]). The
  26. // singularity threshold is a small nonnegative number. It is
  27. // used to characterize whether the DWTrn matrix is singular. In
  28. // theory, set the threshold to zero. In practice you might have
  29. // to set this to a small positive number.
  30. void operator()(
  31. size_t numVertices, Vector3<Real> const* vertices,
  32. size_t numTriangles, unsigned int const* indices,
  33. Real singularityThreshold)
  34. {
  35. mNormals.resize(numVertices);
  36. mMinCurvatures.resize(numVertices);
  37. mMaxCurvatures.resize(numVertices);
  38. mMinDirections.resize(numVertices);
  39. mMaxDirections.resize(numVertices);
  40. // Compute the normal vectors for the vertices as an
  41. // area-weighted sum of the triangles sharing a vertex.
  42. Vector3<Real> vzero{ (Real)0, (Real)0, (Real)0 };
  43. std::fill(mNormals.begin(), mNormals.end(), vzero);
  44. unsigned int const* currentIndex = indices;
  45. for (size_t i = 0; i < numTriangles; ++i)
  46. {
  47. // Get vertex indices.
  48. unsigned int v0 = *currentIndex++;
  49. unsigned int v1 = *currentIndex++;
  50. unsigned int v2 = *currentIndex++;
  51. // Compute the normal (length provides a weighted sum).
  52. Vector3<Real> edge1 = vertices[v1] - vertices[v0];
  53. Vector3<Real> edge2 = vertices[v2] - vertices[v0];
  54. Vector3<Real> normal = Cross(edge1, edge2);
  55. mNormals[v0] += normal;
  56. mNormals[v1] += normal;
  57. mNormals[v2] += normal;
  58. }
  59. for (size_t i = 0; i < numVertices; ++i)
  60. {
  61. Normalize(mNormals[i]);
  62. }
  63. // Compute the matrix of normal derivatives.
  64. Matrix3x3<Real> mzero;
  65. std::vector<Matrix3x3<Real>> DNormal(numVertices, mzero);
  66. std::vector<Matrix3x3<Real>> WWTrn(numVertices, mzero);
  67. std::vector<Matrix3x3<Real>> DWTrn(numVertices, mzero);
  68. std::vector<bool> DWTrnZero(numVertices, false);
  69. currentIndex = indices;
  70. for (size_t i = 0; i < numTriangles; ++i)
  71. {
  72. // Get vertex indices.
  73. unsigned int v[3];
  74. v[0] = *currentIndex++;
  75. v[1] = *currentIndex++;
  76. v[2] = *currentIndex++;
  77. for (size_t j = 0; j < 3; j++)
  78. {
  79. unsigned int v0 = v[j];
  80. unsigned int v1 = v[(j + 1) % 3];
  81. unsigned int v2 = v[(j + 2) % 3];
  82. // Compute the edge direction from vertex v0 to vertex v1,
  83. // project it to the tangent plane of vertex v0 and
  84. // compute the difference of adjacent normals.
  85. Vector3<Real> E = vertices[v1] - vertices[v0];
  86. Vector3<Real> W = E - Dot(E, mNormals[v0]) * mNormals[v0];
  87. Vector3<Real> D = mNormals[v1] - mNormals[v0];
  88. for (int row = 0; row < 3; ++row)
  89. {
  90. for (int col = 0; col < 3; ++col)
  91. {
  92. WWTrn[v0](row, col) += W[row] * W[col];
  93. DWTrn[v0](row, col) += D[row] * W[col];
  94. }
  95. }
  96. // Compute the edge direction from vertex v0 to vertex v2,
  97. // project it to the tangent plane of vertex v0 and
  98. // compute the difference of adjacent normals.
  99. E = vertices[v2] - vertices[v0];
  100. W = E - Dot(E, mNormals[v0]) * mNormals[v0];
  101. D = mNormals[v2] - mNormals[v0];
  102. for (int row = 0; row < 3; ++row)
  103. {
  104. for (int col = 0; col < 3; ++col)
  105. {
  106. WWTrn[v0](row, col) += W[row] * W[col];
  107. DWTrn[v0](row, col) += D[row] * W[col];
  108. }
  109. }
  110. }
  111. }
  112. // Add in N*N^T to W*W^T for numerical stability. In theory 0*0^T
  113. // is added to D*W^T, but of course no update is needed in the
  114. // implementation. Compute the matrix of normal derivatives.
  115. for (size_t i = 0; i < numVertices; ++i)
  116. {
  117. for (int row = 0; row < 3; ++row)
  118. {
  119. for (int col = 0; col < 3; ++col)
  120. {
  121. WWTrn[i](row, col) = (Real)0.5 * WWTrn[i](row, col) +
  122. mNormals[i][row] * mNormals[i][col];
  123. DWTrn[i](row, col) *= (Real)0.5;
  124. }
  125. }
  126. // Compute the max-abs entry of D*W^T. If this entry is
  127. // (nearly) zero, flag the DNormal matrix as singular.
  128. Real maxAbs = (Real)0;
  129. for (int row = 0; row < 3; ++row)
  130. {
  131. for (int col = 0; col < 3; ++col)
  132. {
  133. Real absEntry = std::fabs(DWTrn[i](row, col));
  134. if (absEntry > maxAbs)
  135. {
  136. maxAbs = absEntry;
  137. }
  138. }
  139. }
  140. if (maxAbs < singularityThreshold)
  141. {
  142. DWTrnZero[i] = true;
  143. }
  144. DNormal[i] = DWTrn[i] * Inverse(WWTrn[i]);
  145. }
  146. // If N is a unit-length normal at a vertex, let U and V be
  147. // unit-length tangents so that {U, V, N} is an orthonormal set.
  148. // Define the matrix J = [U | V], a 3-by-2 matrix whose columns
  149. // are U and V. Define J^T to be the transpose of J, a 2-by-3
  150. // matrix. Let dN/dX denote the matrix of first-order derivatives
  151. // of the normal vector field. The shape matrix is
  152. // S = (J^T * J)^{-1} * J^T * dN/dX * J = J^T * dN/dX * J
  153. // where the superscript of -1 denotes the inverse; the formula
  154. // allows for J to be created from non-perpendicular vectors. The
  155. // matrix S is 2-by-2. The principal curvatures are the
  156. // eigenvalues of S. If k is a principal curvature and W is the
  157. // 2-by-1 eigenvector corresponding to it, then S*W = k*W (by
  158. // definition). The corresponding 3-by-1 tangent vector at the
  159. // vertex is a principal direction for k and is J*W.
  160. for (size_t i = 0; i < numVertices; ++i)
  161. {
  162. // Compute U and V given N.
  163. Vector3<Real> basis[3];
  164. basis[0] = mNormals[i];
  165. ComputeOrthogonalComplement(1, basis);
  166. Vector3<Real> const& U = basis[1];
  167. Vector3<Real> const& V = basis[2];
  168. if (DWTrnZero[i])
  169. {
  170. // At a locally planar point.
  171. mMinCurvatures[i] = (Real)0;
  172. mMaxCurvatures[i] = (Real)0;
  173. mMinDirections[i] = U;
  174. mMaxDirections[i] = V;
  175. continue;
  176. }
  177. // Compute S = J^T * dN/dX * J. In theory S is symmetric, but
  178. // because dN/dX is estimated, we must ensure that the
  179. // computed S is symmetric.
  180. Real s00 = Dot(U, DNormal[i] * U);
  181. Real s01 = Dot(U, DNormal[i] * V);
  182. Real s10 = Dot(V, DNormal[i] * U);
  183. Real s11 = Dot(V, DNormal[i] * V);
  184. Real avr = (Real)0.5 * (s01 + s10);
  185. Matrix2x2<Real> S{ s00, avr, avr, s11 };
  186. // Compute the eigenvalues of S (min and max curvatures).
  187. Real trace = S(0, 0) + S(1, 1);
  188. Real det = S(0, 0) * S(1, 1) - S(0, 1) * S(1, 0);
  189. Real discr = trace * trace - (Real)4.0 * det;
  190. Real rootDiscr = std::sqrt(std::max(discr, (Real)0));
  191. mMinCurvatures[i] = (Real)0.5* (trace - rootDiscr);
  192. mMaxCurvatures[i] = (Real)0.5* (trace + rootDiscr);
  193. // Compute the eigenvectors of S.
  194. Vector2<Real> W0{ S(0, 1), mMinCurvatures[i] - S(0, 0) };
  195. Vector2<Real> W1{ mMinCurvatures[i] - S(1, 1), S(1, 0) };
  196. if (Dot(W0, W0) >= Dot(W1, W1))
  197. {
  198. Normalize(W0);
  199. mMinDirections[i] = W0[0] * U + W0[1] * V;
  200. }
  201. else
  202. {
  203. Normalize(W1);
  204. mMinDirections[i] = W1[0] * U + W1[1] * V;
  205. }
  206. W0 = Vector2<Real>{ S(0, 1), mMaxCurvatures[i] - S(0, 0) };
  207. W1 = Vector2<Real>{ mMaxCurvatures[i] - S(1, 1), S(1, 0) };
  208. if (Dot(W0, W0) >= Dot(W1, W1))
  209. {
  210. Normalize(W0);
  211. mMaxDirections[i] = W0[0] * U + W0[1] * V;
  212. }
  213. else
  214. {
  215. Normalize(W1);
  216. mMaxDirections[i] = W1[0] * U + W1[1] * V;
  217. }
  218. }
  219. }
  220. void operator()(
  221. std::vector<Vector3<Real>> const& vertices,
  222. std::vector<unsigned int> const& indices,
  223. Real singularityThreshold)
  224. {
  225. operator()(vertices.size(), vertices.data(), indices.size() / 3,
  226. indices.data(), singularityThreshold);
  227. }
  228. inline std::vector<Vector3<Real>> const& GetNormals() const
  229. {
  230. return mNormals;
  231. }
  232. inline std::vector<Real> const& GetMinCurvatures() const
  233. {
  234. return mMinCurvatures;
  235. }
  236. inline std::vector<Real> const& GetMaxCurvatures() const
  237. {
  238. return mMaxCurvatures;
  239. }
  240. inline std::vector<Vector3<Real>> const& GetMinDirections() const
  241. {
  242. return mMinDirections;
  243. }
  244. inline std::vector<Vector3<Real>> const& GetMaxDirections() const
  245. {
  246. return mMaxDirections;
  247. }
  248. private:
  249. std::vector<Vector3<Real>> mNormals;
  250. std::vector<Real> mMinCurvatures;
  251. std::vector<Real> mMaxCurvatures;
  252. std::vector<Vector3<Real>> mMinDirections;
  253. std::vector<Vector3<Real>> mMaxDirections;
  254. };
  255. }