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>
19Scalar 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 ) {
39 result += W1j * W1k * W2j * W2k * diff;
47void 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 {
65 return ( hx ^ ( hy << 1 ) ) ^ hz;
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();
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;
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,
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() ) );
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; }
214void 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] );
T ipow(const T &x, uint exp)
Run-time exponent version.
hepler function to manage enum as underlying types in VariableSet