mirror of
https://github.com/KillzXGaming/Switch-Toolbox
synced 2024-11-25 14:00:21 +00:00
00ba993fa1
Add pane deleting via right clicking them. Added pane overlap select from right click (displays all panes at position of the mouse). Add option to hide and show all hidden panes from right click menu.
955 lines
37 KiB
C#
955 lines
37 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 KeyGroupData
|
|
{
|
|
public float Value;
|
|
}
|
|
|
|
public class BoneAnimKeyData
|
|
{
|
|
public Dictionary<int, KeyGroupData> XPOS = new Dictionary<int, KeyGroupData>();
|
|
public Dictionary<int, KeyGroupData> YPOS = new Dictionary<int, KeyGroupData>();
|
|
public Dictionary<int, KeyGroupData> ZPOS = new Dictionary<int, KeyGroupData>();
|
|
|
|
public Dictionary<int, KeyGroupData> XROT = new Dictionary<int, KeyGroupData>();
|
|
public Dictionary<int, KeyGroupData> YROT = new Dictionary<int, KeyGroupData>();
|
|
public Dictionary<int, KeyGroupData> ZROT = new Dictionary<int, KeyGroupData>();
|
|
|
|
public Dictionary<int, KeyGroupData> XSCA = new Dictionary<int, KeyGroupData>();
|
|
public Dictionary<int, KeyGroupData> YSCA = new Dictionary<int, KeyGroupData>();
|
|
public Dictionary<int, KeyGroupData> ZSCA = new Dictionary<int, KeyGroupData>();
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (entry is CLR0MaterialNode)
|
|
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;
|
|
matAnim.Constants = new List<ResU.AnimConstant>();
|
|
matAnim.Curves = new List<ResU.AnimCurve>();
|
|
matAnim.ParamAnimInfos = new List<ResU.ParamAnimInfo>();
|
|
|
|
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 FTXPConverter
|
|
{
|
|
public static void Pat02Ftxp(MaterialAnim matAnim, string FileName)
|
|
{
|
|
PAT0Node pat0 = new PAT0Node();
|
|
matAnim.FrameCount = pat0.FrameCount;
|
|
matAnim.Name = pat0.Name;
|
|
matAnim.Path = pat0.OriginalPath;
|
|
matAnim.Loop = pat0.Loop;
|
|
|
|
foreach (var entry in pat0.Children)
|
|
{
|
|
var Material = (PAT0EntryNode)entry;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
public class FSKAConverter
|
|
{
|
|
public static List<BoneAnimKeyData> BoneAnimKeys = new List<BoneAnimKeyData>();
|
|
|
|
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)
|
|
{
|
|
Console.WriteLine("Making CHR0Node");
|
|
CHR0Node chr0 = new CHR0Node();
|
|
Console.WriteLine("Created 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);
|
|
|
|
Console.WriteLine("Exporting CHR0Node");
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|