direct_delta_mush.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. // This file is part of libigl, a simple C++ geometry processing library.
  2. //
  3. // Copyright (C) 2020 Xiangyu Kong <[email protected]>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #include "direct_delta_mush.h"
  9. #include "cotmatrix.h"
  10. template <
  11. typename DerivedV,
  12. typename DerivedF,
  13. typename DerivedC,
  14. typename DerivedE,
  15. typename DerivedOmega,
  16. typename DerivedU>
  17. IGL_INLINE void igl::direct_delta_mush(
  18. const Eigen::MatrixBase<DerivedV> & V,
  19. const Eigen::MatrixBase<DerivedF> & F,
  20. const Eigen::MatrixBase<DerivedC> & C,
  21. const Eigen::MatrixBase<DerivedE> & E,
  22. const std::vector<Eigen::Affine3d, Eigen::aligned_allocator<Eigen::Affine3d> > & T,
  23. const Eigen::MatrixBase<DerivedOmega> & Omega,
  24. Eigen::PlainObjectBase<DerivedU> & U)
  25. {
  26. using namespace Eigen;
  27. // Shape checks
  28. assert(V.cols() == 3 && "V should contain 3D positions.");
  29. assert(F.cols() == 3 && "F should contain triangles.");
  30. assert(C.cols() == 3 && "C should contain 3D bone endpoint positions.");
  31. assert(E.cols() == 2 && "E should contain 2 endpoint indices forming bone edges.");
  32. assert(E.rows() == T.size() && "E.rows() should equal to T.size()");
  33. assert(Omega.rows() == V.rows() && "Omega contain the same number of rows as V.");
  34. assert(Omega.cols() == T.size() * 10 && "Omega should have #T*10 columns.");
  35. typedef typename DerivedV::Scalar Scalar;
  36. int n = V.rows();
  37. int m = T.size();
  38. // V_homogeneous: #V by 4, homogeneous version of V
  39. // Note:
  40. // in the paper, the rest pose vertices are represented in U \in R^{4 \times #V}
  41. // Thus the formulae involving U would differ from the paper by a transpose.
  42. Matrix<Scalar, Dynamic, 4> V_homogeneous(n, 4);
  43. V_homogeneous << V, Matrix<Scalar, Dynamic, 1>::Ones(n, 1);
  44. U.resize(n, 3);
  45. for (int i = 0; i < n; ++i)
  46. {
  47. // Construct Q matrix using Omega and Transformations
  48. Matrix<Scalar, 4, 4> Q_mat(4, 4);
  49. Q_mat = Matrix<Scalar, 4, 4>::Zero(4, 4);
  50. for (int j = 0; j < m; ++j)
  51. {
  52. Matrix<typename DerivedOmega::Scalar, 4, 4> Omega_curr(4, 4);
  53. Matrix<typename DerivedOmega::Scalar, 10, 1> curr = Omega.block(i, j * 10, 1, 10).transpose();
  54. Omega_curr << curr(0), curr(1), curr(2), curr(3),
  55. curr(1), curr(4), curr(5), curr(6),
  56. curr(2), curr(5), curr(7), curr(8),
  57. curr(3), curr(6), curr(8), curr(9);
  58. Affine3d M_curr = T[j];
  59. Q_mat += M_curr.matrix() * Omega_curr;
  60. }
  61. // Normalize so that the last element is 1
  62. Q_mat /= Q_mat(Q_mat.rows() - 1, Q_mat.cols() - 1);
  63. Matrix<Scalar, 3, 3> Q_i = Q_mat.block(0, 0, 3, 3);
  64. Matrix<Scalar, 3, 1> q_i = Q_mat.block(0, 3, 3, 1);
  65. Matrix<Scalar, 3, 1> p_i = Q_mat.block(3, 0, 1, 3).transpose();
  66. // Get rotation and translation matrices using SVD
  67. Matrix<Scalar, 3, 3> SVD_i = Q_i - q_i * p_i.transpose();
  68. JacobiSVD<Matrix<Scalar, 3, 3>> svd;
  69. svd.compute(SVD_i, ComputeFullU | ComputeFullV);
  70. Matrix<Scalar, 3, 3> R_i = svd.matrixU() * svd.matrixV().transpose();
  71. Matrix<Scalar, 3, 1> t_i = q_i - R_i * p_i;
  72. // Gamma final transformation matrix
  73. Matrix<Scalar, 3, 4> Gamma_i(3, 4);
  74. Gamma_i.block(0, 0, 3, 3) = R_i;
  75. Gamma_i.block(0, 3, 3, 1) = t_i;
  76. // Final deformed position
  77. Matrix<Scalar, 4, 1> v_i = V_homogeneous.row(i);
  78. U.row(i) = Gamma_i * v_i;
  79. }
  80. }
  81. template <
  82. typename DerivedV,
  83. typename DerivedF,
  84. typename DerivedC,
  85. typename DerivedE,
  86. typename DerivedW,
  87. typename DerivedOmega>
  88. IGL_INLINE void igl::direct_delta_mush_precomputation(
  89. const Eigen::MatrixBase<DerivedV> & V,
  90. const Eigen::MatrixBase<DerivedF> & F,
  91. const Eigen::MatrixBase<DerivedC> & C,
  92. const Eigen::MatrixBase<DerivedE> & E,
  93. const Eigen::SparseMatrix<DerivedW> & W,
  94. const int p,
  95. const typename DerivedV::Scalar lambda,
  96. const typename DerivedV::Scalar kappa,
  97. const typename DerivedV::Scalar alpha,
  98. Eigen::PlainObjectBase<DerivedOmega> & Omega)
  99. {
  100. using namespace Eigen;
  101. // Shape checks
  102. assert(V.cols() == 3 && "V should contain 3D positions.");
  103. assert(F.cols() == 3 && "F should contain triangles.");
  104. assert(C.cols() == 3 && "C should contain 3D bone endpoint positions.");
  105. assert(E.cols() == 2 && "E should contain 2 endpoint indices forming bone edges.");
  106. assert(W.rows() == V.rows() && "W.rows() should be equal to V.rows().");
  107. assert(W.cols() == E.rows() && "W.cols() should be equal to E.rows().");
  108. // Parameter checks
  109. assert(p > 0 && "Laplacian iteration p should be positive.");
  110. assert(lambda > 0 && "lambda should be positive.");
  111. assert(kappa > 0 && kappa < lambda && "kappa should be positive and less than lambda.");
  112. assert(alpha >= 0 && alpha < 1 && "alpha should be non-negative and less than 1.");
  113. // This constraint is due to the explicit calculation of Psi.
  114. // If Psi is calculated implicitly, this upper bound would not be needed.
  115. assert(lambda <= 1 && "lambda should be less than or equal to 0");
  116. typedef typename DerivedV::Scalar Scalar;
  117. // lambda helper
  118. // Given a square matrix, extract the upper triangle (including diagonal) to an array.
  119. // E.g. 1 2 3 4
  120. // 5 6 7 8 -> [1, 2, 3, 4, 6, 7, 8, 11, 12, 16]
  121. // 9 10 11 12 0 1 2 3 4 5 6 7 8 9
  122. // 13 14 15 16
  123. auto extract_upper_triangle = [](
  124. const Matrix<Scalar, Dynamic, Dynamic> & full) -> Matrix<Scalar, Dynamic, 1>
  125. {
  126. int dims = full.rows();
  127. Matrix<Scalar, Dynamic, 1> upper_triangle((dims * (dims + 1)) / 2);
  128. int vector_idx = 0;
  129. for (int i = 0; i < dims; ++i)
  130. {
  131. for (int j = i; j < dims; ++j)
  132. {
  133. upper_triangle(vector_idx) = full(i, j);
  134. vector_idx++;
  135. }
  136. }
  137. return upper_triangle;
  138. };
  139. const int n = V.rows();
  140. const int m = E.rows();
  141. // V_homogeneous: #V by 4, homogeneous version of V
  142. // Note:
  143. // in the paper, the rest pose vertices are represented in U \in R^{4 \times #V}
  144. // Thus the formulae involving U would differ from the paper by a transpose.
  145. Matrix<Scalar, Dynamic, 4> V_homogeneous(n, 4);
  146. V_homogeneous << V, Matrix<Scalar, Dynamic, 1>::Ones(n);
  147. // Identity matrix of #V by #V
  148. SparseMatrix<Scalar> I(n, n);
  149. I.setIdentity();
  150. // Laplacian matrix of #V by #V
  151. // L_bar = L \times D_L^{-1}
  152. SparseMatrix<Scalar> L;
  153. igl::cotmatrix(V, F, L);
  154. L = -L;
  155. // Inverse of diagonal matrix = reciprocal elements in diagonal
  156. Matrix<Scalar, Dynamic, 1> D_L = L.diagonal();
  157. // D_L = D_L.array().pow(-1); // Not using this since not sure if diagonal contains 0
  158. for (int i = 0; i < D_L.size(); ++i)
  159. {
  160. if (D_L(i) != 0)
  161. {
  162. D_L(i) = 1 / D_L(i);
  163. }
  164. }
  165. SparseMatrix<Scalar> D_L_inv = D_L.asDiagonal().toDenseMatrix().sparseView();
  166. SparseMatrix<Scalar> L_bar = L * D_L_inv;
  167. // Implicitly and iteratively solve for W'
  168. // w'_{ij} = \sum_{k=1}^{n}{C_{ki} w_{kj}} where C = (I + kappa L_bar)^{-p}:
  169. // W' = C^T \times W => c^T W_k = W_{k-1} where c = (I + kappa L_bar)
  170. // C positive semi-definite => ldlt solver
  171. SimplicialLDLT<SparseMatrix<DerivedW>> ldlt_W_prime;
  172. SparseMatrix<Scalar> c(I + kappa * L_bar);
  173. SparseMatrix<DerivedW> W_prime(W);
  174. ldlt_W_prime.compute(c.transpose());
  175. for (int iter = 0; iter < p; ++iter)
  176. {
  177. W_prime.makeCompressed();
  178. W_prime = ldlt_W_prime.solve(W_prime);
  179. }
  180. // U_precomputed: #V by 10
  181. // Cache u_i^T \dot u_i \in R^{4 x 4} to reduce computation time in Psi.
  182. Matrix<Scalar, Dynamic, 10> U_precomputed(n, 10);
  183. for (int k = 0; k < n; ++k)
  184. {
  185. Matrix<Scalar, 4, 4> u_full = V_homogeneous.row(k).transpose() * V_homogeneous.row(k);
  186. U_precomputed.row(k) = extract_upper_triangle(u_full);
  187. }
  188. // Psi: #V by #T*10 of \Psi_{ij}s.
  189. // Note: this step deviates from the original paper
  190. // - The original paper calculates Psi implicitly and iteratively using
  191. // B = (I + lambda * L_bar)^{-p}, similar to the implicit calculation of W' using C (see above).
  192. // - I was not able to successfully vectorize Psi, so here I am explicitly computing Psi using
  193. // A = (I - lambda * L_bar)^{p} instead.
  194. // - The explicit method should produce similar result as the implicit method, but it poses
  195. // an additional constraint to the parameter: lambda <= 1.
  196. Matrix<Scalar, Dynamic, Dynamic> Psi(n, m * 10);
  197. SparseMatrix<Scalar> a(I - lambda * L_bar);
  198. SparseMatrix<Scalar> A(I);
  199. for (int i = 0; i < p; ++i)
  200. {
  201. A = A * a;
  202. }
  203. for (int i = 0; i < n; ++i)
  204. {
  205. for (int j = 0; j < m; ++j)
  206. {
  207. Matrix<Scalar, 10, 1> Psi_curr = Matrix<Scalar, 10, 1>::Zero(10);
  208. for (int k = 0; k < n; ++k)
  209. {
  210. Psi_curr += A.coeff(k, i) * W.coeff(k, j) * U_precomputed.row(k);
  211. }
  212. Psi.block(i, j * 10, 1, 10) = Psi_curr.transpose();
  213. }
  214. }
  215. // P: #V by 10 precomputed upper triangle of
  216. // p_i p_i^T , p_i
  217. // p_i^T , 1
  218. // where p_i = (\sum_{j=1}^{n} Psi_{ij})'s top right 3 by 1 column
  219. Matrix<Scalar, Dynamic, 10> P(n, 10);
  220. for (int i = 0; i < n; ++i)
  221. {
  222. Matrix<Scalar, 3, 1> p_i = Matrix<Scalar, 3, 1>::Zero(3);
  223. Scalar last = 0;
  224. for (int j = 0; j < m; ++j)
  225. {
  226. Matrix<Scalar, 3, 1> p_i_curr(3);
  227. p_i_curr << Psi(i, j * 10 + 3), Psi(i, j * 10 + 6), Psi(i, j * 10 + 8);
  228. p_i += p_i_curr;
  229. last += Psi(i, j * 10 + 9);
  230. }
  231. p_i /= last; // normalize
  232. Matrix<Scalar, 4, 4> p_matrix(4, 4);
  233. p_matrix.block(0, 0, 3, 3) = p_i * p_i.transpose();
  234. p_matrix.block(0, 3, 3, 1) = p_i;
  235. p_matrix.block(3, 0, 1, 3) = p_i.transpose();
  236. p_matrix(3, 3) = 1;
  237. P.row(i) = extract_upper_triangle(p_matrix);
  238. }
  239. // Omega
  240. Omega.resize(n, m * 10);
  241. for (int i = 0; i < n; ++i)
  242. {
  243. Matrix<Scalar, 10, 1> p_vector = P.row(i);
  244. for (int j = 0; j < m; ++j)
  245. {
  246. Matrix<Scalar, 10, 1> Omega_curr(10);
  247. Matrix<Scalar, 10, 1> Psi_curr = Psi.block(i, j * 10, 1, 10).transpose();
  248. Omega_curr = (1. - alpha) * Psi_curr + alpha * W_prime.coeff(i, j) * p_vector;
  249. Omega.block(i, j * 10, 1, 10) = Omega_curr.transpose();
  250. }
  251. }
  252. }
  253. #ifdef IGL_STATIC_LIBRARY
  254. // Explicit template instantiation
  255. template void
  256. igl::direct_delta_mush<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(
  257. Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const &,
  258. Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const &,
  259. Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const &,
  260. Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const &,
  261. std::__1::vector<Eigen::Transform<double, 3, 2, 0>, Eigen::aligned_allocator<Eigen::Transform<double, 3, 2, 0> > > const &,
  262. Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const &,
  263. Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > &);
  264. template void
  265. igl::direct_delta_mush_precomputation<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, double, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(
  266. Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const &,
  267. Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const &,
  268. Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const &,
  269. Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const &,
  270. Eigen::SparseMatrix<double, 0, int> const &, int,
  271. Eigen::Matrix<double, -1, -1, 0, -1, -1>::Scalar,
  272. Eigen::Matrix<double, -1, -1, 0, -1, -1>::Scalar,
  273. Eigen::Matrix<double, -1, -1, 0, -1, -1>::Scalar,
  274. Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > &);
  275. #endif