1 #include <Core/Animation/RotationCenterSkinning.hpp>
4 #include <unordered_map>
6 #include <Core/Animation/DualQuaternionSkinning.hpp>
7 #include <Core/Animation/HandleWeight.hpp>
8 #include <Core/Animation/Pose.hpp>
9 #include <Core/Animation/SkinningData.hpp>
10 #include <Core/Geometry/TopologicalMesh.hpp>
11 #include <Core/Utils/Log.hpp>
17 using namespace Utils;
19 Scalar weightSimilarity(
const Eigen::SparseVector<Scalar>& v1w,
20 const Eigen::SparseVector<Scalar>& v2w,
22 const Scalar sigmaSq = sigma * sigma;
26 for ( Eigen::SparseVector<Scalar>::InnerIterator it1( v1w ); it1; ++it1 ) {
27 const uint j = it1.index();
28 const Scalar& W1j = it1.value();
29 const Scalar& W2j = v2w.coeff( it1.index() );
32 for ( Eigen::SparseVector<Scalar>::InnerIterator it2( v2w ); it2; ++it2 ) {
33 const uint k = it2.index();
34 const Scalar& W1k = v1w.coeff( it2.index() );
35 const Scalar& W2k = it2.value();
36 if ( j != k && W1k > 0 ) {
38 std::exp( -Math::ipow<2>( ( W1j * W2k ) - ( W1k * W2j ) ) / ( sigmaSq ) );
39 result += W1j * W1k * W2j * W2k * diff;
47 void computeCoR( SkinningRefData& dataInOut, Scalar sigma, Scalar weightEpsilon ) {
48 LOG( logDEBUG ) <<
"Precomputing CoRs";
55 Geometry::TriangleMesh triMesh;
56 triMesh.copy( dataInOut.m_referenceMesh );
57 Geometry::TopologicalMesh topoMesh( triMesh );
61 size_t operator()(
const Ra::Core::Vector3& lvalue )
const {
62 size_t hx = std::hash<Scalar>()( lvalue[0] );
63 size_t hy = std::hash<Scalar>()( lvalue[1] );
64 size_t hz = std::hash<Scalar>()( lvalue[2] );
65 return ( hx ^ ( hy << 1 ) ) ^ hz;
71 std::unordered_map<Ra::Core::Vector3, int, hash_vec> mapV2I;
72 for (
auto vit = topoMesh.vertices_begin(); vit != topoMesh.vertices_end(); ++vit ) {
73 mapV2I[topoMesh.point( *vit )] = vit->idx();
78 Eigen::SparseMatrix<Scalar, Eigen::RowMajor> subdivW;
79 const int numCols = dataInOut.m_weights.cols();
80 subdivW.resize( topoMesh.n_vertices(), numCols );
81 const auto& V = triMesh.vertices();
82 for ( std::size_t i = 0; i < V.size(); ++i ) {
83 subdivW.row( mapV2I[V[i]] ) = dataInOut.m_weights.row(
int( i ) );
90 const Scalar wEps2 = weightEpsilon * weightEpsilon;
91 Scalar maxWeightDistance = 0;
93 maxWeightDistance = 0;
96 std::vector<Geometry::TopologicalMesh::EdgeHandle> edgesToSplit;
99 for (
auto e_it = topoMesh.edges_begin(); e_it != topoMesh.edges_end(); ++e_it ) {
100 const auto& he0 = topoMesh.halfedge_handle( *e_it, 0 );
101 const auto& he1 = topoMesh.halfedge_handle( *e_it, 1 );
102 int v0 = topoMesh.to_vertex_handle( he0 ).idx();
103 int v1 = topoMesh.to_vertex_handle( he1 ).idx();
105 Scalar weightDistance = ( subdivW.row( v0 ) - subdivW.row( v1 ) ).squaredNorm();
107 maxWeightDistance = std::max( maxWeightDistance, weightDistance );
108 if ( weightDistance > wEps2 ) { edgesToSplit.push_back( *e_it ); }
110 LOG( logDEBUG ) <<
"Max weight distance is " << sqrt( maxWeightDistance );
115 edgesToSplit.begin(),
117 [&topoMesh, &subdivW](
const auto& a,
const auto& b ) {
118 const auto& a0 = topoMesh.to_vertex_handle( topoMesh.halfedge_handle( a, 0 ) );
119 const auto& a1 = topoMesh.to_vertex_handle( topoMesh.halfedge_handle( a, 1 ) );
120 Scalar la = ( subdivW.row( a0.idx() ) - subdivW.row( a1.idx() ) ).squaredNorm();
121 const auto& b0 = topoMesh.to_vertex_handle( topoMesh.halfedge_handle( b, 0 ) );
122 const auto& b1 = topoMesh.to_vertex_handle( topoMesh.halfedge_handle( b, 1 ) );
123 Scalar lb = ( subdivW.row( b0.idx() ) - subdivW.row( b1.idx() ) ).squaredNorm();
128 if ( !edgesToSplit.empty() ) {
129 LOG( logDEBUG ) <<
"Splitting " << edgesToSplit.size() <<
" edges";
130 int startIndex = subdivW.rows();
132 Eigen::SparseMatrix<Scalar, Eigen::RowMajor> newWeights(
133 startIndex + edgesToSplit.size(), numCols );
135 newWeights.topRows( startIndex ) = subdivW;
136 subdivW = newWeights;
140 for (
const auto& edge : edgesToSplit ) {
141 int v0 = topoMesh.to_vertex_handle( topoMesh.halfedge_handle( edge, 0 ) ).idx();
142 int v1 = topoMesh.to_vertex_handle( topoMesh.halfedge_handle( edge, 1 ) ).idx();
144 topoMesh.splitEdge( edge, 0.5f );
146 subdivW.row( startIndex + i ) = 0.5f * ( subdivW.row( v0 ) + subdivW.row( v1 ) );
150 }
while ( maxWeightDistance > wEps2 );
152 CORE_ASSERT( topoMesh.n_vertices() ==
size_t( subdivW.rows() ),
153 "Weights and vertices don't match" );
161 const uint nVerts = V.size();
162 dataInOut.m_CoR.clear();
163 dataInOut.m_CoR.resize( nVerts, Vector3::Zero() );
166 std::map<Geometry::TopologicalMesh::FaceHandle,
167 std::tuple<Vector3, Scalar, Eigen::SparseVector<Scalar>>>
169 for (
auto f_it = topoMesh.faces_begin(); f_it != topoMesh.faces_end(); ++f_it ) {
171 const auto& he0 = topoMesh.halfedge_handle( *f_it );
172 const auto& he1 = topoMesh.next_halfedge_handle( he0 );
173 const auto& he2 = topoMesh.next_halfedge_handle( he1 );
174 const auto& v0 = topoMesh.to_vertex_handle( he0 );
175 const auto& v1 = topoMesh.to_vertex_handle( he1 );
176 const auto& v2 = topoMesh.to_vertex_handle( he2 );
177 const auto& p0 = topoMesh.point( v0 );
178 const auto& p1 = topoMesh.point( v1 );
179 const auto& p2 = topoMesh.point( v2 );
180 const Vector3 centroid = ( p0 + p1 + p2 ) / 3.f;
181 const Scalar area = ( ( ( p1 - p0 ).cross( p2 - p0 ) ).norm() * 0.5 );
182 const Eigen::SparseVector<Scalar> triWeight =
184 ( subdivW.row( v0.idx() ) + subdivW.row( v1.idx() ) + subdivW.row( v2.idx() ) );
185 triangleData[*f_it] = std::make_tuple( centroid, area, triWeight );
188 #pragma omp parallel for
189 for (
int i = 0; i < int( nVerts ); ++i ) {
190 Vector3 cor( 0, 0, 0 );
191 Scalar sumweight = 0;
192 const Eigen::SparseVector<Scalar> Wi = subdivW.row( mapV2I[V[i]] );
195 for (
auto f_it = topoMesh.faces_begin(); f_it != topoMesh.faces_end(); ++f_it ) {
196 const auto& triData = triangleData[*f_it];
197 const Vector3& centroid = std::get<0>( triData );
198 Scalar area = std::get<1>( triData );
199 const auto& triWeight = std::get<2>( triData );
200 const Scalar s = weightSimilarity( Wi, triWeight, sigma );
201 cor += s * area * centroid;
202 sumweight += s * area;
206 if ( sumweight > 0 ) { dataInOut.m_CoR[i] = cor / sumweight; }
208 #if defined CORE_DEBUG
209 if ( i % 100 == 0 ) { LOG( logDEBUG ) <<
"CoR: " << i <<
" / " << nVerts; }
214 void centerOfRotationSkinning(
const SkinningRefData& refData,
215 const Vector3Array& tangents,
216 const Vector3Array& bitangents,
217 SkinningFrameData& frameData ) {
218 CORE_ASSERT( refData.m_CoR.size() == frameData.m_currentPosition.size(),
219 "Invalid center of rotations" );
221 const auto& W = refData.m_weights;
222 const auto& vertices = refData.m_referenceMesh.vertices();
223 const auto& normals = refData.m_referenceMesh.normals();
224 const auto& CoR = refData.m_CoR;
225 auto pose = frameData.m_skeleton.getPose( HandleArray::SpaceType::MODEL );
228 #pragma omp parallel for
229 for (
int i = 0; i < int( frameData.m_skeleton.size() ); ++i ) {
230 pose[i] = refData.m_meshTransformInverse * pose[i] * refData.m_bindMatrices[i];
233 const auto DQ = computeDQ( pose, W );
236 #pragma omp parallel for
237 for (
int i = 0; i < int( frameData.m_currentPosition.size() ); ++i ) {
238 frameData.m_currentPosition[i] = Vector3::Zero();
240 for (
int k = 0; k < W.outerSize(); ++k ) {
241 const int nonZero = W.col( k ).nonZeros();
242 WeightMatrix::InnerIterator it0( W, k );
243 #pragma omp parallel for
244 for (
int nz = 0; nz < nonZero; ++nz ) {
245 WeightMatrix::InnerIterator it = it0 + Eigen::Index( nz );
246 const uint i = it.row();
247 const uint j = it.col();
248 const Scalar w = it.value();
249 frameData.m_currentPosition[i] += w * ( pose[j] * CoR[i] );
254 #pragma omp parallel for
255 for (
int i = 0; i < int( frameData.m_currentPosition.size() ); ++i ) {
256 frameData.m_currentPosition[i] += DQ[i].rotate( vertices[i] - CoR[i] );
257 frameData.m_currentNormal[i] = DQ[i].rotate( normals[i] );
258 frameData.m_currentTangent[i] = DQ[i].rotate( tangents[i] );
259 frameData.m_currentBitangent[i] = DQ[i].rotate( bitangents[i] );