RBF Solver for Quaternions Interpolation in Maya

Recently I came across this paper:
RBF Solver for Quaternions Interpolation by: Rinaldi FabioDolci 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!