2a92afa122
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
553 lines
21 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|