[Closed] Matrix Interpolation
Hi there,
The last couple of days, I’m trying to find a way to interpolate two Matrices by a percent.
After a lot of failed attempts, I’ve decided to try recreate Blur’s Transform Cache in C#.
Blur’s C++ code
static void comp_affine( const AffineParts &ap, Matrix3 &mat )
{
Matrix3 tm;
mat.IdentityMatrix();
mat.SetTrans( ap.t );
if ( ap.f != 1.0f ) { // has f component
tm.SetScale( Point3( ap.f, ap.f, ap.f ) );
mat = tm * mat;
}
if ( !ap.q.IsIdentity() ) { // has q rotation component
ap.q.MakeMatrix( tm );
mat = tm * mat;
}
if ( ap.k.x != 1.0f || ap.k.y != 1.0f || ap.k.z != 1.0f ) { // has k scale component
tm.SetScale( ap.k );
if ( !ap.u.IsIdentity() ) { // has u rotation component
Matrix3 utm;
ap.u.MakeMatrix( utm );
mat = Inverse( utm ) * tm * utm * mat;
} else {
mat = tm * mat;
}
}
}
// matrix interpolation
Matrix3 InterpolateMatrix3 ( Matrix3 matA, Matrix3 matB, float percentage )
{
AffineParts parts;
decomp_affine(matA,&parts);
parts.q.Normalize();
Point3 tempPos = parts.t;
Quat tempRot = parts.q;
Point3 tempScl = parts.k;
decomp_affine(matB,&parts);
parts.q.Normalize();
parts.q.MakeClosest(tempRot);
tempPos = tempPos + ( (parts.t - tempPos)*(float)percentage );
tempRot = Slerp(tempRot, parts.q , percentage);
tempScl = tempScl + ( (parts.k - tempScl)*(float)percentage );
parts.t = tempPos;
parts.q = tempRot;
parts.k = tempScl;
Matrix3 mat(0);
comp_affine(parts, mat);
return mat;
}
My Implementation in C#
static private IGlobal global = GlobalInterface.Instance;
static private IMatrix3 mat;
static private void comp_affine( IAffineParts ap, IMatrix3 mat )
{
IMatrix3 tm = global.Matrix3.Create();
mat.IdentityMatrix();
mat.SetTranslate(ap.T);
if (ap.F != 1.0f) // has f component
{
tm.SetScale(global.Point3.Create(ap.F, ap.F, ap.F));
mat = tm.Multiply(mat);
}
if (!ap.Q.IsIdentity) // has q rotation component
{
ap.Q.MakeMatrix(mat, false);
mat = tm.Multiply(mat);
}
if (ap.K.X != 1.0f || ap.K.Y != 1.0f || ap.K.Z != 1.0f) // has k scale component
{
tm.SetScale(ap.K);
if (!ap.U.IsIdentity) // has u rotation component
{
IMatrix3 utm = global.Matrix3.Create();
ap.U.MakeMatrix(utm, false);
mat = global.Inverse(utm).Multiply(tm).Multiply(utm).Multiply(mat);
}
else
{
mat = tm.Multiply(mat);
}
}
}
public static IMatrix3 InterpolateMatrix3(IMatrix3 matA, IMatrix3 matB, float percentage)
{
IAffineParts parts = global.AffineParts.Create();
global.DecompAffine(matA, parts);
parts.Q.Normalize();
IPoint3 tempPos = parts.T;
IQuat tempRot = parts.Q;
IPoint3 tempScl = parts.K;
global.DecompAffine(matB, parts);
parts.Q.Normalize();
parts.Q.MakeClosest(tempRot);
tempPos = tempPos.Add(parts.T.Subtract(tempPos).MultiplyBy(percentage));
tempRot = global.Slerp(tempRot, parts.Q, percentage);
tempScl = tempScl.Add(parts.K.Subtract(tempScl).MultiplyBy(percentage));
parts.T = tempPos;
parts.Q = tempRot;
parts.K = tempScl;
mat = global.Matrix3.Create();
mat.Zero();
comp_affine(parts, mat);
return mat;
}
MXS code
(
local
mat,
matA,
matB,
percent = .5;
--Create DotNet Matrix3 for the first key
u = GlobalInterface.Point3.create 1 0 0
v = GlobalInterface.Point3.create 0 1 0
n = GlobalInterface.Point3.create 0 0 1
t = GlobalInterface.Point3.create 10 0 0
matA = GlobalInterface.Matrix3.create u v n t
--Create DotNet Matrix3 for the second key
u = GlobalInterface.Point3.create 1 0 0
v = GlobalInterface.Point3.create 0 1 0
n = GlobalInterface.Point3.create 0 0 1
t = GlobalInterface.Point3.create -10 0 0
matB = GlobalInterface.Matrix3.create u v n t
--Find interpolation key
mat = myClass.InterpolateMatrix3 matA matB percent
--Convert to MXS Matrix3
row1 = [(mat.GetColumn3 1).x, (mat.GetColumn3 1).y, (mat.GetColumn3 1).z]
row2 = [(mat.GetColumn3 2).x, (mat.GetColumn3 2).y, (mat.GetColumn3 2).z]
row3 = [(mat.GetColumn3 3).x, (mat.GetColumn3 3).y, (mat.GetColumn3 3).z]
row4 = [(mat.GetColumn3 4).x, (mat.GetColumn3 4).y, (mat.GetColumn3 4).z]
matrix3 row1 row2 row3 row4
)
The result:
(matrix3 [0,1,0] [0,0,1] [0,0,-10] [1,0,0])
The code compiles fine, but no matter what values I type, I get the same (wrong) result.
Do you know what I miss?
Thanks,
Nick
hm… I’ve just realized that I get the following error “– Runtime error: dotNet runtime exception: Unable to cast object of type ‘Autodesk.Max.Wrappers.Point3’ to type ‘Autodesk.Max.IAffineParts’.” if I execute the following code
g = (dotNetClass "Autodesk.Max.GlobalInterface").instance
parts = g.AffineParts.Create()
parts.T
the parts has the following properties
.F : <System.Single>
.Implementation : <System.Object>, read-only
.INativeObject__Handle : <System.IntPtr>, read-only
.K : <Autodesk.Max.IPoint3>
.Q : <Autodesk.Max.IQuat>
.T : <Autodesk.Max.IPoint3>
.U : <Autodesk.Max.IQuat>
and everything seems to work fine, except from the Point3 properties. Is this probably a DotNet wrapper problem or something?
Your implementation looks correct, I have the same code in C++ working fine for me.
But the original interpolation method doesn’t care about of axis scale interpolation.
For example, if you rotate one matrix and then scale it not in the local space, this interpolation will fail.
In order to get a correct result for scale part you should add an interpolation of rotation stretch component as you did it for regular rotation. It’s a “U” component of AffineParts.
tempStretchRot = global.Slerp(tempStretchRot , parts.U, percentage);
Thanks Daniel for your reply,
here is an update to the code that kinda works
using Autodesk.Max;
using System.IO;
namespace realtimeuk
{
public class math
{
static private IGlobal global = GlobalInterface.Instance;
static private string contents = "";
static private IMatrix3 comp_affine( IAffineParts ap, IMatrix3 mat )
{
IMatrix3 tm = global.Matrix3.Create();
tm.IdentityMatrix();
mat.IdentityMatrix();
mat.SetTranslate(ap.T);
if (ap.F != 1.0f) // has f component
{
tm.SetScale(global.Point3.Create(ap.F, ap.F, ap.F));
mat = tm.Multiply(mat);
}
if (!ap.Q.IsIdentity) // has q rotation component
{
ap.Q.MakeMatrix(mat, false);
mat = tm.Multiply(mat);
}
if (ap.K.X != 1.0f || ap.K.Y != 1.0f || ap.K.Z != 1.0f) // has k scale component
{
tm.SetScale(ap.K);
if (!ap.U.IsIdentity) // has u rotation component
{
IMatrix3 utm = global.Matrix3.Create();
utm.IdentityMatrix();
ap.U.MakeMatrix(utm, false);
mat = global.Inverse(utm).Multiply(tm).Multiply(utm).Multiply(mat);
}
else
{
mat = tm.Multiply(mat);
}
}
return mat;
}
static public IMatrix3 InterpolateMatrix3(IMatrix3 matA, IMatrix3 matB, float percentage)
{
IAffineParts partsA = global.AffineParts.Create();
global.DecompAffine(matA, partsA);
partsA.Q.Normalize();
IPoint3 tempPos = partsA.T;
IQuat tempRot = partsA.Q;
IPoint3 tempScl = partsA.K;
IQuat tempStretchRot = partsA.U;
IAffineParts partsB = global.AffineParts.Create();
global.DecompAffine(matB, partsB);
partsB.Q.Normalize();
partsB.Q.MakeClosest(tempRot);
tempPos = tempPos.Add(partsB.T.Subtract(tempPos).MultiplyBy(percentage));
tempRot = global.Slerp(tempRot, partsB.Q, percentage);
tempScl = tempScl.Add(partsB.K.Subtract(tempScl).MultiplyBy(percentage));
tempStretchRot = global.Slerp(tempStretchRot, partsA.U, percentage);
partsB.T = tempPos;
partsB.Q = tempRot;
partsB.K = tempScl;
partsB.U = tempStretchRot;
contents += "$test.rotation = quat " + tempStretchRot.X.ToString() + " " + tempStretchRot.Y.ToString() + " " + tempStretchRot.Z.ToString() + " " + tempStretchRot.W.ToString() + "
";
contents += "$test.rotation = quat " + tempRot.X.ToString() + " " + tempRot.Y.ToString() + " " + tempRot.Z.ToString() + " " + tempRot.W.ToString() + "
";
contents += "$test.scale = [" + tempScl.X.ToString() + ", " + tempScl.Y.ToString() + ", " + tempScl.Z.ToString() + "]
";
contents += "$test.pos = [" + tempPos.X.ToString() + ", " + tempPos.Y.ToString() + ", " + tempPos.Z.ToString() + "]
";
IMatrix3 mat = global.Matrix3.Create();
mat.Zero();
mat = comp_affine(partsB, mat);
contents += "$test.transform = matrix3 ";
contents += "[" + mat.GetColumn3(0).X.ToString() + ", " + mat.GetColumn3(0).Y.ToString() + ", " + mat.GetColumn3(0).Z.ToString() + "] ";
contents += "[" + mat.GetColumn3(1).X.ToString() + ", " + mat.GetColumn3(1).Y.ToString() + ", " + mat.GetColumn3(1).Z.ToString() + "] ";
contents += "[" + mat.GetColumn3(2).X.ToString() + ", " + mat.GetColumn3(2).Y.ToString() + ", " + mat.GetColumn3(2).Z.ToString() + "] ";
contents += "[" + mat.GetColumn3(3).X.ToString() + ", " + mat.GetColumn3(3).Y.ToString() + ", " + mat.GetColumn3(3).Z.ToString() + "]
";
File.WriteAllText(@"C:\path.txt", contents);
return mat;
}
}
}
First of all, I’ve tried to add your code, did I did it right cause I don’t really understand how it works.
with this code I get the following results.
- Only translation
returns wrong Matrix but the printing on the text file, returns the correct position. - With rotation and scale
returns wrong Matrix but the printing on the text file, returns correct position and scale but wrong rotation.
In any case, the comp_affine function doesn’t seem to work not even for a simple translation. any ideas?
First of all, that line
tempStretchRot = global.Slerp(tempStretchRot, partsA.U, percentage);
should be replaced with that one
tempStretchRot = global.Slerp(tempStretchRot, partsB.U, percentage);
about the other problems,
I don’t work with C# Api, so I can not really say at first sight if there are some broken methods.
Hm, I think I’m stack with C#…
Can you please take a look the following MXS code?
--because MXS doesn't have a makeClosest function
fn makeClosest prevRot nextRot = (
prevDotNetQuat = ((dotNetClass "Autodesk.Max.GlobalInterface").instance).quat.create prevRot.x prevRot.y prevRot.z prevRot.w
nextDotNetQuat = ((dotNetClass "Autodesk.Max.GlobalInterface").instance).quat.create nextRot.x nextRot.y nextRot.z nextRot.w
newDotNetQuat = nextDotNetQuat.MakeClosest prevDotNetQuat
quat newDotNetQuat.x newDotNetQuat.y newDotNetQuat.z newDotNetQuat.w
)
(
start = 2
end = 4
sampleRate = 1
samples = for t=start to end+1 by sampleRate collect at time t $source.transform
cTime = (currentTime as float)/ticksPerFrame
samplePostMultiplied = (1/sampleRate)
modResult = mod cTime sampleRate
prevS = ((cTime - modResult - start)*samplePostMultiplied)+1
nextS = prevS + 1
prevTM = samples[prevS]
nextTM = samples[nextS]
nextF = modResult * samplePostMultiplied
prevF = 1 - nextF
nextRot = makeClosest prevTM.rotationPart nextTM.rotationPart
rotationValue = slerp prevTM.rotationPart nextRot nextF
returnMatrix = rotate (matrix3 1) rotationValue
returnMatrix.row1 *= ((length prevTM.row1)*prevF + (length nextTM.row1)*nextF)
returnMatrix.row2 *= ((length prevTM.row2)*prevF + (length nextTM.row2)*nextF)
returnMatrix.row3 *= ((length prevTM.row3)*prevF + (length nextTM.row3)*nextF)
returnMatrix.pos = prevTM.pos * prevF + nextTM.pos * nextF
$interpolated.transform = returnMatrix
)
It almost works… ( interpolation between two keyframes with sample 1)
I’ve also attached the max file.