Recently I came across this paper:
RBF Solver for Quaternions Interpolation by: Rinaldi Fabio, Dolci Daniele
Using their pseudocode:
SamplesPositions = LIST ()
SampleValues = LIST ()
MatrixT = Creatematrixfromlist(SamplesPositions)
MatrixS = CreatematrixfromList(SampleValues)
Epsilon = INT ()
MatrixD = ComputeDistanceMatrix(Matrix T)
Kernel = CreateKernel(SQUARED(MatrixD), Epsilon)
Invertedkernel = Kernel.Interpolate(INTERPOLATION TYPE).Invert()
WeightsMatrix = Invertedkernel∗MatrixS
CurrentPosition = MATRIX()
CurrentDistanceMatrix = ComputeDistanceMatrix(CurrentPosition)
Result = CurrentDistanceMatrix∗WeightsMatrix
We can build a simple MPxNode to interpolate Quaternions using Eigen. In the .gif below we have a simple scene to illustrate what we can do with this solver. We have 9 poses as inputs, and 9 rotations as inputValues, solve for the weights, and use a vector of the locator’s position as the driver as the current position:
In the next .gif, we took a jaw joint driven by an expression, and trained the RBF with Jaw controller as the driver, and the jaw joint as the driven. With 8 poses we have a good interpolating jaw joint.
This technique combined with more control on the input of the data type, we are able to create a pretty good RBF solver to handle almost anything we would like to drive.
Pro-Tip:
Eigen doesn’t seem to have built-in exp/log methods for quaternions, here is some code that can help with that:
Lionel Heng on github:
#ifndef QUATERNIONMAPPING_H
#define QUATERNIONMAPPING_H
namespace px
{
double sinc(const double x)
{
if (x == 0)
return 1;
return sin(x) / x;
}
template<typename T>
Eigen::Quaternion<T> expq(const Eigen::Quaternion<T>& q)
{
T a = q.vec().norm();
T exp_w = exp(q.w());
if (a == T(0))
{
return Eigen::Quaternion<T>(exp_w, 0, 0, 0);
}
Eigen::Quaternion<T> res;
res.w() = exp_w * T(cos(a));
res.vec() = exp_w * T(sinc(a)) * q.vec();
return res;
}
template<typename T>
Eigen::Quaternion<T> logq(const Eigen::Quaternion<T>& q)
{
T exp_w = q.norm();
T w = log(exp_w);
T a = acos(q.w() / exp_w);
if (a == T(0))
{
return Eigen::Quaternion<T>(w, T(0), T(0), T(0));
}
Eigen::Quaternion<T> res;
res.w() = w;
res.vec() = q.vec() / exp_w / (sin(a) / a);
return res;
}
}
#endif
~Cheers!
Thanks for mentioning the article! Cool stuff, I ended up writing my own math library, not really needed but it was a cool learning experience. Furthermore most of math library were 40 times heavier than the entire plugin given the amount of features they offer, so I thought it would be cooler to have something small and well optimized for the purpose 🙂
That’s amazing! Until I learn some more math, using Eigen seems ok for now.