1#include <Core/Animation/DualQuaternionSkinning.hpp>
2#include <Core/Animation/HandleArray.hpp>
3#include <Core/Animation/Skeleton.hpp>
4#include <Core/Animation/SkinningData.hpp>
5#include <Core/CoreMacros.hpp>
6#include <Core/Geometry/IndexedGeometry.hpp>
7#include <Core/Math/DualQuaternion.hpp>
8#include <Core/Math/Math.hpp>
11#include <Eigen/Geometry>
12#include <Eigen/SparseCore>
22DQList computeDQ(
const Pose& pose,
const Sparse& weight ) {
23 CORE_ASSERT( ( pose.size() ==
size_t( weight.cols() ) ),
"pose/weight size mismatch." );
24 DQList DQ( weight.rows(),
25 DualQuaternion( Quaternion( 0, 0, 0, 0 ), Quaternion( 0, 0, 0, 0 ) ) );
33 for (
int j = 0; j < weight.outerSize(); ++j ) {
34 poseDQ[j] = DualQuaternion( pose[j] );
36 const int nonZero = weight.col( j ).nonZeros();
38 Sparse::InnerIterator it0( weight, j );
39#pragma omp parallel for
53 for (
int nz = 0; nz < nonZero; ++nz ) {
54 Sparse::InnerIterator itn = it0 + Eigen::Index( nz );
55 const uint i = itn.row();
56 const Scalar w = itn.value();
58 firstNonZero[i] =
std::min( firstNonZero[i], uint( j ) );
62 const auto wq = poseDQ[j] * w *
sign;
68#pragma omp parallel for
69 for (
int i = 0; i < int( DQ.size() ); ++i ) {
78DQList computeDQ_naive(
const Pose& pose,
const Sparse& weight ) {
79 CORE_ASSERT( ( pose.size() ==
size_t( weight.cols() ) ),
"pose/weight size mismatch." );
80 DQList DQ( weight.rows(),
81 DualQuaternion( Quaternion( 0, 0, 0, 0 ), Quaternion( 0, 0, 0, 0 ) ) );
85#pragma omp parallel for
86 for (
int j = 0; j < weight.cols(); ++j ) {
87 poseDQ[j] = DualQuaternion( pose[j] );
91 for (
int i = 0; i < weight.rows(); ++i ) {
92 int firstNonZero = -1;
93 for ( uint j = 0; j < weight.cols(); ++j ) {
94 const Scalar& w = weight.coeff( i, j );
95 if ( w == 0 ) {
continue; }
97 if ( firstNonZero < 0 ) { firstNonZero = j; }
104 DQ[i] +=
sign * w * poseDQ[j];
109#pragma omp parallel for
110 for (
int i = 0; i < int( DQ.size() ); ++i ) {
117Vector3Array applyDualQuaternions(
const DQList& DQ,
const Vector3Array& vertices ) {
118 Vector3Array out( vertices.size(), Vector3::Zero() );
119#pragma omp parallel for
120 for (
int i = 0; i < int( vertices.size() ); ++i ) {
121 out[i] = DQ[i].transform( vertices[i] );
126void dualQuaternionSkinning(
const SkinningRefData& refData,
127 const Vector3Array& tangents,
128 const Vector3Array& bitangents,
129 SkinningFrameData& frameData ) {
131 auto pose = frameData.m_skeleton.getPose( HandleArray::SpaceType::MODEL );
132#pragma omp parallel for
133 for (
int i = 0; i < int( frameData.m_skeleton.size() ); ++i ) {
134 pose[i] = refData.m_meshTransformInverse * pose[i] * refData.m_bindMatrices[i];
137 const auto DQ = computeDQ( pose, refData.m_weights );
139 const auto& vertices = refData.m_referenceMesh.vertices();
140 const auto& normals = refData.m_referenceMesh.normals();
141#pragma omp parallel for
142 for (
int i = 0; i < int( frameData.m_currentPosition.size() ); ++i ) {
143 const auto& DQi = DQ[i];
144 frameData.m_currentPosition[i] = DQi.transform( vertices[i] );
145 frameData.m_currentNormal[i] = DQi.rotate( normals[i] );
146 frameData.m_currentTangent[i] = DQi.rotate( tangents[i] );
147 frameData.m_currentBitangent[i] = DQi.rotate( bitangents[i] );
constexpr T signNZ(const T &val)
constexpr int sign(const T &val)
Returns the sign of any numeric type as { -1, 0, 1}.
hepler function to manage enum as underlying types in VariableSet