Loading [MathJax]/extensions/TeX/AMSsymbols.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
DualQuaternionSkinning.cpp
1 #include <Core/Animation/DualQuaternionSkinning.hpp>
2 
3 #include <Core/Animation/SkinningData.hpp>
4 
5 namespace Ra {
6 namespace Core {
7 namespace Animation {
8 
9 DQList computeDQ( const Pose& pose, const Sparse& weight ) {
10  CORE_ASSERT( ( pose.size() == size_t( weight.cols() ) ), "pose/weight size mismatch." );
11  DQList DQ( weight.rows(),
12  DualQuaternion( Quaternion( 0, 0, 0, 0 ), Quaternion( 0, 0, 0, 0 ) ) );
13 
14  // Stores the first non-zero quaternion for each vertex.
15  std::vector<uint> firstNonZero( weight.rows(), std::numeric_limits<uint>::max() );
16  // Contains the converted dual quaternions from the pose
17  std::vector<DualQuaternion> poseDQ( pose.size() );
18 
19  // Loop through all transforms Tj
20  for ( int j = 0; j < weight.outerSize(); ++j ) {
21  poseDQ[j] = DualQuaternion( pose[j] );
22  // Count how many vertices are influenced by the given transform
23  const int nonZero = weight.col( j ).nonZeros();
24 
25  Sparse::InnerIterator it0( weight, j );
26 #pragma omp parallel for
27  // This for loop is here just because OpenMP wants classic for loops.
28  // Since we cannot iterate directly through the non-zero elements using the InnerIterator,
29  // we initialize an InnerIterator to the first element and then we increase it nz times.
30  /*
31  * This crappy piece of code was done in order to avoid the critical section
32  * DQ[i] += wq;
33  *
34  * that was occurring when parallelizing the main for loop.
35  *
36  * NOTE: this could be definitely improved by using std::thread
37  */
38  // Loop through all vertices vi who depend on Tj
39 
40  for ( int nz = 0; nz < nonZero; ++nz ) {
41  Sparse::InnerIterator itn = it0 + Eigen::Index( nz );
42  const uint i = itn.row();
43  const Scalar w = itn.value();
44 
45  firstNonZero[i] = std::min( firstNonZero[i], uint( j ) );
46  const Scalar sign =
47  Ra::Core::Math::signNZ( poseDQ[j].getQ0().dot( poseDQ[firstNonZero[i]].getQ0() ) );
48 
49  const auto wq = poseDQ[j] * w * sign;
50  DQ[i] += wq;
51  }
52  }
53 
54  // Normalize all dual quats.
55 #pragma omp parallel for
56  for ( int i = 0; i < int( DQ.size() ); ++i ) {
57  DQ[i].normalize();
58  }
59 
60  return DQ;
61 }
62 
63 // alternate naive version, for reference purposes.
64 // See Kavan , Collins, Zara and O'Sullivan, 2008
65 DQList computeDQ_naive( const Pose& pose, const Sparse& weight ) {
66  CORE_ASSERT( ( pose.size() == size_t( weight.cols() ) ), "pose/weight size mismatch." );
67  DQList DQ( weight.rows(),
68  DualQuaternion( Quaternion( 0, 0, 0, 0 ), Quaternion( 0, 0, 0, 0 ) ) );
69  std::vector<DualQuaternion> poseDQ( pose.size() );
70 
71  // 1. Convert all transforms to DQ
72 #pragma omp parallel for
73  for ( int j = 0; j < weight.cols(); ++j ) {
74  poseDQ[j] = DualQuaternion( pose[j] );
75  }
76 
77  // 2. for all vertices, blend the dual quats.
78  for ( int i = 0; i < weight.rows(); ++i ) {
79  int firstNonZero = -1;
80  for ( uint j = 0; j < weight.cols(); ++j ) {
81  const Scalar& w = weight.coeff( i, j );
82  if ( w == 0 ) { continue; }
83 
84  if ( firstNonZero < 0 ) { firstNonZero = j; }
85 
86  // Flip the dual quaternion sign according to the dot product with the first non-null
87  // dual quat see Algorithm 2 in section 4.1 of the paper.
88  Scalar sign =
89  Ra::Core::Math::signNZ( poseDQ[j].getQ0().dot( poseDQ[firstNonZero].getQ0() ) );
90 
91  DQ[i] += sign * w * poseDQ[j];
92  }
93  }
94 
95  // 3. renormalize all dual quats.
96 #pragma omp parallel for
97  for ( int i = 0; i < int( DQ.size() ); ++i ) {
98  DQ[i].normalize();
99  }
100 
101  return DQ;
102 }
103 
104 Vector3Array applyDualQuaternions( const DQList& DQ, const Vector3Array& vertices ) {
105  Vector3Array out( vertices.size(), Vector3::Zero() );
106 #pragma omp parallel for
107  for ( int i = 0; i < int( vertices.size() ); ++i ) {
108  out[i] = DQ[i].transform( vertices[i] );
109  }
110  return out;
111 }
112 
113 void dualQuaternionSkinning( const SkinningRefData& refData,
114  const Vector3Array& tangents,
115  const Vector3Array& bitangents,
116  SkinningFrameData& frameData ) {
117  // prepare the pose w.r.t. the bind matrices and the mesh tranform
118  auto pose = frameData.m_skeleton.getPose( HandleArray::SpaceType::MODEL );
119 #pragma omp parallel for
120  for ( int i = 0; i < int( frameData.m_skeleton.size() ); ++i ) {
121  pose[i] = refData.m_meshTransformInverse * pose[i] * refData.m_bindMatrices[i];
122  }
123  // compute the dual quaternion for each vertex
124  const auto DQ = computeDQ( pose, refData.m_weights );
125  // apply DQS
126  const auto& vertices = refData.m_referenceMesh.vertices();
127  const auto& normals = refData.m_referenceMesh.normals();
128 #pragma omp parallel for
129  for ( int i = 0; i < int( frameData.m_currentPosition.size() ); ++i ) {
130  const auto& DQi = DQ[i];
131  frameData.m_currentPosition[i] = DQi.transform( vertices[i] );
132  frameData.m_currentNormal[i] = DQi.rotate( normals[i] );
133  frameData.m_currentTangent[i] = DQi.rotate( tangents[i] );
134  frameData.m_currentBitangent[i] = DQi.rotate( bitangents[i] );
135  }
136 }
137 } // namespace Animation
138 } // namespace Core
139 } // namespace Ra
constexpr T signNZ(const T &val)
Definition: Math.hpp:114
constexpr int sign(const T &val)
Returns the sign of any numeric type as { -1, 0, 1}.
Definition: Math.hpp:106
Definition: Cage.cpp:3