mirror of
https://github.com/KillzXGaming/Switch-Toolbox
synced 2024-11-10 15:14:55 +00:00
903 lines
35 KiB
C#
903 lines
35 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Syroot.NintenTools.NSW.Bfres;
|
|
using ResU = Syroot.NintenTools.Bfres;
|
|
using System.Windows;
|
|
using Syroot.Maths;
|
|
using BrawlLib.SSBB.ResourceNodes;
|
|
using BrawlLib.Wii.Animations;
|
|
|
|
namespace BrawlboxHelper
|
|
{
|
|
public class FSHUConverter
|
|
{
|
|
public static ResU.ShaderParamAnim Clr02Fshu(string FileName)
|
|
{
|
|
CLR0Node clr0 = NodeFactory.FromFile(null, FileName) as CLR0Node;
|
|
|
|
ResU.ShaderParamAnim fshu = new ResU.ShaderParamAnim();
|
|
fshu.FrameCount = clr0.FrameCount;
|
|
fshu.Name = clr0.Name;
|
|
fshu.Path = clr0.OriginalPath;
|
|
fshu.UserData = new ResU.ResDict<Syroot.NintenTools.Bfres.UserData>();
|
|
|
|
//Set flags
|
|
if (clr0.Loop)
|
|
fshu.Flags |= ResU.ShaderParamAnimFlags.Looping;
|
|
|
|
//Set mat anims and then calculate data after
|
|
foreach (var entry in clr0.Children)
|
|
fshu.ShaderParamMatAnims.Add(Clr0Entry2ShaderMatAnim(clr0, (CLR0MaterialNode)entry));
|
|
|
|
fshu.BakedSize = CalculateBakeSize(fshu);
|
|
fshu.BindIndices = SetIndices(fshu);
|
|
|
|
return fshu;
|
|
}
|
|
|
|
public static ResU.ShaderParamMatAnim Clr0Entry2ShaderMatAnim(CLR0Node clr0, CLR0MaterialNode clrMaterial)
|
|
{
|
|
ResU.ShaderParamMatAnim matAnim = new ResU.ShaderParamMatAnim();
|
|
matAnim.Name = clrMaterial.Name;
|
|
foreach (var entry in clrMaterial.Children)
|
|
{
|
|
ushort curveIndex = 0;
|
|
ushort constantIndex = 0;
|
|
|
|
CLR0MaterialEntryNode ParamEntry = (CLR0MaterialEntryNode)entry;
|
|
|
|
//Add constants for RGBA if constant
|
|
if (ParamEntry.Constant)
|
|
{
|
|
//Red
|
|
matAnim.Constants.Add(new ResU.AnimConstant()
|
|
{
|
|
AnimDataOffset = 0,
|
|
Value = (float)ParamEntry.Colors[0].R / 255f,
|
|
});
|
|
|
|
//Green
|
|
matAnim.Constants.Add(new ResU.AnimConstant()
|
|
{
|
|
AnimDataOffset = 4,
|
|
Value = (float)ParamEntry.Colors[0].G / 255f,
|
|
});
|
|
|
|
//Blue
|
|
matAnim.Constants.Add(new ResU.AnimConstant()
|
|
{
|
|
AnimDataOffset = 8,
|
|
Value = (float)ParamEntry.Colors[0].B / 255f,
|
|
});
|
|
|
|
//Alpha
|
|
matAnim.Constants.Add(new ResU.AnimConstant()
|
|
{
|
|
AnimDataOffset = 12,
|
|
Value = (float)ParamEntry.Colors[0].A / 255f,
|
|
});
|
|
}
|
|
|
|
var RedCurve = GenerateCurve(0, clr0, ParamEntry);
|
|
var GreenCurve = GenerateCurve(4, clr0, ParamEntry);
|
|
var BlueCurve = GenerateCurve(8, clr0, ParamEntry);
|
|
var AlphaCurve = GenerateCurve(12, clr0, ParamEntry);
|
|
|
|
if (RedCurve != null)
|
|
matAnim.Curves.Add(RedCurve);
|
|
if (GreenCurve != null)
|
|
matAnim.Curves.Add(GreenCurve);
|
|
if (BlueCurve != null)
|
|
matAnim.Curves.Add(BlueCurve);
|
|
if (AlphaCurve != null)
|
|
matAnim.Curves.Add(AlphaCurve);
|
|
|
|
matAnim.ParamAnimInfos.Add(new ResU.ParamAnimInfo()
|
|
{
|
|
Name = entry.Name,
|
|
BeginCurve = matAnim.Curves.Count > 0 ? curveIndex : ushort.MaxValue,
|
|
FloatCurveCount = (ushort)matAnim.Curves.Count,
|
|
SubBindIndex = ushort.MaxValue,
|
|
ConstantCount = (ushort)matAnim.Constants.Count,
|
|
BeginConstant = matAnim.Constants.Count > 0 ? constantIndex : ushort.MaxValue,
|
|
});
|
|
|
|
constantIndex += (ushort)matAnim.Constants.Count;
|
|
curveIndex += (ushort)matAnim.Curves.Count;
|
|
}
|
|
|
|
return matAnim;
|
|
}
|
|
|
|
|
|
private static ResU.AnimCurve GenerateCurve(uint AnimOffset, CLR0Node anim, CLR0MaterialEntryNode entry)
|
|
{
|
|
ResU.AnimCurve curve = new ResU.AnimCurve();
|
|
curve.AnimDataOffset = AnimOffset;
|
|
curve.StartFrame = 0;
|
|
curve.Offset = 0;
|
|
curve.Scale = 1;
|
|
curve.FrameType = ResU.AnimCurveFrameType.Single;
|
|
curve.KeyType = ResU.AnimCurveKeyType.Single;
|
|
curve.CurveType = ResU.AnimCurveType.Linear;
|
|
|
|
List<float> Frames = new List<float>();
|
|
List<float> Keys = new List<float>();
|
|
|
|
for (int c = 0; c < entry.Colors.Count; c++)
|
|
{
|
|
Frames.Add(c);
|
|
//Max of 4 values. Cubic using 4, linear using 2, and step using 1
|
|
float[] KeyValues = new float[4];
|
|
|
|
switch (AnimOffset)
|
|
{
|
|
case 0: //Red
|
|
Keys.Add((float)entry.Colors[c].R / 255f);
|
|
break;
|
|
case 4: //Green
|
|
Keys.Add((float)entry.Colors[c].G / 255f);
|
|
break;
|
|
case 8: //Blue
|
|
Keys.Add((float)entry.Colors[c].B / 255f);
|
|
break;
|
|
case 12: //Alpha
|
|
Keys.Add((float)entry.Colors[c].A / 255f);
|
|
break;
|
|
default:
|
|
throw new Exception("Invalid animation offset set!");
|
|
}
|
|
}
|
|
|
|
//Max value in frames is our end frame
|
|
curve.EndFrame = Frames.Max();
|
|
curve.Frames = Frames.ToArray();
|
|
|
|
//If a curve only has one frame we don't need to interpolate or add keys to a curve as it's constant
|
|
if (curve.Frames.Length <= 1)
|
|
return null;
|
|
|
|
switch (curve.CurveType)
|
|
{
|
|
case ResU.AnimCurveType.Cubic:
|
|
curve.Keys = new float[Keys.Count, 4];
|
|
for (int frame = 0; frame < Keys.Count; frame++)
|
|
{
|
|
float Delta = 0;
|
|
|
|
if (frame < Keys.Count - 1)
|
|
Delta = Keys[frame + 1] - Keys[frame];
|
|
|
|
float value = Keys[frame];
|
|
float Slope = 0;
|
|
float Slope2 = 0;
|
|
|
|
curve.Keys[frame, 0] = value;
|
|
curve.Keys[frame, 1] = Slope;
|
|
curve.Keys[frame, 2] = Slope2;
|
|
curve.Keys[frame, 3] = Delta;
|
|
}
|
|
break;
|
|
case ResU.AnimCurveType.StepInt:
|
|
//Step requires no interpolation
|
|
curve.Keys = new float[Keys.Count, 1];
|
|
for (int frame = 0; frame < Keys.Count; frame++)
|
|
{
|
|
curve.Keys[frame, 0] = 0;
|
|
}
|
|
break;
|
|
case ResU.AnimCurveType.Linear:
|
|
curve.Keys = new float[Keys.Count, 2];
|
|
for (int frame = 0; frame < Keys.Count; frame++)
|
|
{
|
|
//Delta for second value used in linear curves
|
|
float time = curve.Frames[frame];
|
|
float Delta = 0;
|
|
|
|
if (frame < Keys.Count - 1)
|
|
Delta = Keys[frame + 1] - Keys[frame];
|
|
|
|
curve.Keys[frame, 0] = Keys[frame];
|
|
curve.Keys[frame, 1] = Delta;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return curve;
|
|
}
|
|
|
|
private static ushort[] SetIndices(ResU.ShaderParamAnim fshu)
|
|
{
|
|
List<ushort> indces = new List<ushort>();
|
|
foreach (var matAnim in fshu.ShaderParamMatAnims)
|
|
indces.Add(65535);
|
|
|
|
return indces.ToArray();
|
|
}
|
|
private static uint CalculateBakeSize(ResU.ShaderParamAnim fshu)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
public class FSKAConverter
|
|
{
|
|
static float Deg2Rad = (float)(Math.PI / 180f);
|
|
static float Rad2Deg = (float)(180f / Math.PI);
|
|
|
|
public static SkeletalAnim Anim2Fska(string FileName)
|
|
{
|
|
CHR0Node chr0 = AnimFormat.Read(FileName);
|
|
return Chr02Fska(chr0);
|
|
}
|
|
|
|
public static void Fska2Chr0(SkeletalAnim fska, string FileName)
|
|
{
|
|
|
|
CHR0Node chr0 = new CHR0Node();
|
|
chr0.FrameCount = fska.FrameCount;
|
|
chr0.Name = fska.Name;
|
|
chr0.OriginalPath = fska.Path;
|
|
chr0.UserEntries = new UserDataCollection();
|
|
chr0.Loop = fska.Loop;
|
|
|
|
foreach (var entry in fska.BoneAnims)
|
|
BoneAnim2Chr0Entry(entry, chr0);
|
|
|
|
chr0.Export(FileName);
|
|
}
|
|
|
|
public class FSKAKeyNode
|
|
{
|
|
public float Value = 0;
|
|
public float Slope = 0;
|
|
public float Slope2 = 0;
|
|
public float Delta = 0;
|
|
public float Frame = 0;
|
|
}
|
|
|
|
public static void BoneAnim2Chr0Entry(BoneAnim boneAnim, CHR0Node chr0)
|
|
{
|
|
CHR0EntryNode chr0Entry = chr0.CreateEntry(boneAnim.Name);
|
|
chr0Entry.UseModelRotate = false;
|
|
chr0Entry.UseModelScale = false;
|
|
chr0Entry.UseModelTranslate = false;
|
|
chr0Entry.ScaleCompensateApply = boneAnim.ApplySegmentScaleCompensate;
|
|
|
|
//Float for time/frame
|
|
Dictionary<float, FSKAKeyNode> TranslateX = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> TranslateY = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> TranslateZ = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> RotateX = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> RotateY = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> RotateZ = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> ScaleX = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> ScaleY = new Dictionary<float, FSKAKeyNode>();
|
|
Dictionary<float, FSKAKeyNode> ScaleZ = new Dictionary<float, FSKAKeyNode>();
|
|
|
|
if (boneAnim.FlagsBase.HasFlag(BoneAnimFlagsBase.Translate))
|
|
{
|
|
TranslateX.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Translate.X, });
|
|
TranslateY.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Translate.Y, });
|
|
TranslateZ.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Translate.Z, });
|
|
}
|
|
if (boneAnim.FlagsBase.HasFlag(BoneAnimFlagsBase.Rotate))
|
|
{
|
|
RotateX.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Rotate.X, });
|
|
RotateY.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Rotate.Y, });
|
|
RotateZ.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Rotate.Z, });
|
|
}
|
|
|
|
if (boneAnim.FlagsBase.HasFlag(BoneAnimFlagsBase.Scale))
|
|
{
|
|
ScaleX.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Scale.X, });
|
|
ScaleY.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Scale.Y, });
|
|
ScaleZ.Add(0, new FSKAKeyNode() { Value = boneAnim.BaseData.Scale.Z, });
|
|
}
|
|
else
|
|
{
|
|
ScaleX.Add(0, new FSKAKeyNode() { Value = 1 });
|
|
ScaleY.Add(0, new FSKAKeyNode() { Value = 1 });
|
|
ScaleZ.Add(0, new FSKAKeyNode() { Value = 1 });
|
|
}
|
|
|
|
foreach (var curve in boneAnim.Curves)
|
|
{
|
|
for (int frame = 0; frame < curve.Frames.Length; frame++)
|
|
{
|
|
float time = curve.Frames[frame];
|
|
float value = 0;
|
|
float slope = 0;
|
|
float slope2 = 0;
|
|
float delta = 0;
|
|
|
|
float scale = curve.Scale;
|
|
if (scale <= 0)
|
|
scale = 1;
|
|
|
|
if (curve.CurveType == AnimCurveType.Cubic)
|
|
{
|
|
value = curve.Offset + curve.Keys[frame, 0] * scale;
|
|
slope = curve.Offset + curve.Keys[frame, 1] * scale;
|
|
slope2 = curve.Offset + curve.Keys[frame, 2] * scale;
|
|
delta = curve.Offset + curve.Keys[frame, 3] * scale;
|
|
}
|
|
if (curve.CurveType == AnimCurveType.Linear)
|
|
{
|
|
value = curve.Offset + curve.Keys[frame, 0] * scale;
|
|
delta = curve.Offset + curve.Keys[frame, 1] * scale;
|
|
}
|
|
if (curve.CurveType == AnimCurveType.StepInt)
|
|
{
|
|
value = curve.Offset + curve.Keys[frame, 0] * scale;
|
|
}
|
|
|
|
|
|
switch (curve.AnimDataOffset)
|
|
{
|
|
case 0x10:
|
|
if (TranslateX.Count > 0 && frame == 0)
|
|
TranslateX.Remove(0);
|
|
|
|
TranslateX.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x14:
|
|
if (TranslateY.Count > 0 && frame == 0)
|
|
TranslateY.Remove(0);
|
|
|
|
TranslateY.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x18:
|
|
if (TranslateZ.Count > 0 && frame == 0)
|
|
TranslateZ.Remove(0);
|
|
|
|
TranslateZ.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x20:
|
|
if (RotateX.Count > 0 && frame == 0)
|
|
RotateX.Remove(0);
|
|
|
|
RotateX.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x24:
|
|
if (RotateY.Count > 0 && frame == 0)
|
|
RotateY.Remove(0);
|
|
|
|
RotateY.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x28:
|
|
if (RotateZ.Count > 0 && frame == 0)
|
|
RotateZ.Remove(0);
|
|
|
|
RotateZ.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x04:
|
|
if (ScaleX.Count > 0 && frame == 0)
|
|
ScaleX.Remove(0);
|
|
|
|
ScaleX.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x08:
|
|
if (ScaleY.Count > 0 && frame == 0)
|
|
ScaleY.Remove(0);
|
|
|
|
ScaleY.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
case 0x0C:
|
|
if (ScaleZ.Count > 0 && frame == 0)
|
|
ScaleZ.Remove(0);
|
|
|
|
ScaleZ.Add(time, new FSKAKeyNode()
|
|
{
|
|
Value = value,
|
|
Slope = slope,
|
|
Slope2 = slope2,
|
|
Delta = delta,
|
|
Frame = time,
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int frame = 0; frame < chr0.FrameCount; frame++)
|
|
{
|
|
if (TranslateX.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(6, frame, TranslateX[frame].Value);
|
|
if (TranslateY.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(7, frame, TranslateY[frame].Value);
|
|
if (TranslateZ.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(8, frame, TranslateZ[frame].Value);
|
|
if (RotateX.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(3, frame, RotateX[frame].Value * Rad2Deg);
|
|
if (RotateY.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(4, frame, RotateY[frame].Value * Rad2Deg);
|
|
if (RotateZ.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(5, frame, RotateZ[frame].Value * Rad2Deg);
|
|
if (ScaleX.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(0, frame, ScaleX[frame].Value);
|
|
if (ScaleY.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(1, frame, ScaleY[frame].Value);
|
|
if (ScaleZ.ContainsKey(frame))
|
|
chr0Entry.SetKeyframe(2, frame, ScaleZ[frame].Value);
|
|
}
|
|
}
|
|
|
|
public static SkeletalAnim Chr02Fska(string FileName)
|
|
{
|
|
CHR0Node chr0 = CHR0Node.FromFile(FileName);
|
|
return Chr02Fska(chr0);
|
|
}
|
|
|
|
private static SkeletalAnim Chr02Fska(CHR0Node chr0)
|
|
{
|
|
SkeletalAnim fska = new SkeletalAnim();
|
|
fska.FrameCount = chr0.FrameCount;
|
|
fska.Name = chr0.Name;
|
|
fska.Path = chr0.OriginalPath;
|
|
fska.UserDatas = new List<Syroot.NintenTools.NSW.Bfres.UserData>();
|
|
fska.UserDataDict = new ResDict();
|
|
|
|
//Set flags
|
|
if (chr0.Loop)
|
|
fska.FlagsAnimSettings |= SkeletalAnimFlags.Looping;
|
|
fska.FlagsRotate = SkeletalAnimFlagsRotate.EulerXYZ;
|
|
fska.FlagsScale = SkeletalAnimFlagsScale.Maya;
|
|
|
|
//Set bone anims and then calculate data after
|
|
foreach (var entry in chr0.Children)
|
|
fska.BoneAnims.Add(Chr0Entry2BoneAnim((CHR0EntryNode)entry));
|
|
|
|
fska.BakedSize = CalculateBakeSize(fska);
|
|
fska.BindIndices = SetIndices(fska);
|
|
|
|
return fska;
|
|
}
|
|
|
|
private static string NameConverterMkWii2Mk8(string name)
|
|
{
|
|
switch (name)
|
|
{
|
|
case "skl_root": return "Skl_Root";
|
|
case "leg_l1": return "LegL";
|
|
case "leg_l2": return "KneeL";
|
|
case "ankle_l1": return "FootL";
|
|
case "leg_r1": return "LegR";
|
|
case "leg_r2": return "KneeR";
|
|
case "ankle_r1": return "FootR";
|
|
case "spin": return "Spine1";
|
|
case "arm_l1": return "ShoulderL";
|
|
case "arm_l2": return "ArmL";
|
|
case "wrist_l1": return "HandL";
|
|
case "arm_r1": return "ShoulderR";
|
|
case "arm_r2": return "ArmR";
|
|
case "wrist_r1": return "HandR";
|
|
case "face_1": return "Head";
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
private static BoneAnim Chr0Entry2BoneAnim(CHR0EntryNode entry)
|
|
{
|
|
BoneAnim boneAnim = new BoneAnim();
|
|
boneAnim.Name = entry.Name;
|
|
// boneAnim.Name = NameConverterMkWii2Mk8(boneAnim.Name);
|
|
|
|
if (entry.UseModelTranslate)
|
|
boneAnim.FlagsBase |= BoneAnimFlagsBase.Translate;
|
|
if (entry.UseModelRotate)
|
|
boneAnim.FlagsBase |= BoneAnimFlagsBase.Rotate;
|
|
if (entry.UseModelScale)
|
|
boneAnim.FlagsBase |= BoneAnimFlagsBase.Scale;
|
|
|
|
var baseData = new BoneAnimData();
|
|
|
|
var FirstFrame = entry.GetAnimFrame(0);
|
|
if (FirstFrame.HasKeys)
|
|
{
|
|
float xRadian = FirstFrame.Rotation._x * Deg2Rad;
|
|
float yRadian = FirstFrame.Rotation._y * Deg2Rad;
|
|
float zRadian = FirstFrame.Rotation._z * Deg2Rad;
|
|
|
|
baseData.Translate = new Vector3F(FirstFrame.Translation._x, FirstFrame.Translation._y, FirstFrame.Translation._z);
|
|
baseData.Rotate = new Vector4F(xRadian, yRadian, zRadian, 1);
|
|
baseData.Scale = new Vector3F(FirstFrame.Scale._x, FirstFrame.Scale._y, FirstFrame.Scale._z);
|
|
baseData.Flags = 0;
|
|
boneAnim.BaseData = baseData;
|
|
}
|
|
else
|
|
{
|
|
baseData.Translate = new Vector3F(0,0,0);
|
|
baseData.Rotate = new Vector4F(0, 0, 0, 1);
|
|
baseData.Scale = new Vector3F(1, 1, 1);
|
|
baseData.Flags = 0;
|
|
boneAnim.BaseData = baseData;
|
|
}
|
|
|
|
|
|
boneAnim.FlagsBase |= BoneAnimFlagsBase.Translate;
|
|
boneAnim.FlagsBase |= BoneAnimFlagsBase.Scale;
|
|
boneAnim.FlagsBase |= BoneAnimFlagsBase.Rotate;
|
|
|
|
if (baseData.Rotate == new Vector4F(0, 0, 0, 1))
|
|
boneAnim.FlagsTransform |= BoneAnimFlagsTransform.RotateZero;
|
|
if (baseData.Translate == new Vector3F(0, 0, 0))
|
|
boneAnim.FlagsTransform |= BoneAnimFlagsTransform.TranslateZero;
|
|
if (baseData.Scale == new Vector3F(1, 1, 1))
|
|
boneAnim.FlagsTransform |= BoneAnimFlagsTransform.ScaleOne;
|
|
if (IsUniform(baseData.Scale))
|
|
boneAnim.FlagsTransform |= BoneAnimFlagsTransform.ScaleUniform;
|
|
if (!IsRoot(boneAnim))
|
|
boneAnim.FlagsTransform |= BoneAnimFlagsTransform.SegmentScaleCompensate;
|
|
|
|
boneAnim.BeginTranslate = 6;
|
|
boneAnim.BeginRotate = 3;
|
|
|
|
if (FirstFrame.HasKeys)
|
|
{
|
|
var AnimFrame = entry.GetAnimFrame(0);
|
|
if (AnimFrame.hasTx)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.TranslateX;
|
|
var curve = GenerateCurve(0x10, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasTy)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.TranslateY;
|
|
var curve = GenerateCurve(0x14, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasTz)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.TranslateZ;
|
|
var curve = GenerateCurve(0x18, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasRx)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.RotateX;
|
|
var curve = GenerateCurve(0x20, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasRy)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.RotateY;
|
|
var curve = GenerateCurve(0x24, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasRz)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.RotateZ;
|
|
var curve = GenerateCurve(0x28, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasSx)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.ScaleX;
|
|
var curve = GenerateCurve(0x4, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasSy)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.ScaleY;
|
|
var curve = GenerateCurve(0x8, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
if (AnimFrame.hasSz)
|
|
{
|
|
boneAnim.FlagsCurve |= BoneAnimFlagsCurve.ScaleZ;
|
|
var curve = GenerateCurve(0xC, entry);
|
|
if (curve != null)
|
|
boneAnim.Curves.Add(curve);
|
|
}
|
|
}
|
|
|
|
return boneAnim;
|
|
}
|
|
|
|
private static bool IsRoot(BoneAnim boneAnim)
|
|
{
|
|
if (boneAnim.Name.Contains("root") || boneAnim.Name.Contains("center"))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private static bool IsUniform(Vector3F value)
|
|
{
|
|
return value.X == value.Y && value.X == value.Z;
|
|
}
|
|
|
|
private static void QuantizeCurveData(AnimCurve curve)
|
|
{
|
|
float MaxFrame = 0;
|
|
float MaxValues = 0;
|
|
|
|
List<bool> IntegerValues = new List<bool>();
|
|
for (int frame = 0; frame < curve.Frames.Length; frame++)
|
|
{
|
|
MaxFrame = Math.Max(MaxFrame, curve.Frames[frame]);
|
|
|
|
if (curve.CurveType == AnimCurveType.Linear)
|
|
{
|
|
MaxValues = Math.Max(MaxValues, curve.Keys[frame, 0]);
|
|
MaxValues = Math.Max(MaxValues, curve.Keys[frame, 1]);
|
|
|
|
IntegerValues.Add(IsInt(curve.Keys[frame, 0]));
|
|
IntegerValues.Add(IsInt(curve.Keys[frame, 1]));
|
|
}
|
|
else if (curve.CurveType == AnimCurveType.Cubic)
|
|
{
|
|
MaxValues = Math.Max(MaxValues, curve.Keys[frame, 0]);
|
|
MaxValues = Math.Max(MaxValues, curve.Keys[frame, 1]);
|
|
MaxValues = Math.Max(MaxValues, curve.Keys[frame, 2]);
|
|
MaxValues = Math.Max(MaxValues, curve.Keys[frame, 3]);
|
|
|
|
IntegerValues.Add(IsInt(curve.Keys[frame, 0]));
|
|
IntegerValues.Add(IsInt(curve.Keys[frame, 1]));
|
|
IntegerValues.Add(IsInt(curve.Keys[frame, 2]));
|
|
IntegerValues.Add(IsInt(curve.Keys[frame, 3]));
|
|
}
|
|
else
|
|
{
|
|
MaxValues = Math.Max(MaxValues, curve.Keys[frame, 0]);
|
|
|
|
IntegerValues.Add(IsInt(curve.Keys[frame, 0]));
|
|
}
|
|
|
|
int ConvertedInt = Convert.ToInt32(MaxValues);
|
|
|
|
}
|
|
|
|
if (MaxFrame < Byte.MaxValue)
|
|
curve.FrameType = AnimCurveFrameType.Byte;
|
|
else if (MaxFrame < Int16.MaxValue)
|
|
curve.FrameType = AnimCurveFrameType.Decimal10x5;
|
|
else
|
|
curve.FrameType = AnimCurveFrameType.Single;
|
|
|
|
|
|
if (IntegerValues.Any(x => x == false))
|
|
{
|
|
curve.KeyType = AnimCurveKeyType.Single;
|
|
}
|
|
else
|
|
{
|
|
if (MaxValues < Byte.MaxValue)
|
|
curve.KeyType = AnimCurveKeyType.SByte;
|
|
else if (MaxFrame < Int16.MaxValue)
|
|
curve.KeyType = AnimCurveKeyType.Int16;
|
|
else
|
|
curve.KeyType = AnimCurveKeyType.Single;
|
|
}
|
|
}
|
|
|
|
private static bool IsInt(float value) => value == Math.Truncate(value);
|
|
|
|
private static AnimCurve GenerateCurve(uint AnimOffset, CHR0EntryNode entry)
|
|
{
|
|
AnimCurve curve = new AnimCurve();
|
|
curve.AnimDataOffset = AnimOffset;
|
|
curve.StartFrame = 0;
|
|
curve.Offset = 0;
|
|
curve.Scale = 1;
|
|
curve.FrameType = AnimCurveFrameType.Single;
|
|
curve.KeyType = AnimCurveKeyType.Single;
|
|
curve.CurveType = AnimCurveType.Linear;
|
|
|
|
List<float> Frames = new List<float>();
|
|
List<float> Keys = new List<float>();
|
|
|
|
CHRAnimationFrame ain, aout;
|
|
for (int frame = 0; frame < entry.FrameCount; frame++)
|
|
{
|
|
//Max of 4 values. Cubic using 4, linear using 2, and step using 1
|
|
float[] KeyValues = new float[4];
|
|
|
|
//Set the main values to the curve based on offset for encoding later
|
|
if ((ain = entry.GetAnimFrame(frame)).HasKeys)
|
|
{
|
|
aout = entry.GetAnimFrame(frame, true);
|
|
FillKeyList(aout, frame, curve.AnimDataOffset, Frames, Keys);
|
|
|
|
if (!ain.Equals(aout))
|
|
{
|
|
FillKeyList(aout, frame, curve.AnimDataOffset, Frames, Keys);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Frames.Count <= 0)
|
|
return null;
|
|
|
|
// Console.WriteLine($"AnimOffset {AnimOffset} Keys {Keys.Count}");
|
|
|
|
//Max value in frames is our end frame
|
|
curve.EndFrame = Frames.Max();
|
|
curve.Frames = Frames.ToArray();
|
|
|
|
//If a curve only has one frame we don't need to interpolate or add keys to a curve as it's constant
|
|
if (curve.Frames.Length <= 1)
|
|
return null;
|
|
|
|
switch (curve.CurveType)
|
|
{
|
|
case AnimCurveType.Cubic:
|
|
curve.Keys = new float[Keys.Count, 4];
|
|
for (int frame = 0; frame < Keys.Count; frame++)
|
|
{
|
|
float Delta = 0;
|
|
|
|
if (frame < Keys.Count - 1)
|
|
Delta = Keys[frame + 1] - Keys[frame];
|
|
|
|
float value = Keys[frame];
|
|
float Slope = 0;
|
|
float Slope2 = 0;
|
|
|
|
curve.Keys[frame, 0] = value;
|
|
curve.Keys[frame, 1] = Slope;
|
|
curve.Keys[frame, 2] = Slope2;
|
|
curve.Keys[frame, 3] = Delta;
|
|
}
|
|
break;
|
|
case AnimCurveType.StepInt:
|
|
//Step requires no interpolation
|
|
curve.Keys = new float[Keys.Count, 1];
|
|
for (int frame = 0; frame < Keys.Count; frame++)
|
|
{
|
|
curve.Keys[frame, 0] = Keys[frame];
|
|
}
|
|
break;
|
|
case AnimCurveType.Linear:
|
|
curve.Keys = new float[Keys.Count, 2];
|
|
for (int frame = 0; frame < Keys.Count; frame++)
|
|
{
|
|
//Delta for second value used in linear curves
|
|
float time = curve.Frames[frame];
|
|
float Delta = 0;
|
|
|
|
if (frame < Keys.Count - 1)
|
|
Delta = Keys[frame + 1] - Keys[frame];
|
|
|
|
curve.Keys[frame, 0] = Keys[frame];
|
|
curve.Keys[frame, 1] = Delta;
|
|
}
|
|
break;
|
|
}
|
|
|
|
QuantizeCurveData(curve);
|
|
|
|
return curve;
|
|
}
|
|
|
|
private static void FillKeyList(CHRAnimationFrame AnimFrame, int frame, uint AnimOffset, List<float> Frames, List<float> Keys)
|
|
{
|
|
if (AnimFrame.hasTx && AnimOffset == 0x10)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Translation._x);
|
|
}
|
|
if (AnimFrame.hasTy && AnimOffset == 0x14)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Translation._y);
|
|
}
|
|
if (AnimFrame.hasTz && AnimOffset == 0x18)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Translation._z);
|
|
}
|
|
if (AnimFrame.hasRx && AnimOffset == 0x20)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Rotation._x * Deg2Rad);
|
|
}
|
|
if (AnimFrame.hasRy && AnimOffset == 0x24)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Rotation._y * Deg2Rad);
|
|
}
|
|
if (AnimFrame.hasRz && AnimOffset == 0x28)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Rotation._z * Deg2Rad);
|
|
}
|
|
if (AnimFrame.hasSx && AnimOffset == 0x04)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Scale._x);
|
|
}
|
|
if (AnimFrame.hasSy && AnimOffset == 0x08)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Scale._y);
|
|
}
|
|
if (AnimFrame.hasSz && AnimOffset == 0x0C)
|
|
{
|
|
Frames.Add(frame);
|
|
Keys.Add(AnimFrame.Scale._z);
|
|
}
|
|
}
|
|
|
|
private static ushort[] SetIndices(SkeletalAnim fska)
|
|
{
|
|
List<ushort> indces = new List<ushort>();
|
|
foreach (var boneAnim in fska.BoneAnims)
|
|
indces.Add(65535);
|
|
|
|
return indces.ToArray();
|
|
}
|
|
private static uint CalculateBakeSize(SkeletalAnim fska)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
}
|