public TransformTrack MakeTrack()
{
var track = new TransformTrack();
track.Flags = 0;
track.Name = Bone.Name;
var positions = Transforms.Select(m => m.ExtractTranslation()).ToList();
var rotations = Transforms.Select(m => m.ExtractRotation()).ToList();
var scales = Transforms.Select(m => ScaleToScaleShear(m.ExtractScale())).ToList();
// Quaternion sign fixup
// Since GR2 interpolation operates on the raw XYZ values of the quaternion, two subsequent quaternions
// that express the same rotation (eg. [1, 0.5, 0.5, -0.5] and [1, -0.5, -0.5, 0.5]) will result in a 360 deg
// rotation during the animation. Shuffle XYZ signs around to make this less likely to happen
for (var i = 1; i < rotations.Count; i++)
{
var r0 = rotations[i - 1];
var r1 = rotations[i];
var dot = r0.W * r1.W + r0.X * r1.X + r0.Y * r1.Y + r0.Z * r1.Z;
if (dot < 0.0)
{
rotations[i] = new Quaternion(-r1.X, -r1.Y, -r1.Z, r1.W);
}
}
var posTimes = Times;
var minPositions = positions;
RemoveTrivialFrames(ref posTimes, ref minPositions);
if (minPositions.Count == 1)
{
var posCurve = new D3Constant32f();
posCurve.CurveDataHeader_D3Constant32f = new CurveDataHeader { Format = (int)CurveFormat.D3Constant32f, Degree = 2 };
posCurve.Controls = new float[3] { positions[0].X, positions[0].Y, positions[0].Z };
track.PositionCurve = new AnimationCurve { CurveData = posCurve };
}
else
{
var posCurve = new DaK32fC32f();
posCurve.CurveDataHeader_DaK32fC32f = new CurveDataHeader { Format = (int)CurveFormat.DaK32fC32f, Degree = 2 };
posCurve.SetKnots(Times);
posCurve.SetPoints(positions);
track.PositionCurve = new AnimationCurve { CurveData = posCurve };
}
var rotTimes = Times;
var minRotations = rotations;
RemoveTrivialFrames(ref rotTimes, ref minRotations);
if (minRotations.Count == 1)
{
var rotCurve = new D4Constant32f();
rotCurve.CurveDataHeader_D4Constant32f = new CurveDataHeader { Format = (int)CurveFormat.D4Constant32f, Degree = 2 };
rotCurve.Controls = new float[4] { rotations[0].X, rotations[0].Y, rotations[0].Z, rotations[0].W };
track.OrientationCurve = new AnimationCurve { CurveData = rotCurve };
}
else
{
var rotCurve = new DaK32fC32f();
rotCurve.CurveDataHeader_DaK32fC32f = new CurveDataHeader { Format = (int)CurveFormat.DaK32fC32f, Degree = 2 };
rotCurve.SetKnots(Times);
rotCurve.SetQuaternions(rotations);
track.OrientationCurve = new AnimationCurve { CurveData = rotCurve };
}
var scaleTimes = Times;
var minScales = scales;
RemoveTrivialFrames(ref scaleTimes, ref minScales);
if (minScales.Count == 1)
{
var scaleCurve = new DaConstant32f();
scaleCurve.CurveDataHeader_DaConstant32f = new CurveDataHeader { Format = (int)CurveFormat.DaConstant32f, Degree = 2 };
var m = minScales[0];
scaleCurve.Controls = new List<float>
{
m[0, 0], m[0, 1], m[0, 2],
m[1, 0], m[1, 1], m[1, 2],
m[2, 0], m[2, 1], m[2, 2]
};
track.ScaleShearCurve = new AnimationCurve { CurveData = scaleCurve };
}
else
{
var scaleCurve = new DaK32fC32f();
scaleCurve.CurveDataHeader_DaK32fC32f = new CurveDataHeader { Format = (int)CurveFormat.DaK32fC32f, Degree = 2 };
scaleCurve.SetKnots(Times);
scaleCurve.SetMatrices(scales);
track.ScaleShearCurve = new AnimationCurve { CurveData = scaleCurve };
}
return track;
}
}