1
0
mirror of synced 2024-11-13 18:50:50 +01:00
Switch-Toolbox/BrawlboxHelper/BrawlboxHelper.cs
KillzXGaming 2a92afa122 Chr0 animation importing, dae exporting and tons more improvements!
Chr0 can be imported/replaced.
More fixes to importing sections including some errors and proper filtering.
Dae epxorting now has a progress bar and an option to idsable texture exporting.
Bfska can now be swapped between platforms. More sections will handle this method soon!.
Fixed spaces on files from "Export All".
Display multiple texture maps in bcres materials
2019-05-24 15:15:35 -04:00

553 lines
21 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;
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);
public static SkeletalAnim Chr02Fska(string FileName)
{
CHR0Node chr0 = CHR0Node.FromFile(FileName);
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 BoneAnim Chr0Entry2BoneAnim(CHR0EntryNode entry)
{
BoneAnim boneAnim = new BoneAnim();
boneAnim.Name = entry.Name;
if (entry.UseModelTranslate)
boneAnim.FlagsBase |= BoneAnimFlagsBase.Translate;
if (entry.UseModelRotate)
boneAnim.FlagsBase |= BoneAnimFlagsBase.Rotate;
if (entry.UseModelScale)
boneAnim.FlagsBase |= BoneAnimFlagsBase.Scale;
var FirstFrame = entry.GetAnimFrame(0);
float xRadian = FirstFrame.Rotation._x * Deg2Rad;
float yRadian = FirstFrame.Rotation._y * Deg2Rad;
float zRadian = FirstFrame.Rotation._z * Deg2Rad;
var baseData = new BoneAnimData();
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;
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 bool HasKeyFrames(CHR0EntryNode entry)
{
for (int frame = 0; frame < entry.FrameCount; frame++)
{
var AnimFrame = entry.GetAnimFrame(frame);
if (frame > 0)
{
if (AnimFrame.hasTx) return true;
if (AnimFrame.hasTy) return true;
if (AnimFrame.hasTz) return true;
if (AnimFrame.hasRx) return true;
if (AnimFrame.hasRy) return true;
if (AnimFrame.hasRz) return true;
if (AnimFrame.hasSx) return true;
if (AnimFrame.hasSy) return true;
if (AnimFrame.hasSz) return true;
}
}
return false;
}
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.Cubic;
List<float> Frames = new List<float>();
List<float[]> Keys = new List<float[]>();
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
var AnimFrame = entry.GetAnimFrame(frame);
if (AnimFrame.hasTx && AnimOffset == 0x10)
{
Frames.Add(frame);
KeyValues[0] = AnimFrame.Translation._x;
Keys.Add(KeyValues);
}
if (AnimFrame.hasTy && AnimOffset == 0x14)
{
Frames.Add(frame);
KeyValues[0] = AnimFrame.Translation._y;
Keys.Add(KeyValues);
}
if (AnimFrame.hasTz && AnimOffset == 0x18)
{
Frames.Add(frame);
KeyValues[0] = AnimFrame.Translation._z;
Keys.Add(KeyValues);
}
if (AnimFrame.hasRx && AnimOffset == 0x20)
{
Frames.Add(frame);
float xRadian = AnimFrame.Rotation._x * Deg2Rad;
KeyValues[0] = xRadian;
Keys.Add(KeyValues);
}
if (AnimFrame.hasRy && AnimOffset == 0x24)
{
Frames.Add(frame);
float yRadian = AnimFrame.Rotation._y * Deg2Rad;
KeyValues[0] = yRadian;
Keys.Add(KeyValues);
}
if (AnimFrame.hasRz && AnimOffset == 0x28)
{
Frames.Add(frame);
float zRadian = AnimFrame.Rotation._z * Deg2Rad;
KeyValues[0] = zRadian;
Keys.Add(KeyValues);
}
if (AnimFrame.hasSx && AnimOffset == 0x04)
{
Frames.Add(frame);
KeyValues[0] = AnimFrame.Scale._x;
Keys.Add(KeyValues);
}
if (AnimFrame.hasSy && AnimOffset == 0x08)
{
Frames.Add(frame);
KeyValues[0] = AnimFrame.Scale._y;
Keys.Add(KeyValues);
}
if (AnimFrame.hasSz && AnimOffset == 0x0C)
{
Frames.Add(frame);
KeyValues[0] = AnimFrame.Scale._z;
Keys.Add(KeyValues);
}
}
//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][0] - Keys[frame][0];
float value = Keys[frame][0];
float Slope = Keys[frame][1];
float Slope2 = Keys[frame][2];
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][0];
}
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][0] - Keys[frame][0];
curve.Keys[frame, 0] = Keys[frame][0];
curve.Keys[frame, 1] = Delta;
}
break;
}
return curve;
}
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;
}
}
}