Add support for G1M model files. Rewrite some bone properties.
This commit is contained in:
parent
f87487cad4
commit
d991a23980
@ -41,19 +41,18 @@ namespace FirstPlugin.CtrLibrary
|
||||
|
||||
parentIndex = bone.ParentIndex;
|
||||
RotationType = BoneRotationType.Euler;
|
||||
position = new float[3];
|
||||
scale = new float[3];
|
||||
rotation = new float[4];
|
||||
scale[0] = bone.Scale.X;
|
||||
scale[1] = bone.Scale.Y;
|
||||
scale[2] = bone.Scale.Z;
|
||||
rotation[0] = bone.Rotation.X;
|
||||
rotation[1] = bone.Rotation.Y;
|
||||
rotation[2] = bone.Rotation.Z;
|
||||
rotation[3] = 1;
|
||||
position[0] = bone.Translation.X;
|
||||
position[1] = bone.Translation.Y;
|
||||
position[2] = bone.Translation.Z;
|
||||
Position = new OpenTK.Vector3(
|
||||
bone.Translation.X,
|
||||
bone.Translation.Y,
|
||||
bone.Translation.Z);
|
||||
EulerRotation = new OpenTK.Vector3(
|
||||
bone.Rotation.X,
|
||||
bone.Rotation.Y,
|
||||
bone.Rotation.Z);
|
||||
Scale = new OpenTK.Vector3(
|
||||
bone.Scale.X,
|
||||
bone.Scale.Y,
|
||||
bone.Scale.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,15 +49,20 @@ namespace Bfres.Structs
|
||||
{
|
||||
case ResU.AnimCurveType.Cubic: //4 elements are stored for cubic
|
||||
track.InterpolationType = InterpolationType.HERMITE;
|
||||
var coef0 = animCurve.Offset + (animCurve.Keys[i, 0] * animCurve.Scale);
|
||||
var coef1 = animCurve.Offset + (animCurve.Keys[i, 1] * animCurve.Scale);
|
||||
var coef2 = animCurve.Offset + (animCurve.Keys[i, 2] * animCurve.Scale);
|
||||
var coef3 = animCurve.Offset + (animCurve.Keys[i, 3] * animCurve.Scale);
|
||||
var slopes = GetSlopes(animCurve, i);
|
||||
|
||||
track.Keys.Add(new Animation.KeyFrame()
|
||||
{
|
||||
IsKeyed = true,
|
||||
InterType = InterpolationType.HERMITE,
|
||||
Frame = (int)animCurve.Frames[i],
|
||||
Value = animCurve.Offset + (animCurve.Keys[i, 0] * animCurve.Scale),
|
||||
Slope1 = animCurve.Offset + (animCurve.Keys[i, 1] * animCurve.Scale),
|
||||
Slope2 = animCurve.Offset + (animCurve.Keys[i, 2] * animCurve.Scale),
|
||||
Delta = animCurve.Offset + (animCurve.Keys[i, 3] * animCurve.Scale),
|
||||
Value = coef0,
|
||||
// Slope1 = slopes[0],
|
||||
// Slope2 = slopes[1],
|
||||
});
|
||||
break;
|
||||
case ResU.AnimCurveType.Linear: //2 elements are stored for linear
|
||||
@ -79,7 +84,6 @@ namespace Bfres.Structs
|
||||
InterType = InterpolationType.STEP,
|
||||
Frame = (int)animCurve.Frames[i],
|
||||
Value = (int)animCurve.Offset + (int)animCurve.Keys[i, 0] * animCurve.Scale,
|
||||
Value1 = (int)animCurve.Offset + (int)animCurve.Keys[i, 0] * animCurve.Scale,
|
||||
});
|
||||
|
||||
Console.WriteLine($"Frame {animCurve.Frames[i]} FrameINT {(int)animCurve.Frames[i]} Offset " + (int)animCurve.Offset + " " + ((int)animCurve.Offset + (int)animCurve.Keys[i, 0] * animCurve.Scale));
|
||||
@ -92,6 +96,84 @@ namespace Bfres.Structs
|
||||
return track;
|
||||
}
|
||||
|
||||
public static float[] GetSlopes(AnimCurve curve, float index)
|
||||
{
|
||||
float[] slopes = new float[2];
|
||||
if (curve.CurveType == AnimCurveType.Cubic)
|
||||
{
|
||||
float InSlope = 0;
|
||||
float OutSlope = 0;
|
||||
for (int i = 0; i < curve.Frames.Length; i++)
|
||||
{
|
||||
var coef0 = curve.Keys[i, 0] * curve.Scale + curve.Offset;
|
||||
var coef1 = curve.Keys[i, 1] * curve.Scale + curve.Offset;
|
||||
var coef2 = curve.Keys[i, 2] * curve.Scale + curve.Offset;
|
||||
var coef3 = curve.Keys[i, 3] * curve.Scale + curve.Offset;
|
||||
float time = 0;
|
||||
float delta = 0;
|
||||
if (i < curve.Frames.Length - 1)
|
||||
{
|
||||
var nextValue = curve.Keys[i + 1, 0] * curve.Scale + curve.Offset;
|
||||
delta = nextValue - coef0;
|
||||
time = curve.Frames[i + 1] - curve.Frames[i];
|
||||
}
|
||||
|
||||
var slopeData = CurveInterpolationHelper.GetCubicSlopes(time, delta,
|
||||
new float[4] { coef0, coef1, coef2, coef3, });
|
||||
|
||||
if (index == i)
|
||||
{
|
||||
OutSlope = slopeData[1];
|
||||
return new float[2] { InSlope, OutSlope };
|
||||
}
|
||||
|
||||
//The previous inslope is used
|
||||
InSlope = slopeData[0];
|
||||
}
|
||||
}
|
||||
|
||||
return slopes;
|
||||
}
|
||||
|
||||
public static float[] GetSlopes(ResU.AnimCurve curve, float index)
|
||||
{
|
||||
float[] slopes = new float[2];
|
||||
if (curve.CurveType == ResU.AnimCurveType.Cubic)
|
||||
{
|
||||
float InSlope = 0;
|
||||
float OutSlope = 0;
|
||||
for (int i = 0; i < curve.Frames.Length; i++)
|
||||
{
|
||||
var coef0 = curve.Keys[i, 0] * curve.Scale + curve.Offset;
|
||||
var coef1 = curve.Keys[i, 1] * curve.Scale + curve.Offset;
|
||||
var coef2 = curve.Keys[i, 2] * curve.Scale + curve.Offset;
|
||||
var coef3 = curve.Keys[i, 3] * curve.Scale + curve.Offset;
|
||||
float time = 0;
|
||||
float delta = 0;
|
||||
if (i < curve.Frames.Length - 1)
|
||||
{
|
||||
var nextValue = curve.Keys[i + 1, 0] * curve.Scale + curve.Offset;
|
||||
delta = nextValue - coef0;
|
||||
time = curve.Frames[i + 1] - curve.Frames[i];
|
||||
}
|
||||
|
||||
var slopeData = CurveInterpolationHelper.GetCubicSlopes(time, delta,
|
||||
new float[4] { coef0, coef1, coef2, coef3, });
|
||||
|
||||
if (index == i)
|
||||
{
|
||||
OutSlope = slopeData[1];
|
||||
return new float[2] { InSlope, OutSlope };
|
||||
}
|
||||
|
||||
//The previous inslope is used
|
||||
InSlope = slopeData[0];
|
||||
}
|
||||
}
|
||||
|
||||
return slopes;
|
||||
}
|
||||
|
||||
public static BooleanKeyGroup CreateBooleanTrackWiiU(ResU.AnimCurve animCurve)
|
||||
{
|
||||
BooleanKeyGroup track = new BooleanKeyGroup();
|
||||
@ -176,17 +258,23 @@ namespace Bfres.Structs
|
||||
{
|
||||
case AnimCurveType.Cubic: //4 elements are stored for cubic
|
||||
track.InterpolationType = InterpolationType.HERMITE;
|
||||
var coef0 = animCurve.Offset + (animCurve.Keys[i, 0] * animCurve.Scale);
|
||||
var coef1 = animCurve.Offset + (animCurve.Keys[i, 1] * animCurve.Scale);
|
||||
var coef2 = animCurve.Offset + (animCurve.Keys[i, 2] * animCurve.Scale);
|
||||
var coef3 = animCurve.Offset + (animCurve.Keys[i, 3] * animCurve.Scale);
|
||||
var slopes = GetSlopes(animCurve, i);
|
||||
|
||||
var inSlope = slopes[0] * animCurve.Scale + animCurve.Offset;
|
||||
var outSlope = slopes[1] * animCurve.Scale + animCurve.Offset;
|
||||
|
||||
track.Keys.Add(new Animation.KeyFrame()
|
||||
{
|
||||
IsKeyed = true,
|
||||
InterType = InterpolationType.HERMITE,
|
||||
Frame = (int)animCurve.Frames[i],
|
||||
Value = animCurve.Offset + (animCurve.Keys[i, 0] * animCurve.Scale),
|
||||
|
||||
Value1 = animCurve.Offset + (animCurve.Keys[i, 0] * animCurve.Scale),
|
||||
Slope1 = animCurve.Offset + (animCurve.Keys[i, 1] * animCurve.Scale),
|
||||
Slope2 = animCurve.Offset + (animCurve.Keys[i, 2] * animCurve.Scale),
|
||||
Delta = animCurve.Offset + (animCurve.Keys[i, 3] * animCurve.Scale),
|
||||
Value = coef0,
|
||||
Slope1 = inSlope,
|
||||
Slope2 = outSlope,
|
||||
});
|
||||
break;
|
||||
case AnimCurveType.Linear: //2 elements are stored for linear
|
||||
|
@ -750,15 +750,6 @@ namespace Bfres.Structs
|
||||
else
|
||||
shape.vertexAttributes = csvsettings.CreateNewAttributes();
|
||||
|
||||
shape.BoneIndex = 0;
|
||||
shape.Text = obj.ObjectName;
|
||||
shape.lodMeshes = obj.lodMeshes;
|
||||
shape.CreateNewBoundingBoxes();
|
||||
shape.CreateBoneList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);
|
||||
shape.CreateIndexList(obj, this);
|
||||
shape.ApplyImportSettings(csvsettings, GetMaterial(shape.MaterialIndex));
|
||||
shape.BoneIndices = shape.GetIndices(Skeleton);
|
||||
|
||||
Console.WriteLine($"ForceSkinInfluence {ForceSkinInfluence}");
|
||||
|
||||
if (!ForceSkinInfluence)
|
||||
@ -768,6 +759,15 @@ namespace Bfres.Structs
|
||||
|
||||
Console.WriteLine($"VertexSkinCount { shape.VertexSkinCount}");
|
||||
|
||||
shape.BoneIndex = 0;
|
||||
shape.Text = obj.ObjectName;
|
||||
shape.lodMeshes = obj.lodMeshes;
|
||||
shape.CreateNewBoundingBoxes();
|
||||
shape.CreateBoneList(obj, this, ForceSkinInfluence, ForceSkinInfluenceMax);
|
||||
shape.CreateIndexList(obj, this);
|
||||
shape.ApplyImportSettings(csvsettings, GetMaterial(shape.MaterialIndex));
|
||||
shape.BoneIndices = shape.GetIndices(Skeleton);
|
||||
|
||||
if (shape.VertexSkinCount == 1)
|
||||
{
|
||||
int boneIndex = shape.BoneIndices[0];
|
||||
@ -1178,7 +1178,10 @@ namespace Bfres.Structs
|
||||
else
|
||||
shape.vertexAttributes = settings.CreateNewAttributes();
|
||||
|
||||
shape.BoneIndex = obj.BoneIndex;
|
||||
if (ForceSkinInfluence)
|
||||
shape.VertexSkinCount = (byte)ForceSkinInfluenceMax;
|
||||
else
|
||||
shape.VertexSkinCount = obj.GetMaxSkinInfluenceCount();
|
||||
|
||||
if (obj.MaterialIndex + MatStartIndex < materials.Count && obj.MaterialIndex > 0)
|
||||
shape.MaterialIndex = obj.MaterialIndex + MatStartIndex;
|
||||
@ -1192,10 +1195,9 @@ namespace Bfres.Structs
|
||||
shape.ApplyImportSettings(settings, GetMaterial(shape.MaterialIndex));
|
||||
shape.BoneIndices = shape.GetIndices(Skeleton);
|
||||
|
||||
if (ForceSkinInfluence)
|
||||
shape.VertexSkinCount = (byte)ForceSkinInfluenceMax;
|
||||
else
|
||||
shape.VertexSkinCount = obj.GetMaxSkinInfluenceCount();
|
||||
shape.OptmizeAttributeFormats();
|
||||
shape.SaveShape(IsWiiU);
|
||||
shape.SaveVertexBuffer(IsWiiU);
|
||||
|
||||
if (shape.VertexSkinCount == 1 && shape.BoneIndices.Count > 0)
|
||||
{
|
||||
@ -1203,9 +1205,7 @@ namespace Bfres.Structs
|
||||
shape.BoneIndex = boneIndex;
|
||||
}
|
||||
|
||||
shape.OptmizeAttributeFormats();
|
||||
shape.SaveShape(IsWiiU);
|
||||
shape.SaveVertexBuffer(IsWiiU);
|
||||
shape.BoneIndex = obj.BoneIndex;
|
||||
|
||||
if (IsWiiU)
|
||||
{
|
||||
@ -1316,12 +1316,19 @@ namespace Bfres.Structs
|
||||
bn.FlagsTransformCumulative = BoneFlagsTransformCumulative.None;
|
||||
bn.Name = bone.Text;
|
||||
bn.RigidMatrixIndex = 0;
|
||||
bn.Rotation = new Syroot.Maths.Vector4F(bone.rotation[0],
|
||||
bone.rotation[1], bone.rotation[2], bone.rotation[3]);
|
||||
bn.Position = new Syroot.Maths.Vector3F(bone.position[0],
|
||||
bone.position[1], bone.position[2]);
|
||||
bn.Scale = new Syroot.Maths.Vector3F(bone.scale[0],
|
||||
bone.scale[1], bone.scale[2]);
|
||||
bn.Rotation = new Syroot.Maths.Vector4F(
|
||||
bone.Rotation.X,
|
||||
bone.Rotation.Y,
|
||||
bone.Rotation.Z,
|
||||
bone.Rotation.W);
|
||||
bn.Position = new Syroot.Maths.Vector3F(
|
||||
bone.Position.X,
|
||||
bone.Position.Y,
|
||||
bone.Position.Z);
|
||||
bn.Scale = new Syroot.Maths.Vector3F(
|
||||
bone.Scale.X,
|
||||
bone.Scale.Y,
|
||||
bone.Scale.Z);
|
||||
bn.UserData = new List<UserData>();
|
||||
bn.UserDataDict = new ResDict();
|
||||
}
|
||||
|
@ -745,20 +745,19 @@ namespace Bfres.Structs
|
||||
{
|
||||
Text = genericBone.Text;
|
||||
|
||||
position = new float[3];
|
||||
rotation = new float[4];
|
||||
scale = new float[3];
|
||||
Position = new OpenTK.Vector3(
|
||||
genericBone.Position.X,
|
||||
genericBone.Position.Y,
|
||||
genericBone.Position.Z);
|
||||
EulerRotation = new OpenTK.Vector3(
|
||||
genericBone.Rotation.X,
|
||||
genericBone.Rotation.Y,
|
||||
genericBone.Rotation.Z);
|
||||
Scale = new OpenTK.Vector3(
|
||||
genericBone.Scale.X,
|
||||
genericBone.Scale.Y,
|
||||
genericBone.Scale.Z);
|
||||
|
||||
position[0] = genericBone.position[0];
|
||||
position[1] = genericBone.position[1];
|
||||
position[2] = genericBone.position[2];
|
||||
rotation[0] = genericBone.rotation[0];
|
||||
rotation[1] = genericBone.rotation[1];
|
||||
rotation[2] = genericBone.rotation[2];
|
||||
rotation[3] = genericBone.rotation[3];
|
||||
scale[0] = genericBone.scale[0];
|
||||
scale[1] = genericBone.scale[1];
|
||||
scale[2] = genericBone.scale[2];
|
||||
RotationType = genericBone.RotationType;
|
||||
parentIndex = genericBone.parentIndex;
|
||||
RigidMatrixIndex = genericBone.RigidMatrixIndex;
|
||||
@ -769,9 +768,9 @@ namespace Bfres.Structs
|
||||
{
|
||||
if (BoneU != null)
|
||||
{
|
||||
BoneU.Position = new Syroot.Maths.Vector3F(position[0], position[1], position[2]);
|
||||
BoneU.Scale = new Syroot.Maths.Vector3F(scale[0], scale[1], scale[2]);
|
||||
BoneU.Rotation = new Syroot.Maths.Vector4F(rotation[0], rotation[1], rotation[2], rotation[3]);
|
||||
BoneU.Position = new Syroot.Maths.Vector3F(Position.X, Position.Y, Position.Z);
|
||||
BoneU.Scale = new Syroot.Maths.Vector3F(Scale.X, Scale.Y, Scale.Z);
|
||||
BoneU.Rotation = new Syroot.Maths.Vector4F(Rotation.X, Rotation.Y, Rotation.Z, Rotation.W);
|
||||
BoneU.Name = Text;
|
||||
BoneU.Flags = FlagVisible ? ResU.BoneFlags.Visible : 0;
|
||||
BoneU.ParentIndex = (short)parentIndex;
|
||||
@ -782,9 +781,9 @@ namespace Bfres.Structs
|
||||
}
|
||||
else
|
||||
{
|
||||
Bone.Position = new Syroot.Maths.Vector3F(position[0], position[1], position[2]);
|
||||
Bone.Scale = new Syroot.Maths.Vector3F(scale[0], scale[1], scale[2]);
|
||||
Bone.Rotation = new Syroot.Maths.Vector4F(rotation[0], rotation[1], rotation[2], rotation[3]);
|
||||
Bone.Position = new Syroot.Maths.Vector3F(Position.X, Position.Y, Position.Z);
|
||||
Bone.Scale = new Syroot.Maths.Vector3F(Scale.X, Scale.Y, Scale.Z);
|
||||
Bone.Rotation = new Syroot.Maths.Vector4F(Rotation.X, Rotation.Y, Rotation.Z, Rotation.W);
|
||||
Bone.Name = Text;
|
||||
Bone.Flags = FlagVisible ? BoneFlags.Visible : 0;
|
||||
Bone.ParentIndex = (short)parentIndex;
|
||||
|
@ -576,23 +576,24 @@ namespace FirstPlugin
|
||||
if (SetParent)
|
||||
bone.parentIndex = bn.ParentIndex;
|
||||
|
||||
bone.scale = new float[3];
|
||||
bone.rotation = new float[4];
|
||||
bone.position = new float[3];
|
||||
if (bn.FlagsRotation == BoneFlagsRotation.Quaternion)
|
||||
bone.RotationType = STBone.BoneRotationType.Quaternion;
|
||||
else
|
||||
bone.RotationType = STBone.BoneRotationType.Euler;
|
||||
bone.scale[0] = bn.Scale.X;
|
||||
bone.scale[1] = bn.Scale.Y;
|
||||
bone.scale[2] = bn.Scale.Z;
|
||||
bone.rotation[0] = bn.Rotation.X;
|
||||
bone.rotation[1] = bn.Rotation.Y;
|
||||
bone.rotation[2] = bn.Rotation.Z;
|
||||
bone.rotation[3] = bn.Rotation.W;
|
||||
bone.position[0] = bn.Position.X;
|
||||
bone.position[1] = bn.Position.Y;
|
||||
bone.position[2] = bn.Position.Z;
|
||||
|
||||
bone.Position = new OpenTK.Vector3(
|
||||
bn.Position.X,
|
||||
bn.Position.Y,
|
||||
bn.Position.Z);
|
||||
bone.Rotation = new OpenTK.Quaternion(
|
||||
bn.Rotation.X,
|
||||
bn.Rotation.Y,
|
||||
bn.Rotation.Z,
|
||||
bn.Rotation.W);
|
||||
bone.Scale = new OpenTK.Vector3(
|
||||
bn.Scale.X,
|
||||
bn.Scale.Y,
|
||||
bn.Scale.Z);
|
||||
}
|
||||
|
||||
public static void SaveSkeleton(FSKL fskl, List<STBone> Bones)
|
||||
|
@ -380,26 +380,26 @@ namespace FirstPlugin
|
||||
bone.BillboardIndex = bn.BillboardIndex;
|
||||
bone.UseRigidMatrix = bn.RigidMatrixIndex != -1;
|
||||
bone.UseSmoothMatrix = bn.SmoothMatrixIndex != -1;
|
||||
|
||||
if (SetParent)
|
||||
bone.parentIndex = bn.ParentIndex;
|
||||
bone.scale = new float[3];
|
||||
bone.rotation = new float[4];
|
||||
bone.position = new float[3];
|
||||
if (bn.FlagsRotation == BoneFlagsRotation.Quaternion)
|
||||
bone.RotationType = STBone.BoneRotationType.Quaternion;
|
||||
else
|
||||
bone.RotationType = STBone.BoneRotationType.Euler;
|
||||
bone.scale[0] = bn.Scale.X;
|
||||
bone.scale[1] = bn.Scale.Y;
|
||||
bone.scale[2] = bn.Scale.Z;
|
||||
bone.rotation[0] = bn.Rotation.X;
|
||||
bone.rotation[1] = bn.Rotation.Y;
|
||||
bone.rotation[2] = bn.Rotation.Z;
|
||||
bone.rotation[3] = bn.Rotation.W;
|
||||
bone.position[0] = bn.Position.X;
|
||||
bone.position[1] = bn.Position.Y;
|
||||
bone.position[2] = bn.Position.Z;
|
||||
|
||||
bone.Position = new OpenTK.Vector3(
|
||||
bn.Position.X,
|
||||
bn.Position.Y,
|
||||
bn.Position.Z);
|
||||
bone.Rotation = new OpenTK.Quaternion(
|
||||
bn.Rotation.X,
|
||||
bn.Rotation.Y,
|
||||
bn.Rotation.Z,
|
||||
bn.Rotation.W);
|
||||
bone.Scale = new OpenTK.Vector3(
|
||||
bn.Scale.X,
|
||||
bn.Scale.Y,
|
||||
bn.Scale.Z);
|
||||
}
|
||||
|
||||
public static void SetShape(this FSHP s, Shape shp)
|
||||
|
@ -160,7 +160,6 @@ namespace FirstPlugin
|
||||
{
|
||||
if (bone.Parent == null)
|
||||
SkeletonFolder.Nodes.Add(bone);
|
||||
|
||||
}
|
||||
|
||||
for (int i = 0; i < BMDFile.Shapes.Shapes.Count; i++)
|
||||
@ -284,6 +283,10 @@ namespace FirstPlugin
|
||||
{
|
||||
int matIndex = node.Parent.Index;
|
||||
((BMDShapeWrapper)Meshes[node.Index]).SetMaterial((STGenericMaterial)MaterialFolder.Nodes[matIndex]);
|
||||
|
||||
((BMDShapeWrapper)Meshes[node.Index]).Nodes.Add(MaterialFolder.Nodes[matIndex].Text);
|
||||
|
||||
Console.WriteLine("" + '"' + MaterialFolder.Nodes[matIndex].Text + '"' + ",");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,6 +294,20 @@ namespace FirstPlugin
|
||||
|
||||
public void FillSkeleton(SuperBMDLib.BMD.INF1 INF1, STSkeleton skeleton, List<SuperBMDLib.Rigging.Bone> flatSkeleton)
|
||||
{
|
||||
for (int i = 1; i < INF1.FlatNodes.Count; i++)
|
||||
{
|
||||
SuperBMDLib.Scenegraph.SceneNode curNode = INF1.FlatNodes[i];
|
||||
if (curNode.Type == SuperBMDLib.Scenegraph.Enums.NodeType.Joint)
|
||||
{
|
||||
var Bone = flatSkeleton[curNode.Index];
|
||||
var stBone = new STBone(skeleton);
|
||||
stBone.Text = Bone.Name;
|
||||
stBone.FromTransform(Bone.TransformationMatrix);
|
||||
skeleton.bones.Add(stBone);
|
||||
}
|
||||
}
|
||||
|
||||
int boneIndex = 0;
|
||||
for (int i = 1; i < INF1.FlatNodes.Count; i++)
|
||||
{
|
||||
SuperBMDLib.Scenegraph.SceneNode curNode = INF1.FlatNodes[i];
|
||||
@ -299,16 +316,18 @@ namespace FirstPlugin
|
||||
{
|
||||
var Bone = flatSkeleton[curNode.Index];
|
||||
|
||||
var stBone = new STBone(skeleton);
|
||||
stBone.Text = Bone.Name;
|
||||
stBone.FromTransform(Bone.TransformationMatrix);
|
||||
var stBone = skeleton.bones[boneIndex];
|
||||
if (curNode.Parent != null && curNode.Parent.Type == SuperBMDLib.Scenegraph.Enums.NodeType.Joint) {
|
||||
var parent = flatSkeleton[curNode.Parent.Index];
|
||||
|
||||
if (Bone.Parent != null)
|
||||
stBone.parentIndex = flatSkeleton.IndexOf(Bone.Parent);
|
||||
var boneParent = skeleton.GetBone(parent.Name);
|
||||
if (boneParent != null)
|
||||
stBone.parentIndex = skeleton.bones.IndexOf(boneParent);
|
||||
}
|
||||
else
|
||||
stBone.parentIndex = -1;
|
||||
|
||||
skeleton.bones.Add(stBone);
|
||||
boneIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace FirstPlugin
|
||||
|
||||
public static string ToYaml(ByamlExt.Byaml.BymlFileData data)
|
||||
{
|
||||
/* var settings = new SerializerSettings()
|
||||
var settings = new SerializerSettings()
|
||||
{
|
||||
EmitTags = false,
|
||||
EmitAlias = false,
|
||||
@ -43,7 +43,8 @@ namespace FirstPlugin
|
||||
settings.RegisterTagMapping("!ul", typeof(ulong));
|
||||
settings.RegisterTagMapping("!ll", typeof(long));
|
||||
|
||||
var serializer = new Serializer(settings);*/
|
||||
var serializer = new Serializer(settings);
|
||||
return serializer.Serialize(data);
|
||||
|
||||
NodePaths.Clear();
|
||||
refNodeId = 0;
|
||||
@ -256,7 +257,8 @@ namespace FirstPlugin
|
||||
|
||||
private static string ConvertValue(dynamic node)
|
||||
{
|
||||
if (node is bool) return ((bool)node)? "true" : "false";
|
||||
if (node is bool) return ((bool)node) ? "true" : "false";
|
||||
else if (node is float) return string.Format("{0:0.000.00}", node);
|
||||
else return node.ToString();
|
||||
}
|
||||
}
|
||||
|
@ -1,162 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using Toolbox;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using Toolbox.Library.Forms;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class BFOTF : TreeNodeFile, IFileFormat, IContextMenuNode
|
||||
{
|
||||
public FileType FileType { get; set; } = FileType.Font;
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "Binary Cafe Open Type Font" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.bfotf" };
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public IFileInfo IFileInfo { get; set; }
|
||||
|
||||
public bool Identify(System.IO.Stream stream)
|
||||
{
|
||||
using (var reader = new Toolbox.Library.IO.FileReader(stream, true))
|
||||
{
|
||||
uint magic = reader.ReadUInt32();
|
||||
reader.Position = 0;
|
||||
return magic == 0x1A879BD9 || magic == 0x1E1AF836 || magic == 0xC1DE68F3;
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] Types
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Type> types = new List<Type>();
|
||||
return types.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("gdi32.dll")]
|
||||
private static extern IntPtr AddFontMemResourceEx(IntPtr pbFont, uint cbFont, IntPtr pdv, [In] ref uint pcFonts);
|
||||
|
||||
public byte[] DecryptedFont { get; set; }
|
||||
|
||||
//Decryption process from https://github.com/TheFearsomeDzeraora/BFTTFutil/blob/master/Program.cs
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
Text = FileName;
|
||||
|
||||
using (var reader = new FileReader(stream))
|
||||
{
|
||||
uint decryptionKey = 0;
|
||||
|
||||
uint magic = reader.ReadUInt32();
|
||||
switch (magic)
|
||||
{
|
||||
case 0x1A879BD9: decryptionKey = 2785117442U; break;
|
||||
case 0x1E1AF836: decryptionKey = 1231165446U; break;
|
||||
case 0xC1DE68F3: decryptionKey = 2364726489U; break;
|
||||
default:
|
||||
Console.WriteLine("Err 0x2: Input file isn't a BFTTF\\BFOTF");
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] inFile = reader.getSection(0, (int)reader.BaseStream.Length);
|
||||
if (inFile.Length <= 8) return;
|
||||
uint value = GetUInt32(inFile, 4) ^ decryptionKey;
|
||||
if (inFile.Length < value) return;
|
||||
byte[] outFile = new byte[inFile.Length - 8];
|
||||
int pos = 8;
|
||||
while (pos < inFile.Length)
|
||||
{
|
||||
SetToUInt32(GetUInt32(inFile, pos) ^ decryptionKey, outFile, pos - 8);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
DecryptedFont = outFile;
|
||||
}
|
||||
}
|
||||
|
||||
public ToolStripItem[] GetContextMenuItems()
|
||||
{
|
||||
List<ToolStripItem> Items = new List<ToolStripItem>();
|
||||
Items.Add(new STToolStipMenuItem("Export", null, ExportAction, Keys.Control | Keys.E));
|
||||
return Items.ToArray();
|
||||
}
|
||||
|
||||
private void ExportAction(object sender, EventArgs args)
|
||||
{
|
||||
SaveFileDialog sfd = new SaveFileDialog();
|
||||
sfd.FileName = Text;
|
||||
sfd.DefaultExt = System.IO.Path.GetExtension(Text);
|
||||
sfd.Filter = "Open Type Font |*.otf;";
|
||||
|
||||
if (sfd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
System.IO.File.WriteAllBytes(sfd.FileName, DecryptedFont);
|
||||
}
|
||||
}
|
||||
|
||||
public System.Drawing.Font ToFont(float Size = 72)
|
||||
{
|
||||
System.Drawing.Text.PrivateFontCollection privateFonts = new System.Drawing.Text.PrivateFontCollection();
|
||||
|
||||
// We HAVE to do this to register the font to the system (Weird .NET bug !)
|
||||
var fontDataPtr = Marshal.AllocCoTaskMem(DecryptedFont.Length);
|
||||
Marshal.Copy(DecryptedFont, 0, fontDataPtr, DecryptedFont.Length);
|
||||
|
||||
uint cFonts = 0;
|
||||
AddFontMemResourceEx(fontDataPtr, (uint)DecryptedFont.Length, IntPtr.Zero, ref cFonts);
|
||||
|
||||
privateFonts.AddMemoryFont(fontDataPtr, DecryptedFont.Length);
|
||||
|
||||
return new System.Drawing.Font(privateFonts.Families[0], Size);
|
||||
}
|
||||
|
||||
public override void OnClick(TreeView treeview)
|
||||
{
|
||||
var font = ToFont();
|
||||
|
||||
var texbox = new RichTextBox() { Multiline = true, BorderStyle = BorderStyle.None, Dock = DockStyle.Fill };
|
||||
texbox.BackColor = FormThemes.BaseTheme.FormBackColor;
|
||||
texbox.ForeColor = FormThemes.BaseTheme.FormForeColor;
|
||||
|
||||
UserControl editor = new UserControl();
|
||||
editor.Controls.Add(texbox);
|
||||
LibraryGUI.LoadEditor(editor);
|
||||
|
||||
editor.Text = Text;
|
||||
editor.Dock = DockStyle.Fill;
|
||||
editor.Font = font;
|
||||
texbox.Text = "Preview Text!";
|
||||
}
|
||||
|
||||
private static UInt32 GetUInt32(byte[] data, int pos)
|
||||
{
|
||||
return (UInt32)(data[pos + 3] | data[pos + 2] << 8 | data[pos + 1] << 16 | data[pos] << 24);
|
||||
}
|
||||
|
||||
private static void SetToUInt32(uint val, byte[] data, int pos)
|
||||
{
|
||||
data[pos + 3] = (byte)(val & (uint)byte.MaxValue);
|
||||
data[pos + 2] = (byte)(val >> 8 & (uint)byte.MaxValue);
|
||||
data[pos + 1] = (byte)(val >> 16 & (uint)byte.MaxValue);
|
||||
data[pos] = (byte)(val >> 24 & (uint)byte.MaxValue);
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
DecryptedFont = null;
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
|
||||
public void Save(System.IO.Stream stream)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ namespace FirstPlugin
|
||||
|
||||
public bool CanSave { get; set; }
|
||||
public string[] Description { get; set; } = new string[] { "Binary Cafe True Type Font" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.bfttf" };
|
||||
public string[] Extension { get; set; } = new string[] { "*.bfttf", "*.bfotf" };
|
||||
public string FileName { get; set; }
|
||||
public string FilePath { get; set; }
|
||||
public IFileInfo IFileInfo { get; set; }
|
||||
|
@ -174,30 +174,31 @@ namespace FirstPlugin
|
||||
|
||||
if (HasSkeleton)
|
||||
{
|
||||
foreach (var bone in header.SectionData.SkeletonChunk.Bones)
|
||||
var bonesOrdered = header.SectionData.SkeletonChunk.Bones.OrderBy(x => x.ID).ToList();
|
||||
foreach (var bone in bonesOrdered)
|
||||
{
|
||||
STBone genericBone = new STBone(Skeleton);
|
||||
genericBone.parentIndex = bone.ParentIndex;
|
||||
genericBone.position = new float[3];
|
||||
genericBone.scale = new float[3];
|
||||
genericBone.rotation = new float[4];
|
||||
genericBone.Checked = true;
|
||||
|
||||
genericBone.Text = $"Bone {bone.ID}";
|
||||
genericBone.RotationType = STBone.BoneRotationType.Euler;
|
||||
|
||||
genericBone.position[0] = bone.Translation.X;
|
||||
genericBone.position[1] = bone.Translation.Y;
|
||||
genericBone.position[2] = bone.Translation.Z;
|
||||
|
||||
genericBone.scale[0] = bone.Scale.X;
|
||||
genericBone.scale[1] = bone.Scale.Y;
|
||||
genericBone.scale[2] = bone.Scale.Z;
|
||||
|
||||
genericBone.rotation[0] = bone.Rotation.X;
|
||||
genericBone.rotation[1] = bone.Rotation.Y;
|
||||
genericBone.rotation[2] = bone.Rotation.Z;
|
||||
|
||||
genericBone.Position = new OpenTK.Vector3(
|
||||
bone.Translation.X,
|
||||
bone.Translation.Y,
|
||||
bone.Translation.Z
|
||||
);
|
||||
genericBone.EulerRotation = new OpenTK.Vector3(
|
||||
bone.Rotation.X,
|
||||
bone.Rotation.Y,
|
||||
bone.Rotation.Z
|
||||
);
|
||||
genericBone.Scale = new OpenTK.Vector3(
|
||||
bone.Scale.X,
|
||||
bone.Scale.Y,
|
||||
bone.Scale.Z
|
||||
);
|
||||
Skeleton.bones.Add(genericBone);
|
||||
}
|
||||
|
||||
@ -289,8 +290,11 @@ namespace FirstPlugin
|
||||
List<ushort> SkinnedBoneTable = new List<ushort>();
|
||||
foreach (var prim in shape.Primatives)
|
||||
{
|
||||
if (prim.BoneIndexTable != null)
|
||||
if (prim.BoneIndexTable != null) {
|
||||
SkinnedBoneTable.AddRange(prim.BoneIndexTable);
|
||||
foreach (var bone in prim.BoneIndexTable)
|
||||
Console.WriteLine($"{genericMesh.Text} SkeletonB {Skeleton.bones[(int)bone].Text} index {bone}");
|
||||
}
|
||||
}
|
||||
|
||||
//Now load the vertex and face data
|
||||
@ -368,7 +372,6 @@ namespace FirstPlugin
|
||||
// Console.WriteLine("boneIds " + BoneIndices[j]);
|
||||
|
||||
// ushort index = shape.Primatives[0].BoneIndexTable[(uint)BoneIndices[j]];
|
||||
// vert.boneIds.Add((int)BoneIndices[j]);
|
||||
}
|
||||
}
|
||||
if (shape.BoneWeights.VertexData != null && HasWeights && shape.BoneWeights.VertexData.Length > v)
|
||||
@ -376,7 +379,6 @@ namespace FirstPlugin
|
||||
var BoneWeights = shape.BoneWeights.VertexData[v];
|
||||
for (int j = 0; j < shape.boneDimension; j++)
|
||||
{
|
||||
Console.WriteLine("weight " + BoneWeights[j]);
|
||||
vert.boneWeights.Add(BoneWeights[j]);
|
||||
}
|
||||
}
|
||||
|
@ -148,9 +148,9 @@ namespace FirstPlugin
|
||||
|
||||
if (node.RotationX.HasKeys || node.RotationY.HasKeys || node.RotationZ.HasKeys)
|
||||
{
|
||||
float x = node.RotationX.HasKeys ? node.RotationX.GetFrameValue(Frame) : b.rotation[0];
|
||||
float y = node.RotationY.HasKeys ? node.RotationY.GetFrameValue(Frame) : b.rotation[1];
|
||||
float z = node.RotationZ.HasKeys ? node.RotationZ.GetFrameValue(Frame) : b.rotation[2];
|
||||
float x = node.RotationX.HasKeys ? node.RotationX.GetFrameValue(Frame) : b.EulerRotation.X;
|
||||
float y = node.RotationY.HasKeys ? node.RotationY.GetFrameValue(Frame) : b.EulerRotation.Y;
|
||||
float z = node.RotationZ.HasKeys ? node.RotationZ.GetFrameValue(Frame) : b.EulerRotation.Z;
|
||||
b.rot = EulerToQuat(z, y, x);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ using Toolbox;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using HyruleWarriors.G1M;
|
||||
using Toolbox.Library.Forms;
|
||||
using Toolbox.Library.Rendering;
|
||||
using OpenTK;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
@ -39,6 +41,54 @@ namespace HyruleWarriors.G1M
|
||||
}
|
||||
}
|
||||
|
||||
//Check for the viewport in the object editor
|
||||
//This is attached to it to load multiple file formats within the object editor to the viewer
|
||||
Viewport viewport
|
||||
{
|
||||
get
|
||||
{
|
||||
var editor = LibraryGUI.GetObjectEditor();
|
||||
return editor.GetViewport();
|
||||
}
|
||||
set
|
||||
{
|
||||
var editor = LibraryGUI.GetObjectEditor();
|
||||
editor.LoadViewport(value);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawablesLoaded = false;
|
||||
public override void OnClick(TreeView treeView)
|
||||
{
|
||||
//Make sure opengl is enabled
|
||||
if (Runtime.UseOpenGL)
|
||||
{
|
||||
//Open the viewport
|
||||
if (viewport == null)
|
||||
{
|
||||
viewport = new Viewport(ObjectEditor.GetDrawableContainers());
|
||||
viewport.Dock = DockStyle.Fill;
|
||||
}
|
||||
|
||||
//Make sure to load the drawables only once so set it to true!
|
||||
if (!DrawablesLoaded)
|
||||
{
|
||||
ObjectEditor.AddContainer(DrawableContainer);
|
||||
DrawablesLoaded = true;
|
||||
}
|
||||
|
||||
//Reload which drawable to display
|
||||
viewport.ReloadDrawables(DrawableContainer);
|
||||
LibraryGUI.LoadEditor(viewport);
|
||||
|
||||
viewport.Text = Text;
|
||||
}
|
||||
}
|
||||
|
||||
public G1M_Renderer Renderer;
|
||||
|
||||
public DrawableContainer DrawableContainer = new DrawableContainer();
|
||||
|
||||
public void Load(System.IO.Stream stream)
|
||||
{
|
||||
Read(new FileReader(stream));
|
||||
@ -54,9 +104,20 @@ namespace HyruleWarriors.G1M
|
||||
}
|
||||
|
||||
public G1MS Skeleton { get; set; }
|
||||
public G1MG Model { get; set; }
|
||||
public NUNO NUNO { get; set; }
|
||||
public NUNV NUNV { get; set; }
|
||||
|
||||
TreeNode meshNode;
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
Renderer = new G1M_Renderer();
|
||||
Renderer.G1MFile = this;
|
||||
DrawableContainer = new DrawableContainer();
|
||||
DrawableContainer.Name = FileName;
|
||||
DrawableContainer.Drawables.Add(Renderer);
|
||||
|
||||
reader.ByteOrder = Syroot.BinaryData.ByteOrder.BigEndian;
|
||||
string Magic = reader.ReadString(4);
|
||||
|
||||
@ -84,52 +145,86 @@ namespace HyruleWarriors.G1M
|
||||
for (int i = 0; i < numChunks; i++)
|
||||
{
|
||||
G1MChunkCommon chunk = new G1MChunkCommon();
|
||||
long chunkPosition = reader.Position;
|
||||
string chunkMagic = reader.ReadString(4, Encoding.ASCII);
|
||||
uint chunkVersion = reader.ReadUInt32();
|
||||
uint chunkSize = reader.ReadUInt32();
|
||||
chunk.ChunkPosition = reader.Position;
|
||||
chunk.Magic = reader.ReadString(4, Encoding.ASCII);
|
||||
chunk.ChunkVersion = reader.ReadUInt32();
|
||||
chunk.ChunkSize = reader.ReadUInt32();
|
||||
|
||||
if (chunkMagic == "G1MF")
|
||||
Console.WriteLine("chunkMagic " + chunk.Magic);
|
||||
if (chunk.Magic == "G1MF")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "SM1G")
|
||||
else if (chunk.Magic == "SM1G" || chunk.Magic == "G1MS")
|
||||
{
|
||||
Skeleton = new G1MS(reader);
|
||||
Renderer.Skeleton = Skeleton.GenericSkeleton;
|
||||
DrawableContainer.Drawables.Add(Skeleton.GenericSkeleton);
|
||||
|
||||
TreeNode skeleton = new TreeNode("Skeleton");
|
||||
Nodes.Add(skeleton);
|
||||
foreach (var bn in Skeleton.GenericSkeleton.bones)
|
||||
if (bn.Parent == null)
|
||||
{
|
||||
skeleton.Nodes.Add(bn);
|
||||
}
|
||||
else if (chunkMagic == "G1MS")
|
||||
}
|
||||
else if (chunk.Magic == "G1MM")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "G1MM")
|
||||
else if (chunk.Magic == "G1MG")
|
||||
{
|
||||
Model = new G1MG(reader);
|
||||
Renderer.Meshes.AddRange(Model.GenericMeshes);
|
||||
|
||||
meshNode = new TreeNode("Meshes");
|
||||
Nodes.Add(meshNode);
|
||||
foreach (var mesh in Model.GenericMeshes)
|
||||
meshNode.Nodes.Add(mesh);
|
||||
|
||||
if (Skeleton != null)
|
||||
{
|
||||
foreach (var mesh in Model.GenericMeshes)
|
||||
{
|
||||
bool isSingleBind = false;;
|
||||
|
||||
if (isSingleBind)
|
||||
{
|
||||
for (int v = 0; v < mesh.vertices.Count; v++)
|
||||
{
|
||||
var boneId = mesh.vertices[v].boneIds[0];
|
||||
var transform = Skeleton.GenericSkeleton.bones[boneId].Transform;
|
||||
mesh.vertices[v].pos = Vector3.TransformPosition(
|
||||
mesh.vertices[v].pos, transform);
|
||||
mesh.vertices[v].nrm = Vector3.TransformNormal(
|
||||
mesh.vertices[v].nrm, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (chunk.Magic == "COLL")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "G1MG")
|
||||
else if (chunk.Magic == "HAIR")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "COLL")
|
||||
else if (chunk.Magic == "NUNO")
|
||||
{
|
||||
NUNO = new NUNO(reader, chunk.ChunkVersion);
|
||||
}
|
||||
else if (chunk.Magic == "NUNS")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "HAIR")
|
||||
else if (chunk.Magic == "NUNV")
|
||||
{
|
||||
|
||||
NUNV = new NUNV(reader, chunk.ChunkVersion);
|
||||
}
|
||||
else if (chunkMagic == "NUNO")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "NUNS")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "NUNV")
|
||||
{
|
||||
|
||||
}
|
||||
else if (chunkMagic == "EXTR")
|
||||
else if (chunk.Magic == "EXTR")
|
||||
{
|
||||
|
||||
}
|
||||
@ -137,11 +232,138 @@ namespace HyruleWarriors.G1M
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
reader.SeekBegin(chunk.ChunkPosition + chunk.ChunkSize);
|
||||
}
|
||||
|
||||
ComputeClothDrivers();
|
||||
SetLevelOfDetailGroups();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes mesh and bone cloth drivers
|
||||
/// </summary>
|
||||
public void ComputeClothDrivers()
|
||||
{
|
||||
var boneList = Skeleton.GenericSkeleton.bones;
|
||||
|
||||
var nunProps = new List<NUNO.NUNOType0303Struct>();
|
||||
uint nunoOffset = 0;
|
||||
if (NUNO != null)
|
||||
{
|
||||
nunoOffset = (uint)NUNO.NUNO0303StructList.Count;
|
||||
foreach (var nuno0303 in NUNO.NUNO0303StructList)
|
||||
nunProps.Add(nuno0303);
|
||||
}
|
||||
if (NUNV != null) {
|
||||
foreach (var nuno0303 in NUNV.NUNV0303StructList)
|
||||
nunProps.Add(nuno0303);
|
||||
}
|
||||
|
||||
foreach (var prop in nunProps)
|
||||
{
|
||||
int boneStart = boneList.Count;
|
||||
var parentBone = Model.JointInfos[prop.BoneParentID - 1].JointIndices[0];
|
||||
|
||||
GenericRenderedObject mesh = new GenericRenderedObject();
|
||||
mesh.Text = $"driver_{boneList.Count}";
|
||||
mesh.Checked = true;
|
||||
Renderer.Meshes.Add(mesh);
|
||||
meshNode.Nodes.Add(mesh);
|
||||
|
||||
var polyGroup = new STGenericPolygonGroup();
|
||||
polyGroup.Material = new STGenericMaterial();
|
||||
polyGroup.Material.Text = "driver_cloth";
|
||||
polyGroup.PrimativeType = STPrimitiveType.Triangles;
|
||||
mesh.PolygonGroups.Add(polyGroup);
|
||||
|
||||
for (int p = 0; p < prop.Points.Length; p++) {
|
||||
var point = prop.Points[p];
|
||||
var link = prop.Influences[p];
|
||||
|
||||
STBone b = new STBone(Skeleton.GenericSkeleton);
|
||||
b.Text = $"CP_{boneList.Count}";
|
||||
b.FromTransform(OpenTK.Matrix4.Identity);
|
||||
b.Position = point.Xyz;
|
||||
b.parentIndex = link.P3;
|
||||
if (b.parentIndex == -1)
|
||||
b.parentIndex = (int)parentBone;
|
||||
else
|
||||
{
|
||||
b.parentIndex += boneStart;
|
||||
b.Position = OpenTK.Vector3.TransformPosition(
|
||||
point.Xyz, Skeleton.GenericSkeleton.GetBoneTransform((int)parentBone) *
|
||||
Skeleton.GenericSkeleton.GetBoneTransform(b.parentIndex).Inverted());
|
||||
}
|
||||
|
||||
boneList.Add(b);
|
||||
|
||||
Skeleton.GenericSkeleton.reset();
|
||||
Skeleton.GenericSkeleton.update();
|
||||
|
||||
mesh.vertices.Add(new Vertex()
|
||||
{
|
||||
pos = Vector3.TransformPosition(Vector3.Zero,
|
||||
Skeleton.GenericSkeleton.GetBoneTransform(boneList.Count - 1)),
|
||||
boneWeights = new List<float>() { 1 },
|
||||
boneIds = new List<int>() { boneList.Count - 1 },
|
||||
});
|
||||
|
||||
if (link.P1 > 0 && link.P3 > 0)
|
||||
{
|
||||
polyGroup.faces.Add(p);
|
||||
polyGroup.faces.Add(link.P1);
|
||||
polyGroup.faces.Add(link.P3);
|
||||
}
|
||||
if (link.P2 > 0 && link.P4 > 0)
|
||||
{
|
||||
polyGroup.faces.Add(p);
|
||||
polyGroup.faces.Add(link.P2);
|
||||
polyGroup.faces.Add(link.P4);
|
||||
}
|
||||
}
|
||||
|
||||
mesh.CalculateNormals();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetLevelOfDetailGroups()
|
||||
{
|
||||
foreach (var group in Model.LodGroups[0].Meshes)
|
||||
{
|
||||
var isCloth = (group.ID & 0xF) == 1;
|
||||
var isPoint = (group.ID & 0xF) == 2;
|
||||
var NunoSection = (group.ID2 - (group.ID2 % 10000)) / 10000;
|
||||
|
||||
foreach (var polyindex in group.Indices)
|
||||
{
|
||||
if (polyindex > Model.SubMeshInfos.Length)
|
||||
continue;
|
||||
|
||||
var poly = Model.SubMeshInfos[polyindex];
|
||||
var mesh = Model.GenericMeshes[(int)polyindex];
|
||||
|
||||
mesh.Text = $"{group.Name}_" + (isPoint ? "point" : "") + (isCloth ? "cloth" : "") + $"_{polyindex}";
|
||||
mesh.Text = mesh.Text.Replace(@"\p{C}+", string.Empty);
|
||||
|
||||
if (isPoint)
|
||||
{
|
||||
for (int i = 0; i < mesh.vertices.Count; i++)
|
||||
{
|
||||
var vert = mesh.vertices[i];
|
||||
vert.pos = Vector3.TransformPosition(vert.pos,
|
||||
Skeleton.GenericSkeleton.GetBoneTransform((int)Model.JointInfos[poly.IndexIntoJointMap].JointIndices[0]));
|
||||
vert.nrm = Vector3.TransformNormal(vert.nrm,
|
||||
Skeleton.GenericSkeleton.GetBoneTransform((int)Model.JointInfos[poly.IndexIntoJointMap].JointIndices[0]));
|
||||
|
||||
mesh.vertices[i] = vert;
|
||||
}
|
||||
}
|
||||
if (isCloth)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
public interface IChunk
|
||||
{
|
||||
void Read(FileReader reader);
|
||||
void Write(FileWriter reader);
|
||||
}
|
||||
|
||||
public class G1MCommon
|
||||
{
|
||||
public static T[] ParseStructArray<T>(FileReader reader) {
|
||||
int count = reader.ReadInt32();
|
||||
return reader.ReadMultipleStructs<T>(count).ToArray();
|
||||
}
|
||||
|
||||
public static T[] ParseArray<T>(FileReader reader)
|
||||
where T : IChunk, new()
|
||||
{
|
||||
int count = reader.ReadInt32();
|
||||
T[] values = new T[count];
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
values[i] = new T();
|
||||
values[i].Read(reader);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
public enum VertexAttriubte
|
||||
public enum VertexAttriubte : byte
|
||||
{
|
||||
Position,
|
||||
Weights,
|
||||
@ -19,4 +19,11 @@ namespace HyruleWarriors.G1M
|
||||
Color = 0x0A,
|
||||
Fog = 0x0B,
|
||||
}
|
||||
|
||||
public enum FaceType : uint
|
||||
{
|
||||
Byte = 0x8,
|
||||
UInt16 = 0x10,
|
||||
UInt32 = 0x20,
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.Rendering;
|
||||
using OpenTK;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
@ -12,15 +15,637 @@ namespace HyruleWarriors.G1M
|
||||
{
|
||||
private string Type { get; set; }
|
||||
|
||||
public BoundingBox BoundingBox;
|
||||
|
||||
public Material[] Materials { get; set; }
|
||||
public ShaderData[] Shaders { get; set; }
|
||||
public VertexBuffer[] VertexBuffers { get; set; }
|
||||
public VertexAttributeList[] Attributes { get; set; }
|
||||
public JointMapInfo[] JointInfos { get; set; }
|
||||
public IndexBuffer[] IndexBuffers { get; set; }
|
||||
public SubMeshInfo[] SubMeshInfos { get; set; }
|
||||
public LodGroup[] LodGroups { get; set; }
|
||||
|
||||
public List<GenericRenderedObject> GenericMeshes = new List<GenericRenderedObject>();
|
||||
|
||||
public G1MG(FileReader reader)
|
||||
{
|
||||
Type = reader.ReadString(3, Encoding.ASCII);
|
||||
reader.ReadByte();//padding
|
||||
reader.ReadSingle(); //unk
|
||||
BoundingBox = new BoundingBox()
|
||||
{
|
||||
Min = reader.ReadVec3(),
|
||||
Max = reader.ReadVec3(),
|
||||
};
|
||||
|
||||
if (Type == "DX1" || Type == "NX_")
|
||||
uint numChunks = reader.ReadUInt32();
|
||||
for (int i = 0; i < numChunks; i++)
|
||||
{
|
||||
long pos = reader.Position;
|
||||
uint chunkMagic = reader.ReadUInt32();
|
||||
uint chunkSize = reader.ReadUInt32();
|
||||
switch (chunkMagic)
|
||||
{
|
||||
case 0x00010001: //Unknown
|
||||
break;
|
||||
case 0x00010002: //Materials
|
||||
Materials = G1MCommon.ParseArray<Material>(reader);
|
||||
break;
|
||||
case 0x00010003: //Shaders
|
||||
Shaders = G1MCommon.ParseArray<ShaderData>(reader);
|
||||
break;
|
||||
case 0x00010004: //Vertex Buffer
|
||||
VertexBuffers = G1MCommon.ParseArray<VertexBuffer>(reader);
|
||||
break;
|
||||
case 0x00010005: //Specs
|
||||
Attributes = G1MCommon.ParseArray<VertexAttributeList>(reader);
|
||||
break;
|
||||
case 0x00010006: //Joint map info
|
||||
JointInfos = G1MCommon.ParseArray<JointMapInfo>(reader);
|
||||
break;
|
||||
case 0x00010007: //Index buffer
|
||||
IndexBuffers = G1MCommon.ParseArray<IndexBuffer>(reader);
|
||||
break;
|
||||
case 0x00010008: //Sub mesh info
|
||||
SubMeshInfos = G1MCommon.ParseArray<SubMeshInfo>(reader);
|
||||
break;
|
||||
case 0x00010009: //Level of detail info
|
||||
LodGroups = G1MCommon.ParseArray<LodGroup>(reader);
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine("Unknown chunk! " + chunkMagic.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
reader.SeekBegin(pos + chunkSize);
|
||||
}
|
||||
|
||||
ToGenericMeshes(reader.IsBigEndian);
|
||||
}
|
||||
|
||||
private void ToGenericMeshes(bool isBigEndian)
|
||||
{
|
||||
foreach (SubMeshInfo meshInfo in SubMeshInfos) {
|
||||
GenericRenderedObject genericMesh = new GenericRenderedObject();
|
||||
genericMesh.Text = $"Mesh_{GenericMeshes.Count}";
|
||||
GenericMeshes.Add(genericMesh);
|
||||
|
||||
STGenericPolygonGroup polyGroup = new STGenericPolygonGroup();
|
||||
genericMesh.PolygonGroups.Add(polyGroup);
|
||||
|
||||
var mat = Materials[(int)meshInfo.TextureID];
|
||||
mat.Diffuse.TextureIndex = mat.TextureIndices[0][0];
|
||||
polyGroup.Material = mat;
|
||||
|
||||
Console.WriteLine($"TextureID {meshInfo.TextureID}");
|
||||
Console.WriteLine($"MaterialID {meshInfo.MaterialID}");
|
||||
|
||||
//Set face type
|
||||
if (meshInfo.IndexBufferFormat == 3)
|
||||
polyGroup.PrimativeType = STPrimitiveType.Triangles;
|
||||
else if (meshInfo.IndexBufferFormat == 4)
|
||||
polyGroup.PrimativeType = STPrimitiveType.TrangleStrips;
|
||||
|
||||
//Get faces
|
||||
var buffer = IndexBuffers[(int)meshInfo.IndexBufferID];
|
||||
var indcies = buffer.GetIndices(isBigEndian,
|
||||
meshInfo.IndexBufferOffset,
|
||||
meshInfo.IndexBufferCount).ToArray();
|
||||
|
||||
for (int f = 0; f < indcies.Length; f++)
|
||||
polyGroup.faces.Add((int)indcies[f]);
|
||||
|
||||
//Get vertices
|
||||
genericMesh.vertices.AddRange(GetVertices((int)meshInfo.VertexBufferID,
|
||||
(int)meshInfo.IndexIntoJointMap, meshInfo.VertexBufferOffset, isBigEndian));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Vertex> GetVertices(int index, int jointMapIndex, uint offset, bool isBigEndian)
|
||||
{
|
||||
var buffer = VertexBuffers[index];
|
||||
var attributeList = Attributes[index];
|
||||
|
||||
List<Vertex> vertices = new List<Vertex>();
|
||||
|
||||
var skinningList = JointInfos[jointMapIndex];
|
||||
using (var dataReader = new FileReader(buffer.BufferData))
|
||||
{
|
||||
dataReader.SetByteOrder(isBigEndian);
|
||||
dataReader.SeekBegin(offset);
|
||||
for (int i = 0; i < buffer.ElementCount; i++)
|
||||
{
|
||||
Vertex vertex = new Vertex();
|
||||
vertices.Add(vertex);
|
||||
foreach (var att in attributeList.Attributes)
|
||||
{
|
||||
dataReader.SeekBegin((uint)(i * buffer.Stride + att.Offset));
|
||||
switch (att.AttributeType)
|
||||
{
|
||||
case VertexAttriubte.Position:
|
||||
if (att.DataType == 0x02 || att.DataType == 0x03 ||
|
||||
att.DataType == 0x0200 || att.DataType == 0x0300)
|
||||
{
|
||||
vertex.pos = new Vector3(
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle());
|
||||
dataReader.ReadSingle(); //extra
|
||||
}
|
||||
else if (att.DataType == 0x000B || att.DataType == 0x000B)
|
||||
{
|
||||
vertex.pos = new Vector3(
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle());
|
||||
dataReader.ReadHalfSingle(); //extra
|
||||
}
|
||||
break;
|
||||
case VertexAttriubte.Normals:
|
||||
if (att.DataType == 0x000B || att.DataType == 0x000B)
|
||||
{
|
||||
vertex.nrm = new Vector3(
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle());
|
||||
dataReader.ReadHalfSingle(); //extra
|
||||
}
|
||||
else if (att.DataType == 0x02 || att.DataType == 0x03 ||
|
||||
att.DataType == 0x0200 || att.DataType == 0x0300)
|
||||
{
|
||||
vertex.nrm = new Vector3(
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle());
|
||||
dataReader.ReadSingle(); //extra
|
||||
}
|
||||
break;
|
||||
case VertexAttriubte.TexCoord0:
|
||||
if (att.DataType == 0x0001 || att.DataType == 0x0100)
|
||||
{
|
||||
vertex.uv0 = new Vector2(
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle());
|
||||
}
|
||||
else if (att.DataType == 0x000A || att.DataType == 0x0A00)
|
||||
{
|
||||
vertex.uv0 = new Vector2(
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle());
|
||||
}
|
||||
break;
|
||||
case VertexAttriubte.Color:
|
||||
if (att.Layer == 0)
|
||||
{
|
||||
if (att.DataType == 0x0200 || att.DataType == 0x0300)
|
||||
{
|
||||
vertex.col = new Vector4(
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle(),
|
||||
1.0f);
|
||||
}
|
||||
else if (att.DataType == 0x0003 || att.DataType == 0x0300)
|
||||
{
|
||||
vertex.col = new Vector4(
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle(),
|
||||
dataReader.ReadSingle());
|
||||
}
|
||||
else if (att.DataType == 0x000B || att.DataType == 0x0B00)
|
||||
{
|
||||
vertex.col = new Vector4(
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle(),
|
||||
dataReader.ReadHalfSingle());
|
||||
}
|
||||
else if (att.DataType == 0x000D || att.DataType == 0x0D00)
|
||||
{
|
||||
vertex.col = new Vector4(
|
||||
dataReader.ReadByte() / 255f,
|
||||
dataReader.ReadByte() / 255f,
|
||||
dataReader.ReadByte() / 255f,
|
||||
dataReader.ReadByte() / 255f);
|
||||
}
|
||||
}
|
||||
else //Cloth layer
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
case VertexAttriubte.Weights:
|
||||
if (att.DataType == 0x0000)
|
||||
{
|
||||
vertex.boneWeights.Add(dataReader.ReadSingle());
|
||||
float value2 = 1.0f - vertex.boneWeights[0];
|
||||
vertex.boneWeights.Add(value2);
|
||||
}
|
||||
else if (att.DataType == 0x0001 || att.DataType == 0x0100)
|
||||
{
|
||||
vertex.boneWeights.Add(dataReader.ReadSingle());
|
||||
vertex.boneWeights.Add(dataReader.ReadSingle());
|
||||
float z = 1.0f - vertex.boneWeights[0] - vertex.boneWeights[1];
|
||||
vertex.boneWeights.Add(z);
|
||||
}
|
||||
else if (att.DataType == 0x0002 || att.DataType == 0x0200)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
vertex.boneWeights.Add(dataReader.ReadSingle());
|
||||
var value4 = 1 - vertex.boneWeights[0] - vertex.boneWeights[1] - vertex.boneWeights[2];
|
||||
|
||||
vertex.boneWeights.Add(value4);
|
||||
}
|
||||
else if (att.DataType == 0x0003 || att.DataType == 0x0300)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
vertex.boneWeights.Add(dataReader.ReadSingle());
|
||||
}
|
||||
else if (att.DataType == 0x000A || att.DataType == 0x0A00)
|
||||
{
|
||||
vertex.boneWeights.Add(dataReader.ReadHalfSingle());
|
||||
vertex.boneWeights.Add(dataReader.ReadHalfSingle());
|
||||
float z = 1.0f - vertex.boneWeights[0] - vertex.boneWeights[1];
|
||||
vertex.boneWeights.Add(z);
|
||||
}
|
||||
else if (att.DataType == 0x000B || att.DataType == 0x0B00)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
vertex.boneWeights.Add(dataReader.ReadHalfSingle());
|
||||
}
|
||||
else if (att.DataType == 0x000D || att.DataType == 0x0D00)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
vertex.boneWeights.Add(dataReader.ReadByte() / 255f);
|
||||
}
|
||||
break;
|
||||
case VertexAttriubte.BoneIndices:
|
||||
if (att.DataType == 0x0005 || att.DataType == 0x0500)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
vertex.boneIds.Add(dataReader.ReadByte());
|
||||
}
|
||||
else if (att.DataType == 0x0007 || att.DataType == 0x0700)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
vertex.boneIds.Add(dataReader.ReadUInt16());
|
||||
}
|
||||
else if (att.DataType == 0x000D || att.DataType == 0x0D00)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
vertex.boneIds.Add(dataReader.ReadByte());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertex.boneWeights.All(x => x == 0))
|
||||
{
|
||||
vertex.boneIds.Clear();
|
||||
vertex.boneWeights.Clear();
|
||||
|
||||
vertex.boneIds.Add((int)skinningList.JointIndices[0]);
|
||||
vertex.boneWeights.Add(1);
|
||||
}
|
||||
else if (vertex.boneWeights.Count > 0)
|
||||
{
|
||||
for (int j = 0; j < vertex.boneIds.Count; j++)
|
||||
{
|
||||
int id = vertex.boneIds[j] / 3;
|
||||
if (skinningList.JointIndices.Length > id)
|
||||
id = (int)skinningList.JointIndices[id];
|
||||
vertex.boneIds[j] = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return vertices;
|
||||
}
|
||||
}
|
||||
|
||||
public struct BoundingBox
|
||||
{
|
||||
public Vector3 Min { get; set; }
|
||||
public Vector3 Max { get; set; }
|
||||
}
|
||||
|
||||
public class Material : STGenericMaterial, IChunk
|
||||
{
|
||||
public uint Unknown1;
|
||||
public uint Unknown2;
|
||||
public uint Unknown3;
|
||||
|
||||
public List<ushort[]> TextureIndices;
|
||||
|
||||
public G1MTextureMap Diffuse;
|
||||
|
||||
public void Read(FileReader reader) {
|
||||
Unknown1 = reader.ReadUInt32(); //0
|
||||
uint numTextures = reader.ReadUInt32();
|
||||
Unknown2 = reader.ReadUInt32(); //0, 1, -1
|
||||
Unknown3 = reader.ReadUInt32(); //0, 1, -1
|
||||
|
||||
TextureIndices = new List<ushort[]>();
|
||||
for (int i = 0; i < numTextures; i++)
|
||||
TextureIndices.Add(reader.ReadUInt16s(6));
|
||||
|
||||
Diffuse = new G1MTextureMap();
|
||||
Diffuse.Type = STGenericMatTexture.TextureType.Diffuse;
|
||||
TextureMaps.Add(Diffuse);
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer) {
|
||||
writer.Write(Unknown1);
|
||||
writer.Write(TextureIndices.Count);
|
||||
writer.Write(Unknown2);
|
||||
writer.Write(Unknown3);
|
||||
foreach (var indices in TextureIndices)
|
||||
writer.Write(indices);
|
||||
}
|
||||
}
|
||||
|
||||
public class G1MTextureMap : STGenericMatTexture
|
||||
{
|
||||
public int TextureIndex { get; set; } = -1;
|
||||
}
|
||||
|
||||
public class ShaderData : IChunk
|
||||
{
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class VertexBuffer : IChunk
|
||||
{
|
||||
public uint Unknown1 { get; set; }
|
||||
public uint Stride { get; set; }
|
||||
public uint ElementCount { get; set; }
|
||||
public uint Unknown2 { get; set; }
|
||||
|
||||
public byte[] BufferData { get; set; }
|
||||
|
||||
public void Read(FileReader reader) {
|
||||
Unknown1 = reader.ReadUInt32();
|
||||
Stride = reader.ReadUInt32();
|
||||
ElementCount = reader.ReadUInt32();
|
||||
Unknown2 = reader.ReadUInt32();
|
||||
BufferData = reader.ReadBytes((int)(ElementCount * Stride));
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer) {
|
||||
writer.Write(Unknown1);
|
||||
writer.Write(Stride);
|
||||
writer.Write(ElementCount);
|
||||
writer.Write(Unknown2);
|
||||
writer.Write(BufferData);
|
||||
}
|
||||
}
|
||||
|
||||
public class VertexAttributeList : IChunk
|
||||
{
|
||||
public List<VertexAttribute> Attributes = new List<VertexAttribute>();
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
uint numBuffers = reader.ReadUInt32();
|
||||
int[] bufferIndices = reader.ReadInt32s((int)numBuffers);
|
||||
uint numAttributes = reader.ReadUInt32();
|
||||
for (int i = 0; i < numAttributes; i++)
|
||||
{
|
||||
var buffer = new VertexAttribute();
|
||||
buffer.BufferIndex = bufferIndices[(int)reader.ReadUInt16()];
|
||||
buffer.Offset = reader.ReadUInt16();
|
||||
byte flag1 = reader.ReadByte();
|
||||
byte flag2 = reader.ReadByte();
|
||||
buffer.DataType = (ushort)((flag1 << 8) | flag2);
|
||||
buffer.AttributeType = (VertexAttriubte)reader.ReadByte();
|
||||
buffer.Layer = reader.ReadByte();
|
||||
Attributes.Add(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer)
|
||||
{
|
||||
writer.Write(Attributes.Count);
|
||||
}
|
||||
}
|
||||
|
||||
public class VertexAttribute
|
||||
{
|
||||
public int BufferIndex { get; set; }
|
||||
public ushort Offset { get; set; }
|
||||
public ushort DataType { get; set; }
|
||||
public VertexAttriubte AttributeType { get; set; }
|
||||
public byte Layer { get; set; }
|
||||
}
|
||||
|
||||
public class JointMapInfo : IChunk
|
||||
{
|
||||
public uint[] UnkIndices { get; set; }
|
||||
public uint[] JointIndices { get; set; }
|
||||
public uint[] ClothIndices { get; set; }
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
uint numJoints = reader.ReadUInt32();
|
||||
|
||||
UnkIndices = new uint[(int)numJoints];
|
||||
JointIndices = new uint[(int)numJoints];
|
||||
ClothIndices = new uint[(int)numJoints];
|
||||
for (int i = 0; i < numJoints; i++) {
|
||||
UnkIndices[i] = reader.ReadUInt32();
|
||||
ClothIndices[i] = reader.ReadUInt32() & 0xFFFF;
|
||||
JointIndices[i] = reader.ReadUInt32() & 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer)
|
||||
{
|
||||
writer.Write(JointIndices.Length);
|
||||
for (int i = 0; i < JointIndices.Length; i++) {
|
||||
writer.Write(UnkIndices[i]);
|
||||
writer.Write(ClothIndices[i]);
|
||||
writer.Write(JointIndices[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IndexBuffer : IChunk
|
||||
{
|
||||
public uint ElementCount { get; set; }
|
||||
public FaceType DataType { get; set; }
|
||||
public uint Unknown1 { get; set; }
|
||||
|
||||
public uint Stride
|
||||
{
|
||||
get
|
||||
{
|
||||
if (DataType == FaceType.Byte) return 1;
|
||||
else if (DataType == FaceType.UInt16) return 2;
|
||||
else if (DataType == FaceType.UInt32) return 4;
|
||||
else
|
||||
throw new Exception("Unknown index stride! " + DataType);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] BufferData { get; set; }
|
||||
|
||||
public IEnumerable<uint> GetIndices(bool isBigEndian, uint offset, uint elementCount)
|
||||
{
|
||||
using (var reader = new FileReader(BufferData))
|
||||
{
|
||||
reader.SetByteOrder(isBigEndian);
|
||||
reader.SeekBegin(offset * Stride);
|
||||
|
||||
switch (DataType)
|
||||
{
|
||||
case FaceType.Byte:
|
||||
for (; elementCount > 0; elementCount--)
|
||||
{
|
||||
yield return reader.ReadByte();
|
||||
}
|
||||
break;
|
||||
case FaceType.UInt16:
|
||||
for (; elementCount > 0; elementCount--)
|
||||
{
|
||||
yield return reader.ReadUInt16();
|
||||
}
|
||||
break;
|
||||
case FaceType.UInt32:
|
||||
for (; elementCount > 0; elementCount--)
|
||||
{
|
||||
yield return reader.ReadUInt32();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown type! " + DataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
ElementCount = reader.ReadUInt32();
|
||||
DataType = reader.ReadEnum<FaceType>(true);
|
||||
Unknown1 = reader.ReadUInt32();
|
||||
BufferData = reader.ReadBytes((int)(ElementCount * Stride));
|
||||
reader.Align(4);
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer)
|
||||
{
|
||||
writer.Write(ElementCount);
|
||||
writer.Write(DataType, true);
|
||||
writer.Write(Stride);
|
||||
writer.Write(Unknown1);
|
||||
writer.Write(BufferData);
|
||||
writer.Align(4);
|
||||
}
|
||||
}
|
||||
|
||||
public class SubMeshInfo : IChunk
|
||||
{
|
||||
public uint UnknownIndex;
|
||||
public uint VertexBufferID;
|
||||
public uint IndexIntoJointMap;
|
||||
public uint Unknown2;
|
||||
public uint TextureCount;
|
||||
public uint MaterialID;
|
||||
public uint TextureID;
|
||||
public uint IndexBufferID;
|
||||
public uint Unknown;
|
||||
public uint IndexBufferFormat;
|
||||
public uint VertexBufferOffset;
|
||||
public uint VertexBufferCount;
|
||||
public uint IndexBufferOffset;
|
||||
public uint IndexBufferCount;
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
UnknownIndex = reader.ReadUInt32();
|
||||
VertexBufferID = reader.ReadUInt32();
|
||||
IndexIntoJointMap = reader.ReadUInt32();
|
||||
Unknown2 = reader.ReadUInt32();
|
||||
TextureCount = reader.ReadUInt32();
|
||||
MaterialID = reader.ReadUInt32();
|
||||
TextureID = reader.ReadUInt32();
|
||||
IndexBufferID = reader.ReadUInt32();
|
||||
Unknown = reader.ReadUInt32();
|
||||
IndexBufferFormat = reader.ReadUInt32();
|
||||
VertexBufferOffset = reader.ReadUInt32();
|
||||
VertexBufferCount = reader.ReadUInt32();
|
||||
IndexBufferOffset = reader.ReadUInt32();
|
||||
IndexBufferCount = reader.ReadUInt32();
|
||||
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer)
|
||||
{
|
||||
writer.Write(UnknownIndex);
|
||||
writer.Write(VertexBufferID);
|
||||
writer.Write(IndexIntoJointMap);
|
||||
writer.Write(Unknown2);
|
||||
writer.Write(TextureCount);
|
||||
writer.Write(MaterialID);
|
||||
writer.Write(TextureID);
|
||||
writer.Write(IndexBufferID);
|
||||
writer.Write(Unknown);
|
||||
writer.Write(IndexBufferFormat);
|
||||
writer.Write(VertexBufferOffset);
|
||||
writer.Write(VertexBufferCount);
|
||||
writer.Write(IndexBufferOffset);
|
||||
writer.Write(IndexBufferCount);
|
||||
}
|
||||
}
|
||||
|
||||
public class LodGroup : IChunk
|
||||
{
|
||||
public List<LodMesh> Meshes = new List<LodMesh>();
|
||||
|
||||
public void Read(FileReader reader)
|
||||
{
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
uint numMeshes1 = reader.ReadUInt32();
|
||||
uint numMeshes2 = reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
|
||||
for (int i = 0; i < numMeshes1 + numMeshes2; i++) {
|
||||
LodMesh msh = new LodMesh();
|
||||
Meshes.Add(msh);
|
||||
msh.Name = reader.ReadString(16, true);
|
||||
msh.ID = reader.ReadUInt32();
|
||||
msh.ID2 = reader.ReadUInt32();
|
||||
uint numIndces = reader.ReadUInt32();
|
||||
msh.Indices = reader.ReadUInt32s((int)numIndces);
|
||||
if (numIndces == 0)
|
||||
reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(FileWriter writer)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class LodMesh
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public uint ID { get; set; }
|
||||
public uint ID2 { get; set; }
|
||||
public uint[] Indices { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -4,14 +4,63 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
using Toolbox.Library;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
public class G1MS : G1MChunkCommon
|
||||
{
|
||||
public G1MS(FileReader reader)
|
||||
public STSkeleton GenericSkeleton = new STSkeleton();
|
||||
|
||||
public short[] BoneIndices { get; set; }
|
||||
|
||||
public G1MS ChildSkeleton { get; set; }
|
||||
|
||||
public ushort[] BoneIndexList { get; set; }
|
||||
|
||||
public G1MS(FileReader reader, bool isParent = true)
|
||||
{
|
||||
uint pos = (uint)reader.Position - 12;
|
||||
uint boneOffset = reader.ReadUInt32() + pos;
|
||||
ushort conditionNumber = reader.ReadUInt16();
|
||||
|
||||
if (isParent && conditionNumber == 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ushort unknown = reader.ReadUInt16();
|
||||
ushort numBones = reader.ReadUInt16();
|
||||
ushort numBoneIndices = reader.ReadUInt16();
|
||||
BoneIndices = reader.ReadInt16s((int)numBoneIndices);
|
||||
|
||||
BoneIndexList = new ushort[numBones];
|
||||
for (ushort i = 0; i < BoneIndices?.Length; i++)
|
||||
if (BoneIndices[i] != -1)
|
||||
BoneIndexList[BoneIndices[i]] = i;
|
||||
|
||||
reader.SeekBegin(boneOffset);
|
||||
for (int i = 0; i < numBones; i++) {
|
||||
var scale = reader.ReadVec3();
|
||||
int parentIndex = reader.ReadInt32();
|
||||
var quat = reader.ReadQuaternion(true);
|
||||
var position = reader.ReadVec3();
|
||||
reader.ReadSingle();
|
||||
|
||||
// if ((parentIndex & 0x80000000) > 0 && parentIndex != -1)
|
||||
// parentIndex = parentIndex & 0x7FFFFFFF;
|
||||
|
||||
STBone genericBone = new STBone(GenericSkeleton);
|
||||
genericBone.Text = $"Bone {BoneIndexList[i]}";
|
||||
genericBone.Rotation = quat;
|
||||
genericBone.Scale = scale;
|
||||
genericBone.Position = position;
|
||||
genericBone.parentIndex = parentIndex;
|
||||
GenericSkeleton.bones.Add(genericBone);
|
||||
}
|
||||
|
||||
GenericSkeleton.reset();
|
||||
GenericSkeleton.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
160
File_Format_Library/FileFormats/HyruleWarriors/G1M/NUNO.cs
Normal file
160
File_Format_Library/FileFormats/HyruleWarriors/G1M/NUNO.cs
Normal file
@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
using OpenTK;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
public class NUNO
|
||||
{
|
||||
public List<NUNOType0303Struct> NUNO0303StructList = new List<NUNOType0303Struct>();
|
||||
|
||||
public class NUNOType0303Struct
|
||||
{
|
||||
public int Unknown { get; set; }
|
||||
public int BoneParentID { get; set; }
|
||||
public Vector4[] Points { get; set; }
|
||||
public NUNOInfluence[] Influences { get; set; }
|
||||
|
||||
public int Unknown2 { get; set; }
|
||||
public int Unknown3 { get; set; }
|
||||
public int Unknown4 { get; set; }
|
||||
}
|
||||
|
||||
public struct NUNOInfluence
|
||||
{
|
||||
public int P1 { get; set; }
|
||||
public int P2 { get; set; }
|
||||
public int P3 { get; set; }
|
||||
public int P4 { get; set; }
|
||||
public float P5 { get; set; }
|
||||
public float P6 { get; set; }
|
||||
}
|
||||
|
||||
public NUNO(FileReader reader, uint version)
|
||||
{
|
||||
uint numChunks = reader.ReadUInt32();
|
||||
for (int i = 0; i < numChunks; i++)
|
||||
{
|
||||
long pos = reader.Position;
|
||||
uint subchunkMagic = reader.ReadUInt32();
|
||||
uint subchunkSize = reader.ReadUInt32();
|
||||
uint subChunkCount = reader.ReadUInt32();
|
||||
for (int j = 0; j < subChunkCount; j++)
|
||||
{
|
||||
switch(subchunkMagic)
|
||||
{
|
||||
case 0x00030001:
|
||||
NUNO0303StructList.Add(ParseNUNOSection0301(reader, version));
|
||||
break;
|
||||
case 0x00030002:
|
||||
ParseNUNOSection0302(reader, version);
|
||||
break;
|
||||
case 0x00030003:
|
||||
NUNO0303StructList.Add(ParseNUNOSection0303(reader, version));
|
||||
break;
|
||||
case 0x00030004:
|
||||
ParseNUNOSection0304(reader, version);
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine("Unknown chunk! " + subchunkMagic.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.SeekBegin(pos + subchunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
public NUNOType0303Struct ParseNUNOSection0301(FileReader reader, uint version)
|
||||
{
|
||||
var nuno301 = new NUNOType0303Struct();
|
||||
nuno301.Unknown = reader.ReadInt32();
|
||||
uint numControlPoints = reader.ReadUInt32();
|
||||
uint unkCount = reader.ReadUInt32();
|
||||
nuno301.Unknown2 = reader.ReadInt32();
|
||||
nuno301.Unknown3 = reader.ReadInt32();
|
||||
nuno301.Unknown4 = reader.ReadInt32();
|
||||
reader.ReadInt32();
|
||||
reader.ReadInt32();
|
||||
nuno301.BoneParentID = reader.ReadInt32();
|
||||
|
||||
reader.ReadBytes(0x40);
|
||||
if (version >= 0x30303235)
|
||||
reader.ReadBytes(0x10);
|
||||
|
||||
nuno301.Influences = new NUNOInfluence[(int)numControlPoints];
|
||||
nuno301.Points = new Vector4[(int)numControlPoints];
|
||||
for (int i = 0; i < numControlPoints; i++)
|
||||
nuno301.Points[i] = reader.ReadVec4();
|
||||
|
||||
for (int i = 0; i < numControlPoints; i++) {
|
||||
var influence = new NUNOInfluence();
|
||||
influence.P1 = reader.ReadInt32();
|
||||
influence.P2 = reader.ReadInt32();
|
||||
influence.P3 = reader.ReadInt32();
|
||||
influence.P4 = reader.ReadInt32();
|
||||
influence.P5 = reader.ReadSingle();
|
||||
influence.P6 = reader.ReadSingle();
|
||||
nuno301.Influences[i] = influence;
|
||||
}
|
||||
|
||||
reader.Seek(48 * unkCount);
|
||||
reader.Seek(4 * nuno301.Unknown2);
|
||||
reader.Seek(4 * nuno301.Unknown3);
|
||||
reader.Seek(4 * nuno301.Unknown4);
|
||||
|
||||
return nuno301;
|
||||
}
|
||||
|
||||
public void ParseNUNOSection0302(FileReader reader, uint version)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NUNOType0303Struct ParseNUNOSection0303(FileReader reader, uint version)
|
||||
{
|
||||
var nuno301 = new NUNOType0303Struct();
|
||||
nuno301.Unknown = reader.ReadInt32();
|
||||
uint numControlPoints = reader.ReadUInt32();
|
||||
uint unkCount = reader.ReadUInt32();
|
||||
nuno301.Unknown2 = reader.ReadInt32();
|
||||
nuno301.BoneParentID = reader.ReadInt32();
|
||||
nuno301.Unknown3 = reader.ReadInt32();
|
||||
|
||||
reader.ReadBytes(0xB0);
|
||||
if (version >= 0x30303235)
|
||||
reader.ReadBytes(0x10);
|
||||
|
||||
nuno301.Influences = new NUNOInfluence[(int)numControlPoints];
|
||||
nuno301.Points = new Vector4[(int)numControlPoints];
|
||||
for (int i = 0; i < numControlPoints; i++)
|
||||
nuno301.Points[i] = reader.ReadVec4();
|
||||
|
||||
for (int i = 0; i < numControlPoints; i++)
|
||||
{
|
||||
var influence = new NUNOInfluence();
|
||||
influence.P1 = reader.ReadInt32();
|
||||
influence.P2 = reader.ReadInt32();
|
||||
influence.P3 = reader.ReadInt32();
|
||||
influence.P4 = reader.ReadInt32();
|
||||
influence.P5 = reader.ReadSingle();
|
||||
influence.P6 = reader.ReadSingle();
|
||||
nuno301.Influences[i] = influence;
|
||||
}
|
||||
|
||||
reader.Seek(48 * unkCount);
|
||||
reader.Seek(4 * nuno301.Unknown2);
|
||||
reader.Seek(8 * nuno301.Unknown3);
|
||||
|
||||
return nuno301;
|
||||
}
|
||||
|
||||
public void ParseNUNOSection0304(FileReader reader, uint version)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
76
File_Format_Library/FileFormats/HyruleWarriors/G1M/NUNV.cs
Normal file
76
File_Format_Library/FileFormats/HyruleWarriors/G1M/NUNV.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.IO;
|
||||
using OpenTK;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
public class NUNV
|
||||
{
|
||||
public List<NUNO.NUNOType0303Struct> NUNV0303StructList = new List<NUNO.NUNOType0303Struct>();
|
||||
|
||||
public NUNV(FileReader reader, uint version)
|
||||
{
|
||||
uint numChunks = reader.ReadUInt32();
|
||||
for (int i = 0; i < numChunks; i++)
|
||||
{
|
||||
long pos = reader.Position;
|
||||
uint subchunkMagic = reader.ReadUInt32();
|
||||
uint subchunkSize = reader.ReadUInt32();
|
||||
uint subChunkCount = reader.ReadUInt32();
|
||||
for (int j = 0; j < subChunkCount; j++)
|
||||
{
|
||||
switch (subchunkMagic)
|
||||
{
|
||||
case 0x00050001:
|
||||
NUNV0303StructList.Add(ParseNUVOSection0501(reader, version));
|
||||
break;
|
||||
default:
|
||||
Console.WriteLine("Unknown chunk! " + subchunkMagic.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.SeekBegin(pos + subchunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
public NUNO.NUNOType0303Struct ParseNUVOSection0501(FileReader reader, uint version)
|
||||
{
|
||||
var nuno501 = new NUNO.NUNOType0303Struct();
|
||||
nuno501.Unknown = reader.ReadInt32();
|
||||
uint numControlPoints = reader.ReadUInt32();
|
||||
uint unkCount = reader.ReadUInt32();
|
||||
nuno501.Unknown2 = reader.ReadInt32();
|
||||
nuno501.BoneParentID = reader.ReadInt32();
|
||||
|
||||
reader.ReadBytes(0x50);
|
||||
if (version >= 0x30303131)
|
||||
reader.ReadBytes(0x10);
|
||||
|
||||
nuno501.Influences = new NUNO.NUNOInfluence[(int)numControlPoints];
|
||||
nuno501.Points = new Vector4[(int)numControlPoints];
|
||||
for (int i = 0; i < numControlPoints; i++)
|
||||
nuno501.Points[i] = reader.ReadVec4();
|
||||
|
||||
for (int i = 0; i < numControlPoints; i++)
|
||||
{
|
||||
var influence = new NUNO.NUNOInfluence();
|
||||
influence.P1 = reader.ReadInt32();
|
||||
influence.P2 = reader.ReadInt32();
|
||||
influence.P3 = reader.ReadInt32();
|
||||
influence.P4 = reader.ReadInt32();
|
||||
influence.P5 = reader.ReadSingle();
|
||||
influence.P6 = reader.ReadSingle();
|
||||
nuno501.Influences[i] = influence;
|
||||
}
|
||||
|
||||
reader.Seek(48 * unkCount);
|
||||
reader.Seek(4 * nuno501.Unknown2);
|
||||
|
||||
return nuno501;
|
||||
}
|
||||
}
|
||||
}
|
@ -63,6 +63,8 @@ namespace FirstPlugin
|
||||
G1TFile.Read(new FileReader(stream));
|
||||
for (int i = 0; i < G1TFile.Textures.Count; i++)
|
||||
Nodes.Add(G1TFile.Textures[i]);
|
||||
|
||||
PluginRuntime.G1TextureContainers.Add(this);
|
||||
}
|
||||
|
||||
public void Save(System.IO.Stream stream)
|
||||
@ -73,7 +75,7 @@ namespace FirstPlugin
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
|
||||
PluginRuntime.G1TextureContainers.Remove(this);
|
||||
}
|
||||
|
||||
public virtual ToolStripItem[] GetContextMenuItems()
|
||||
|
@ -7,6 +7,7 @@ using Toolbox;
|
||||
using System.Windows.Forms;
|
||||
using Toolbox.Library;
|
||||
using Toolbox.Library.IO;
|
||||
using HyruleWarriors.G1M;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
@ -28,7 +29,13 @@ namespace FirstPlugin
|
||||
|
||||
public bool Identify(System.IO.Stream stream)
|
||||
{
|
||||
return (FileName.Contains(".bin.gz"));
|
||||
using (var reader = new FileReader(stream, true))
|
||||
{
|
||||
bool isTexture = reader.CheckSignature(4, "G1TG") || reader.CheckSignature(4, "GT1G");
|
||||
bool isModel = reader.CheckSignature(4, "_M1G") || reader.CheckSignature(4, "G1M_");
|
||||
|
||||
return (FileName.Contains(".bin.gz")) && !isTexture && !isModel;
|
||||
}
|
||||
}
|
||||
|
||||
public Type[] Types
|
||||
@ -63,6 +70,20 @@ namespace FirstPlugin
|
||||
|
||||
public bool isBigEndian = true;
|
||||
|
||||
|
||||
public override void OnAfterAdded()
|
||||
{
|
||||
var editor = LibraryGUI.GetObjectEditor();
|
||||
foreach (var file in files)
|
||||
{
|
||||
if (file.FileFormat == null)
|
||||
continue;
|
||||
|
||||
if (file.FileFormat is G1M)
|
||||
editor.SelectNode((G1M)file.FileFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public void Load(Stream stream)
|
||||
{
|
||||
CanSave = true;
|
||||
@ -94,16 +115,21 @@ namespace FirstPlugin
|
||||
reader.SeekBegin(Offsets[i]);
|
||||
fileEntry.FileData = reader.ReadBytes((int)Sizes[i]);
|
||||
fileEntry.FileName = $"File {i}";
|
||||
fileEntry.OpenFileFormatOnLoad = true;
|
||||
|
||||
switch (Magic)
|
||||
{
|
||||
case "GT1G": //Textures
|
||||
case "G1TG": //Textures
|
||||
G1T GITextureU = new G1T();
|
||||
GITextureU.FileName = $"TextureContainer{i}.gt1";
|
||||
GITextureU.Load(new MemoryStream(fileEntry.FileData));
|
||||
Nodes.Add(GITextureU);
|
||||
fileEntry.FileFormat = GITextureU;
|
||||
fileEntry.FileName = $"TextureContainer_{i}.gt1";
|
||||
break;
|
||||
case "_M1G":
|
||||
case "G1M_":
|
||||
fileEntry.FileName = $"Model_{i}.g1m";
|
||||
break;
|
||||
case "_A1G":
|
||||
case "G1A_":
|
||||
fileEntry.FileName = $"Animation_{i}.g1a";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -477,18 +477,9 @@ namespace FirstPlugin
|
||||
boneNode.RotationType = STBone.BoneRotationType.Euler;
|
||||
boneNode.Checked = true;
|
||||
boneNode.Text = Node.Name;
|
||||
boneNode.position = new float[3];
|
||||
boneNode.scale = new float[3];
|
||||
boneNode.rotation = new float[4];
|
||||
boneNode.position[0] = Node.Translation.X;
|
||||
boneNode.position[1] = Node.Translation.Y;
|
||||
boneNode.position[2] = Node.Translation.Z;
|
||||
boneNode.rotation[0] = Node.Rotation.X;
|
||||
boneNode.rotation[1] = Node.Rotation.Y;
|
||||
boneNode.rotation[2] = Node.Rotation.Z;
|
||||
boneNode.scale[0] = Node.Scale.X;
|
||||
boneNode.scale[1] = Node.Scale.Y;
|
||||
boneNode.scale[2] = Node.Scale.Z;
|
||||
boneNode.Position = Node.Translation;
|
||||
boneNode.EulerRotation = Node.Rotation;
|
||||
boneNode.Scale = Node.Scale;
|
||||
|
||||
if (Node.IsBone)
|
||||
Skeleton.bones.Add(boneNode);
|
||||
|
@ -479,18 +479,9 @@ namespace FirstPlugin
|
||||
boneNode.RotationType = STBone.BoneRotationType.Euler;
|
||||
boneNode.Checked = true;
|
||||
boneNode.Text = Node.Name;
|
||||
boneNode.position = new float[3];
|
||||
boneNode.scale = new float[3];
|
||||
boneNode.rotation = new float[4];
|
||||
boneNode.position[0] = Node.Translation.X;
|
||||
boneNode.position[1] = Node.Translation.Y;
|
||||
boneNode.position[2] = Node.Translation.Z;
|
||||
boneNode.rotation[0] = Node.Rotation.X;
|
||||
boneNode.rotation[1] = Node.Rotation.Y;
|
||||
boneNode.rotation[2] = Node.Rotation.Z;
|
||||
boneNode.scale[0] = Node.Scale.X;
|
||||
boneNode.scale[1] = Node.Scale.Y;
|
||||
boneNode.scale[2] = Node.Scale.Z;
|
||||
boneNode.Position = Node.Translation;
|
||||
boneNode.EulerRotation = Node.Rotation;
|
||||
boneNode.Scale = Node.Scale;
|
||||
|
||||
if (Node.IsBone)
|
||||
Skeleton.bones.Add(boneNode);
|
||||
|
56
File_Format_Library/FileFormats/Message/MSYT.cs
Normal file
56
File_Format_Library/FileFormats/Message/MSYT.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpYaml.Serialization;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public class MSYT
|
||||
{
|
||||
public static string ToYaml(MSBT msbt)
|
||||
{
|
||||
var header = MessageHeader.FromMSBT(msbt);
|
||||
var serializer = new Serializer();
|
||||
return serializer.Serialize(header);
|
||||
}
|
||||
|
||||
public class MessageHeader
|
||||
{
|
||||
public List<MessageEntry> entries = new List<MessageEntry>();
|
||||
|
||||
public static MessageHeader FromMSBT(MSBT msbt)
|
||||
{
|
||||
MessageHeader header = new MessageHeader();
|
||||
for (int i = 0; i < msbt.header.Label1.Labels.Count; i++) {
|
||||
var entry = msbt.header.Label1.Labels[i];
|
||||
|
||||
var msgEntry = new MessageEntry();
|
||||
msgEntry.Name = entry.Name;
|
||||
msgEntry.contents.text.Add(entry.String.GetText(msbt.header.StringEncoding));
|
||||
header.entries.Add(msgEntry);
|
||||
}
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
public class MessageEntry
|
||||
{
|
||||
public Content contents = new Content();
|
||||
|
||||
[YamlIgnore]
|
||||
public string Name { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
public class Content
|
||||
{
|
||||
public List<string> text = new List<string>();
|
||||
}
|
||||
}
|
||||
}
|
@ -296,11 +296,12 @@ namespace FirstPlugin.LuigisMansion.DarkMoon
|
||||
if (NLG_Common.HashNames.ContainsKey(HashID))
|
||||
bone.Text = NLG_Common.HashNames[HashID];
|
||||
|
||||
bone.position = new float[3] { Position.X, Position.Z, -Position.Y };
|
||||
bone.rotation = new float[4] { Rotate.X, Rotate.Z, -Rotate.Y, 1 };
|
||||
bone.scale = new float[3] { 0.2f, 0.2f, 0.2f };
|
||||
|
||||
bone.Position = Position;
|
||||
bone.EulerRotation = Rotate;
|
||||
bone.Scale = new OpenTK.Vector3(0.2f, 0.2f, 0.2f);
|
||||
bone.RotationType = STBone.BoneRotationType.Euler;
|
||||
|
||||
currentModel.Skeleton.bones.Add(bone);
|
||||
}
|
||||
|
||||
|
@ -611,11 +611,9 @@ namespace FirstPlugin.NLG
|
||||
if (HashList.ContainsKey(bone.HashID))
|
||||
stBone.Text = HashList[bone.HashID];
|
||||
|
||||
stBone.position = new float[3] { bone.Translate.X, bone.Translate.Z, -bone.Translate.Y };
|
||||
stBone.rotation = new float[4] { bone.Rotate.X, bone.Rotate.Z, -bone.Rotate.Y, 1 };
|
||||
|
||||
// stBone.scale = new float[3] { bone.Scale.X, bone.Scale.Y, bone.Scale.Z };
|
||||
stBone.scale = new float[3] { 0.2f, 0.2f, 0.2f };
|
||||
stBone.Position = bone.Translate;
|
||||
stBone.EulerRotation = bone.Rotate;
|
||||
stBone.Scale = new Vector3(0.2f, 0.2f, 0.2f);
|
||||
|
||||
stBone.RotationType = STBone.BoneRotationType.Euler;
|
||||
}
|
||||
|
@ -214,32 +214,31 @@ namespace FirstPlugin.PunchOutWii
|
||||
reader.ReadUInt32(); //unk
|
||||
reader.ReadUInt32(); //unk
|
||||
reader.ReadSingle(); //0
|
||||
|
||||
STBone bone = new STBone(Skeleton);
|
||||
var Scale = new OpenTK.Vector3(
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle());
|
||||
reader.ReadSingle(); //0
|
||||
var Rotate = new OpenTK.Vector3(
|
||||
bone.EulerRotation = new OpenTK.Vector3(
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle());
|
||||
reader.ReadSingle(); //0
|
||||
var Position = new OpenTK.Vector3(
|
||||
bone.Position = new OpenTK.Vector3(
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle(),
|
||||
reader.ReadSingle());
|
||||
reader.ReadSingle(); //1
|
||||
|
||||
STBone bone = new STBone(Skeleton);
|
||||
bone.Text = HashID.ToString("X");
|
||||
if (NLG_Common.HashNames.ContainsKey(HashID))
|
||||
bone.Text = NLG_Common.HashNames[HashID];
|
||||
else
|
||||
Console.WriteLine($"bone hash {HashID}");
|
||||
|
||||
bone.position = new float[3] { Position.X, Position.Z, -Position.Y };
|
||||
bone.rotation = new float[4] { Rotate.X, Rotate.Z, -Rotate.Y, 1 };
|
||||
bone.scale = new float[3] { 0.2f, 0.2f, 0.2f };
|
||||
bone.Scale = new Vector3(0.2f, 0.2f, 0.2f);
|
||||
|
||||
bone.RotationType = STBone.BoneRotationType.Euler;
|
||||
Skeleton.bones.Add(bone);
|
||||
|
@ -199,24 +199,9 @@ namespace FirstPlugin
|
||||
{
|
||||
STBone bone = new STBone(Skeleton);
|
||||
bone.parentIndex = Joints[i].ParentIndex;
|
||||
bone.position = new float[]
|
||||
{
|
||||
Joints[i].Position.X,
|
||||
Joints[i].Position.Y,
|
||||
Joints[i].Position.Z,
|
||||
};
|
||||
bone.scale = new float[]
|
||||
{
|
||||
Joints[i].Scale.X,
|
||||
Joints[i].Scale.Y,
|
||||
Joints[i].Scale.Z,
|
||||
};
|
||||
bone.rotation = new float[]
|
||||
{
|
||||
Joints[i].Rotation.X,
|
||||
Joints[i].Rotation.Y,
|
||||
Joints[i].Rotation.Z,
|
||||
};
|
||||
bone.Position = Joints[i].Position;
|
||||
bone.EulerRotation = Joints[i].Rotation;
|
||||
bone.Scale = Joints[i].Scale;
|
||||
Skeleton.bones.Add(bone);
|
||||
}
|
||||
Skeleton.reset();
|
||||
|
@ -225,7 +225,7 @@ namespace FirstPlugin
|
||||
}
|
||||
else
|
||||
{
|
||||
b.rot = EulerToQuat(b.rotation[2], b.rotation[1], b.rotation[0]);
|
||||
b.rot = EulerToQuat(b.EulerRotation.Z, b.EulerRotation.Y, b.EulerRotation.X);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -370,9 +370,9 @@ namespace FirstPlugin
|
||||
List<int> SkinningIndices = new List<int>();
|
||||
foreach (var genericBone in skeleton.bones)
|
||||
{
|
||||
var scale = genericBone.scale;
|
||||
var trans = genericBone.position;
|
||||
var rot = genericBone.rotation;
|
||||
var scale = genericBone.Scale;
|
||||
var trans = genericBone.Position;
|
||||
var rot = genericBone.EulerRotation;
|
||||
|
||||
Bone bone = new Bone();
|
||||
bone.Name = genericBone.Text;
|
||||
@ -380,9 +380,9 @@ namespace FirstPlugin
|
||||
bone.Parent = genericBone.parentIndex;
|
||||
bone.Zero = 0;
|
||||
bone.Visible = false;
|
||||
bone.Scale = new GFMDLStructs.Vector3(scale[0], scale[1], scale[2]);
|
||||
bone.Rotation = new GFMDLStructs.Vector3(rot[0], rot[1], rot[2]);
|
||||
bone.Translation = new GFMDLStructs.Vector3(trans[0], trans[1], trans[2]);
|
||||
bone.Scale = new GFMDLStructs.Vector3(scale.X, scale.Y, scale.Z);
|
||||
bone.Rotation = new GFMDLStructs.Vector3(rot.X, rot.Y, rot.Z);
|
||||
bone.Translation = new GFMDLStructs.Vector3(trans.X, trans.Y, trans.Z);
|
||||
bone.RadiusStart = new GFMDLStructs.Vector3(0, 0, 0);
|
||||
bone.RadiusEnd = new GFMDLStructs.Vector3(0, 0, 0);
|
||||
|
||||
@ -524,7 +524,7 @@ namespace FirstPlugin
|
||||
|
||||
for (int j = 0; j < mesh.vertices[i].boneNames?.Count; j++)
|
||||
{
|
||||
string boneName = mesh.vertices[i].boneNames[j];
|
||||
string boneName = mesh.vertices[i].boneNames[j] ;
|
||||
int boneIndex = Model.Model.Bones.IndexOf(Model.Model.Bones.Where(p => p.Name == boneName).FirstOrDefault());
|
||||
|
||||
if (boneIndex != -1 && skinningIndices.IndexOf(boneIndex) != -1)
|
||||
|
@ -356,7 +356,10 @@ namespace FirstPlugin
|
||||
matTexture.Type = STGenericMatTexture.TextureType.Normal;
|
||||
break;
|
||||
case "AmbientTex":
|
||||
if (SwitchParams.ContainsKey("AmbientMapEnable")) {
|
||||
if (SwitchParams["AmbientMapEnable"].Value)
|
||||
matTexture.Type = STGenericMatTexture.TextureType.AO;
|
||||
}
|
||||
break;
|
||||
case "LightTblTex":
|
||||
break;
|
||||
@ -441,30 +444,29 @@ namespace FirstPlugin
|
||||
|
||||
if (bone.Translation != null)
|
||||
{
|
||||
position = new float[3]
|
||||
{ bone.Translation.X,
|
||||
Position = new OpenTK.Vector3(
|
||||
bone.Translation.X,
|
||||
bone.Translation.Y,
|
||||
bone.Translation.Z
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
if (bone.Rotation != null)
|
||||
{
|
||||
rotation = new float[4]
|
||||
{ bone.Rotation.X,
|
||||
EulerRotation = new OpenTK.Vector3(
|
||||
bone.Rotation.X,
|
||||
bone.Rotation.Y,
|
||||
bone.Rotation.Z,
|
||||
1.0f,
|
||||
};
|
||||
bone.Rotation.Z
|
||||
);
|
||||
}
|
||||
|
||||
if (bone.Scale != null)
|
||||
{
|
||||
scale = new float[3]
|
||||
{ bone.Scale.X,
|
||||
Scale = new OpenTK.Vector3(
|
||||
bone.Scale.X,
|
||||
bone.Scale.Y,
|
||||
bone.Scale.Z
|
||||
};
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -229,8 +229,12 @@
|
||||
<Compile Include="FileFormats\BCH\Wrappers\SkeletalAnimation\H3DSkeletalAnimWrapper.cs" />
|
||||
<Compile Include="FileFormats\Byaml\XmlByamlConverter.cs" />
|
||||
<Compile Include="FileFormats\Byaml\YamlByamlConverter.cs" />
|
||||
<Compile Include="FileFormats\HyruleWarriors\G1M\G1MCommon.cs" />
|
||||
<Compile Include="FileFormats\HyruleWarriors\G1M\NUNO.cs" />
|
||||
<Compile Include="FileFormats\HyruleWarriors\G1M\NUNV.cs" />
|
||||
<Compile Include="FileFormats\LM1\LM1_MDL.cs" />
|
||||
<Compile Include="FileFormats\MarioParty\HSF.cs" />
|
||||
<Compile Include="FileFormats\Message\MSYT.cs" />
|
||||
<Compile Include="FileFormats\MKAGPDX\LM2_ARCADE_Model.cs" />
|
||||
<Compile Include="FileFormats\NLG\LM2\LM2_Material.cs" />
|
||||
<Compile Include="FileFormats\NLG\LM3\LM3_ChunkTable.cs" />
|
||||
@ -318,7 +322,6 @@
|
||||
<Compile Include="FileFormats\Effects\PTCL_3DS.cs" />
|
||||
<Compile Include="FileFormats\Effects\PTCL_WiiU.cs" />
|
||||
<Compile Include="FileFormats\Font\BFFNT.cs" />
|
||||
<Compile Include="FileFormats\Font\BFOTF.cs" />
|
||||
<Compile Include="FileFormats\Audio\Archives\BFGRP.cs" />
|
||||
<Compile Include="FileFormats\Font\BffntCharSet2Xlor.cs" />
|
||||
<Compile Include="FileFormats\Font\BFTTF.cs" />
|
||||
@ -397,6 +400,7 @@
|
||||
<Compile Include="GL\BFRES\BFRESRenderBase.cs" />
|
||||
<Compile Include="GL\BMD_Renderer.cs" />
|
||||
<Compile Include="GL\CMB_Renderer.cs" />
|
||||
<Compile Include="GL\G1M_Renderer.cs" />
|
||||
<Compile Include="GL\GXToOpenGL.cs" />
|
||||
<Compile Include="GL\KCL_Render.cs" />
|
||||
<Compile Include="GL\LM2_Render.cs" />
|
||||
|
67
File_Format_Library/GL/G1M_Renderer.cs
Normal file
67
File_Format_Library/GL/G1M_Renderer.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Toolbox.Library.Rendering;
|
||||
using GL_EditorFramework.GL_Core;
|
||||
using GL_EditorFramework.Interfaces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics.OpenGL;
|
||||
using Toolbox.Library;
|
||||
using FirstPlugin;
|
||||
|
||||
namespace HyruleWarriors.G1M
|
||||
{
|
||||
public class G1M_Renderer : GenericModelRenderer
|
||||
{
|
||||
public G1M G1MFile { get; set; }
|
||||
|
||||
public List<ushort> SkinningIndices { get; set; }
|
||||
|
||||
public override void OnRender(GLControl control)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void SetRenderData(STGenericMaterial mat, ShaderProgram shader, STGenericObject m)
|
||||
{
|
||||
shader.SetBoolToInt("NoSkinning", Skeleton.bones.Count == 0);
|
||||
|
||||
if (mat.Text == "driver_cloth")
|
||||
{
|
||||
GL.Disable(EnableCap.CullFace);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.Enable(EnableCap.CullFace);
|
||||
}
|
||||
}
|
||||
|
||||
public override int BindTexture(STGenericMatTexture tex, ShaderProgram shader)
|
||||
{
|
||||
GL.ActiveTexture(TextureUnit.Texture0 + tex.textureUnit + 1);
|
||||
GL.BindTexture(TextureTarget.Texture2D, RenderTools.defaultTex.RenderableTex.TexID);
|
||||
|
||||
string activeTex = tex.Name;
|
||||
|
||||
foreach (var texContainer in PluginRuntime.G1TextureContainers)
|
||||
{
|
||||
if (G1MFile.IFileInfo.ArchiveParent != texContainer.IFileInfo.ArchiveParent)
|
||||
continue;
|
||||
|
||||
var textureList = texContainer.TextureList;
|
||||
foreach (var texture in texContainer.TextureList)
|
||||
{
|
||||
if (textureList.IndexOf(texture) == ((G1MTextureMap)tex).TextureIndex)
|
||||
{
|
||||
BindGLTexture(tex, shader, textureList[((G1MTextureMap)tex).TextureIndex]);
|
||||
return tex.textureUnit + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tex.textureUnit + 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -131,45 +131,61 @@ namespace FirstPlugin
|
||||
|
||||
private void SetBoneTransform(BfresBone bn)
|
||||
{
|
||||
bn.position[0] = (float)posXUD.Value;
|
||||
bn.position[1] = (float)posYUD.Value;
|
||||
bn.position[2] = (float)posZUD.Value;
|
||||
bn.Position = new OpenTK.Vector3(
|
||||
(float)posXUD.Value,
|
||||
(float)posYUD.Value,
|
||||
(float)posZUD.Value
|
||||
);
|
||||
|
||||
bn.rotation[0] = (float)RotXUD.Value;
|
||||
bn.rotation[1] = (float)RotYUD.Value;
|
||||
bn.rotation[2] = (float)RotZUD.Value;
|
||||
bn.rotation[3] = (float)RotWUD.Value;
|
||||
|
||||
bn.scale[0] = (float)ScaXUD.Value;
|
||||
bn.scale[1] = (float)ScaYUD.Value;
|
||||
bn.scale[2] = (float)ScaZUD.Value;
|
||||
|
||||
if (bn.BoneU != null)
|
||||
if ((BoneFlagsRotation)rotModeCB.SelectedItem == BoneFlagsRotation.Quaternion)
|
||||
{
|
||||
bn.BoneU.Position = new Syroot.Maths.Vector3F(bn.position[0], bn.position[1], bn.position[2]);
|
||||
bn.BoneU.Rotation = new Syroot.Maths.Vector4F(bn.rotation[0], bn.rotation[1], bn.rotation[2], bn.rotation[3]);
|
||||
bn.BoneU.Scale = new Syroot.Maths.Vector3F(bn.scale[0], bn.scale[1], bn.scale[2]);
|
||||
bn.Rotation = new OpenTK.Quaternion(
|
||||
(float)RotXUD.Value,
|
||||
(float)RotYUD.Value,
|
||||
(float)RotZUD.Value,
|
||||
(float)RotWUD.Value
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
bn.Bone.Position = new Syroot.Maths.Vector3F(bn.position[0], bn.position[1], bn.position[2]);
|
||||
bn.Bone.Rotation = new Syroot.Maths.Vector4F(bn.rotation[0], bn.rotation[1], bn.rotation[2], bn.rotation[3]);
|
||||
bn.Bone.Scale = new Syroot.Maths.Vector3F(bn.scale[0], bn.scale[1], bn.scale[2]);
|
||||
bn.EulerRotation = new OpenTK.Vector3(
|
||||
(float)RotXUD.Value,
|
||||
(float)RotYUD.Value,
|
||||
(float)RotZUD.Value
|
||||
);
|
||||
}
|
||||
|
||||
bn.Scale = new OpenTK.Vector3(
|
||||
(float)ScaXUD.Value,
|
||||
(float)ScaYUD.Value,
|
||||
(float)ScaZUD.Value
|
||||
);
|
||||
|
||||
bn.GenericToBfresBone();
|
||||
}
|
||||
|
||||
private void GetBoneTransform(STBone bn)
|
||||
{
|
||||
posXUD.Value = (decimal)bn.position[0];
|
||||
posYUD.Value = (decimal)bn.position[1];
|
||||
posZUD.Value = (decimal)bn.position[2];
|
||||
RotXUD.Value = (decimal)bn.rotation[0];
|
||||
RotYUD.Value = (decimal)bn.rotation[1];
|
||||
RotZUD.Value = (decimal)bn.rotation[2];
|
||||
RotWUD.Value = (decimal)bn.rotation[3];
|
||||
ScaXUD.Value = (decimal)bn.scale[0];
|
||||
ScaYUD.Value = (decimal)bn.scale[1];
|
||||
ScaZUD.Value = (decimal)bn.scale[2];
|
||||
posXUD.Value = (decimal)bn.Position.X;
|
||||
posYUD.Value = (decimal)bn.Position.Y;
|
||||
posZUD.Value = (decimal)bn.Position.Z;
|
||||
if (bn.RotationType == STBone.BoneRotationType.Quaternion) {
|
||||
RotXUD.Value = (decimal)bn.Rotation.X;
|
||||
RotYUD.Value = (decimal)bn.Rotation.Y;
|
||||
RotZUD.Value = (decimal)bn.Rotation.Z;
|
||||
RotWUD.Value = (decimal)bn.Rotation.W;
|
||||
}
|
||||
else
|
||||
{
|
||||
RotXUD.Value = (decimal)bn.EulerRotation.X;
|
||||
RotYUD.Value = (decimal)bn.EulerRotation.Y;
|
||||
RotZUD.Value = (decimal)bn.EulerRotation.Z;
|
||||
RotWUD.Value = (decimal)1.0f;
|
||||
}
|
||||
|
||||
ScaXUD.Value = (decimal)bn.Scale.X;
|
||||
ScaYUD.Value = (decimal)bn.Scale.Y;
|
||||
ScaZUD.Value = (decimal)bn.Scale.Z;
|
||||
}
|
||||
|
||||
private void rotMeasureCB_SelectedIndexChanged(object sender, EventArgs e)
|
||||
@ -206,17 +222,7 @@ namespace FirstPlugin
|
||||
if (activeBone == null || !IsLoaded)
|
||||
return;
|
||||
|
||||
if ((BoneFlagsRotation)rotModeCB.SelectedItem == BoneFlagsRotation.Quaternion)
|
||||
{
|
||||
activeBone.ConvertToQuaternion();
|
||||
SetBoneTransform(activeBone);
|
||||
}
|
||||
else
|
||||
{
|
||||
activeBone.ConvertToEular();
|
||||
SetBoneTransform(activeBone);
|
||||
}
|
||||
|
||||
LibraryGUI.UpdateViewport();
|
||||
}
|
||||
|
||||
|
@ -406,14 +406,13 @@ namespace FirstPlugin
|
||||
Formats.Add(typeof(GCDisk));
|
||||
Formats.Add(typeof(TPL));
|
||||
Formats.Add(typeof(BFTTF));
|
||||
Formats.Add(typeof(BFOTF));
|
||||
Formats.Add(typeof(HedgehogLibrary.PACx));
|
||||
Formats.Add(typeof(BinGzArchive));
|
||||
Formats.Add(typeof(GAR));
|
||||
Formats.Add(typeof(CTXB));
|
||||
Formats.Add(typeof(CSAB));
|
||||
Formats.Add(typeof(CMB));
|
||||
Formats.Add(typeof(G1T));
|
||||
Formats.Add(typeof(HyruleWarriors.G1M.G1M));
|
||||
Formats.Add(typeof(LayoutBXLYT.Cafe.BFLYT));
|
||||
Formats.Add(typeof(LayoutBXLYT.BCLYT));
|
||||
Formats.Add(typeof(LayoutBXLYT.BRLYT));
|
||||
@ -449,6 +448,7 @@ namespace FirstPlugin
|
||||
Formats.Add(typeof(CtrLibrary.BCH));
|
||||
Formats.Add(typeof(LZS));
|
||||
Formats.Add(typeof(WTA));
|
||||
Formats.Add(typeof(BinGzArchive));
|
||||
|
||||
|
||||
|
||||
@ -458,10 +458,8 @@ namespace FirstPlugin
|
||||
//Unfinished wip formats not ready for use
|
||||
if (Runtime.DEVELOPER_DEBUG_MODE)
|
||||
{
|
||||
Formats.Add(typeof(XLINK));
|
||||
Formats.Add(typeof(BFSAR));
|
||||
Formats.Add(typeof(GFA));
|
||||
Formats.Add(typeof(HyruleWarriors.G1M.G1M));
|
||||
}
|
||||
|
||||
|
||||
|
@ -30,6 +30,8 @@ namespace FirstPlugin
|
||||
public static List<CtrLibrary.BCHGroupNode> bchTexContainers = new List<CtrLibrary.BCHGroupNode>();
|
||||
public static List<NLG.StrikersRLT.TextureEntry> stikersTextures = new List<NLG.StrikersRLT.TextureEntry>();
|
||||
|
||||
public static List<G1T> G1TextureContainers = new List<G1T>();
|
||||
|
||||
public static string ExternalFMATPath = "";
|
||||
public static string OdysseyGamePath = "";
|
||||
public static string Mk8GamePath = "";
|
||||
|
@ -1,314 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Aampv1 = AampV1Library;
|
||||
using Aampv2 = AampV2Library;
|
||||
using System.IO;
|
||||
using Syroot.Maths;
|
||||
using SharpYaml;
|
||||
using SharpYaml.Events;
|
||||
using SharpYaml.Serialization;
|
||||
using SharpYaml.Serialization.Serializers;
|
||||
|
||||
namespace FirstPlugin
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string Indent(this string value, int size)
|
||||
{
|
||||
var strArray = value.Split('\n');
|
||||
var sb = new StringBuilder();
|
||||
foreach (var s in strArray)
|
||||
sb.Append(new string(' ', size)).Append(s);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class AampYamlConverter
|
||||
{
|
||||
public class YamlAamp
|
||||
{
|
||||
public const string Identifier = "!aamp";
|
||||
|
||||
public string version { get; set; }
|
||||
}
|
||||
|
||||
#region V1 AAMP
|
||||
|
||||
public static string ToYaml(Aampv1.AampFile aampFile)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
using (TextWriter writer = new StringWriter(sb))
|
||||
{
|
||||
writer.WriteLine("!aamp");
|
||||
writer.WriteLine($"version: {aampFile.Version}");
|
||||
writer.WriteLine("!io");
|
||||
writer.WriteLine($"version: 0");
|
||||
writer.WriteLine($"type: {aampFile.EffectType}");
|
||||
WriteParamList(writer, aampFile.RootNode, 0);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void WriteParamList(TextWriter writer, Aampv1.ParamList paramList, int IndentAmount)
|
||||
{
|
||||
writer.WriteLine($"{paramList.HashString}: !list".Indent(IndentAmount));
|
||||
|
||||
if (paramList.paramObjects.Length <= 0)
|
||||
writer.WriteLine("objects: {}".Indent(IndentAmount + 2));
|
||||
else
|
||||
writer.WriteLine("objects: ".Indent(IndentAmount + 2));
|
||||
|
||||
foreach (var paramObj in paramList.paramObjects)
|
||||
{
|
||||
WriteParamObject(writer, paramObj, IndentAmount + 4);
|
||||
}
|
||||
|
||||
if (paramList.childParams.Length <= 0)
|
||||
writer.WriteLine("lists: {}".Indent(IndentAmount + 2));
|
||||
else
|
||||
writer.WriteLine("lists: ".Indent(IndentAmount + 2));
|
||||
|
||||
foreach (var child in paramList.childParams)
|
||||
{
|
||||
WriteParamList(writer, child, IndentAmount + 4);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteParamObject(TextWriter writer, Aampv1.ParamObject paramObj, int IndentAmount)
|
||||
{
|
||||
writer.WriteLine($"{paramObj.HashString} : !obj".Indent(IndentAmount));
|
||||
foreach (var entry in paramObj.paramEntries)
|
||||
{
|
||||
writer.WriteLine($"{entry.HashString}: {WriteParamData(entry)}".Indent(IndentAmount + 2));
|
||||
}
|
||||
}
|
||||
|
||||
private static string WriteParamData(Aampv1.ParamEntry entry)
|
||||
{
|
||||
switch (entry.ParamType)
|
||||
{
|
||||
case Aampv1.ParamType.Boolean: return $"{(bool)entry.Value}";
|
||||
case Aampv1.ParamType.BufferBinary: return $"!BufferBinary [ {WriteBytes((byte[])entry.Value)} ]";
|
||||
case Aampv1.ParamType.BufferFloat: return $"!BufferFloat [ {WriteFloats((float[])entry.Value)} ]";
|
||||
case Aampv1.ParamType.BufferInt: return $"BufferInt [ {WriteInts((int[])entry.Value)} ]";
|
||||
case Aampv1.ParamType.BufferUint: return $"!BufferUint [ {WriteUints((uint[])entry.Value)} ]";
|
||||
case Aampv1.ParamType.Color4F: return $"{WriteColor4F((Vector4F)entry.Value)}";
|
||||
case Aampv1.ParamType.Vector2F: return $"{WriteVec2F((Vector2F)entry.Value)}";
|
||||
case Aampv1.ParamType.Vector3F: return $"{WriteVec3F((Vector3F)entry.Value)}";
|
||||
case Aampv1.ParamType.Vector4F: return $"{WriteVec4F((Vector4F)entry.Value)}";
|
||||
case Aampv1.ParamType.Uint: return $"{(uint)entry.Value}";
|
||||
case Aampv1.ParamType.Int: return $"{(int)entry.Value}";
|
||||
case Aampv1.ParamType.Float: return $"{(float)entry.Value}";
|
||||
case Aampv1.ParamType.String256: return $"!str256 {WriteStringEntry((AampCommon.StringEntry)entry.Value)}";
|
||||
case Aampv1.ParamType.String32: return $"!str32 {WriteStringEntry((AampCommon.StringEntry)entry.Value)}";
|
||||
case Aampv1.ParamType.String64: return $"!str64 {WriteStringEntry((AampCommon.StringEntry)entry.Value)}";
|
||||
case Aampv1.ParamType.StringRef: return $"!strRef {WriteStringEntry((AampCommon.StringEntry)entry.Value)}";
|
||||
case Aampv1.ParamType.Curve1: return $"{WriteCurve((Aampv1.Curve[])entry.Value, 1)}";
|
||||
case Aampv1.ParamType.Curve2: return $"{WriteCurve((Aampv1.Curve[])entry.Value, 2)}";
|
||||
case Aampv1.ParamType.Curve3: return $"{WriteCurve((Aampv1.Curve[])entry.Value, 3)}";
|
||||
case Aampv1.ParamType.Curve4: return $"{WriteCurve((Aampv1.Curve[])entry.Value, 4)}";
|
||||
default:
|
||||
throw new Exception("Unsupported type! " + entry.ParamType);
|
||||
}
|
||||
}
|
||||
|
||||
private static string WriteStringEntry(AampCommon.StringEntry value)
|
||||
{
|
||||
return BytesToStringConverted(value.Data).Replace(" ", string.Empty);
|
||||
// return Encoding.Default.GetString(value.Data).Replace(" ", string.Empty);
|
||||
}
|
||||
|
||||
static string BytesToStringConverted(byte[] bytes)
|
||||
{
|
||||
using (var stream = new MemoryStream(bytes))
|
||||
{
|
||||
using (var reader = new Toolbox.Library.IO.FileReader(stream))
|
||||
{
|
||||
return reader.ReadZeroTerminatedString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static string WriteCurve(Aampv1.Curve[] curves, int Num)
|
||||
{
|
||||
string curveStr = "";
|
||||
foreach (var curve in curves)
|
||||
curveStr += $"!curve{Num}[{WriteUints(curve.valueUints)}] [{WriteFloats(curve.valueFloats)}] \n";
|
||||
|
||||
return curveStr;
|
||||
}
|
||||
|
||||
//I could've used a yaml parser, but incase i need to change it up to look nicer and support leo's aamp layout, do it manually
|
||||
public static void ToAamp(Aampv1.AampFile aampFile, string text)
|
||||
{
|
||||
byte[] TextData = Encoding.Unicode.GetBytes(text);
|
||||
StreamReader t = new StreamReader(new MemoryStream(TextData), Encoding.GetEncoding(932));
|
||||
|
||||
var yaml = new YamlStream();
|
||||
yaml.Load(new StringReader(text));
|
||||
|
||||
/* var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
|
||||
foreach (var item in mapping.AllNodes)
|
||||
{
|
||||
Console.WriteLine("n " + item);
|
||||
}*/
|
||||
|
||||
/* byte[] TextData = Encoding.Unicode.GetBytes(text);
|
||||
StreamReader t = new StreamReader(new MemoryStream(TextData), Encoding.GetEncoding(932));
|
||||
using (var reader = new StringReader(t.ReadToEnd()))
|
||||
{
|
||||
string AampCheck = reader.ReadLine();
|
||||
if (AampCheck != "!aamp")
|
||||
throw new Exception($"Expected !aamp got {AampCheck} at line 0");
|
||||
string VersionCheck = reader.ReadLine();
|
||||
string num = GetProperty(VersionCheck);
|
||||
if (num == "1")
|
||||
{
|
||||
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
public static void ParseList(StringReader reader)
|
||||
{
|
||||
}
|
||||
|
||||
public static string GetProperty(string line)
|
||||
{
|
||||
if (line.Contains(":"))
|
||||
return line.Split(':')[1].Replace(string.Empty, "");
|
||||
return line;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region V2 AAMP
|
||||
|
||||
public static string ToYaml(Aampv2.AampFile aampFile)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
using (TextWriter writer = new StringWriter(sb))
|
||||
{
|
||||
writer.WriteLine("!aamp");
|
||||
writer.WriteLine($"version: {aampFile.Version}");
|
||||
writer.WriteLine("!io");
|
||||
writer.WriteLine($"version: {aampFile.ParameterIOVersion}");
|
||||
writer.WriteLine($"type: {aampFile.ParameterIOType}");
|
||||
WriteParamList(writer, aampFile.RootNode, 0);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void WriteParamList(TextWriter writer, Aampv2.ParamList paramList, int IndentAmount)
|
||||
{
|
||||
writer.WriteLine($"{paramList.HashString}: !list".Indent(IndentAmount));
|
||||
|
||||
if (paramList.paramObjects.Length <= 0)
|
||||
writer.WriteLine("objects: {}".Indent(IndentAmount + 2));
|
||||
else
|
||||
writer.WriteLine("objects: ".Indent(IndentAmount + 2));
|
||||
|
||||
foreach (var paramObj in paramList.paramObjects)
|
||||
{
|
||||
WriteParamObject(writer, paramObj, IndentAmount + 4);
|
||||
}
|
||||
|
||||
if (paramList.childParams.Length <= 0)
|
||||
writer.WriteLine("lists: {}".Indent(IndentAmount + 2));
|
||||
else
|
||||
writer.WriteLine("lists: ".Indent(IndentAmount + 2));
|
||||
|
||||
foreach (var child in paramList.childParams)
|
||||
{
|
||||
WriteParamList(writer, child, IndentAmount + 4);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteParamObject(TextWriter writer, Aampv2.ParamObject paramObj, int IndentAmount)
|
||||
{
|
||||
writer.WriteLine($"{paramObj.HashString} : !obj".Indent(IndentAmount));
|
||||
foreach (var entry in paramObj.paramEntries)
|
||||
{
|
||||
writer.WriteLine($"{WriteParamData(entry)}".Indent(IndentAmount + 2));
|
||||
}
|
||||
}
|
||||
|
||||
private static string WriteParamData(Aampv2.ParamEntry entry)
|
||||
{
|
||||
switch (entry.ParamType)
|
||||
{
|
||||
case Aampv2.ParamType.Boolean: return $"{entry.HashString}: {(bool)entry.Value}";
|
||||
case Aampv2.ParamType.BufferBinary: return $"{entry.HashString}: !BufferBinary [ {WriteBytes((byte[])entry.Value)} ]";
|
||||
case Aampv2.ParamType.BufferFloat: return $"{entry.HashString}: !BufferFloat [ {WriteFloats((float[])entry.Value)} ]";
|
||||
case Aampv2.ParamType.BufferInt: return $"{entry.HashString}: !BufferInt [ {WriteInts((int[])entry.Value)} ]";
|
||||
case Aampv2.ParamType.BufferUint: return $"{entry.HashString}: !BufferUint [ {WriteUints((uint[])entry.Value)} ]";
|
||||
case Aampv2.ParamType.Quat: return $"{entry.HashString}: !BufferUint [ {WriteFloats((float[])entry.Value)} ]";
|
||||
case Aampv2.ParamType.Color4F: return $"{entry.HashString}: {WriteColor4F((Vector4F)entry.Value)}";
|
||||
case Aampv2.ParamType.Vector2F: return $"{entry.HashString}: {WriteVec2F((Vector2F)entry.Value)}";
|
||||
case Aampv2.ParamType.Vector3F: return $"{entry.HashString}: {WriteVec3F((Vector3F)entry.Value)}";
|
||||
case Aampv2.ParamType.Vector4F: return $"{entry.HashString}: {WriteVec4F((Vector4F)entry.Value)}";
|
||||
case Aampv2.ParamType.Uint: return $"{entry.HashString}: {(uint)entry.Value}";
|
||||
case Aampv2.ParamType.Int: return $"{entry.HashString}: {(int)entry.Value}";
|
||||
case Aampv2.ParamType.Float: return $"{entry.HashString}: {(float)entry.Value}";
|
||||
case Aampv2.ParamType.String256: return $"{entry.HashString}: !str256 {((AampCommon.StringEntry)entry.Value).ToString()}";
|
||||
case Aampv2.ParamType.String32: return $"{entry.HashString}: !str32 {((AampCommon.StringEntry)entry.Value).ToString()}";
|
||||
case Aampv2.ParamType.String64: return $"{entry.HashString}: !str64 {((AampCommon.StringEntry)entry.Value).ToString()}";
|
||||
case Aampv2.ParamType.StringRef: return $"{entry.HashString}: !strRef {((AampCommon.StringEntry)entry.Value).ToString()}";
|
||||
case Aampv2.ParamType.Curve1: return $"{entry.HashString}: {WriteCurve((Aampv2.Curve[])entry.Value, 1)}";
|
||||
case Aampv2.ParamType.Curve2: return $"{entry.HashString}: {WriteCurve((Aampv2.Curve[])entry.Value, 2)}";
|
||||
case Aampv2.ParamType.Curve3: return $"{entry.HashString}: {WriteCurve((Aampv2.Curve[])entry.Value, 3)}";
|
||||
case Aampv2.ParamType.Curve4: return $"{entry.HashString}: {WriteCurve((Aampv2.Curve[])entry.Value, 4)}";
|
||||
default:
|
||||
throw new Exception("Unsupported type! " + entry.ParamType);
|
||||
}
|
||||
}
|
||||
|
||||
private static string WriteCurve(Aampv2.Curve[] curves, int Num)
|
||||
{
|
||||
string curveStr = "";
|
||||
foreach (var curve in curves)
|
||||
curveStr += $"!curve{Num}[{WriteUints(curve.valueUints)}] [{WriteFloats(curve.valueFloats)}] \n";
|
||||
|
||||
return curveStr;
|
||||
}
|
||||
|
||||
public static Aampv2.AampFile ToAamp(string FileName)
|
||||
{
|
||||
var aampFile = new Aampv2.AampFile();
|
||||
|
||||
return aampFile;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static string WriteUints(uint[] arr) {
|
||||
return String.Join(",", arr.Select(p => p.ToString()).ToArray());
|
||||
}
|
||||
|
||||
private static string WriteFloats(float[] arr) {
|
||||
return String.Join(",", arr.Select(p => p.ToString()).ToArray());
|
||||
}
|
||||
|
||||
private static string WriteInts(int[] arr) {
|
||||
return String.Join(",", arr.Select(p => p.ToString()).ToArray());
|
||||
}
|
||||
|
||||
private static string WriteBytes(byte[] arr) {
|
||||
return String.Join(",", arr.Select(p => p.ToString()).ToArray());
|
||||
}
|
||||
|
||||
private static string WriteVec2F(Vector2F vec2) { return $"!vec2[{vec2.X}, {vec2.Y}]"; }
|
||||
private static string WriteVec3F(Vector3F vec3) { return $"!vec3[{vec3.X}, {vec3.Y}, {vec3.Z}]"; }
|
||||
private static string WriteVec4F(Vector4F vec4) { return $"!vec4[{vec4.X}, {vec4.Y}, {vec4.Z}, {vec4.W}]"; }
|
||||
private static string WriteColor4F(Vector4F vec4) { return $"!color[{vec4.X}, {vec4.Y}, {vec4.Z}, {vec4.W}]"; }
|
||||
|
||||
}
|
||||
}
|
@ -202,30 +202,6 @@ namespace Toolbox.Library.Animations
|
||||
|
||||
return sca;
|
||||
}
|
||||
|
||||
public void SetKeyFromBone(float frame, STBone bone)
|
||||
{
|
||||
Vector3 rot = ANIM.quattoeul(bone.rot);
|
||||
if (rot.X != bone.rotation[0] || rot.Y != bone.rotation[1] || rot.Z != bone.rotation[2])
|
||||
{
|
||||
XROT.GetKeyFrame(frame).Value = bone.rot.X;
|
||||
YROT.GetKeyFrame(frame).Value = bone.rot.Y;
|
||||
ZROT.GetKeyFrame(frame).Value = bone.rot.Z;
|
||||
WROT.GetKeyFrame(frame).Value = bone.rot.W;
|
||||
}
|
||||
if (bone.pos.X != bone.position[0] || bone.pos.Y != bone.position[1] || bone.pos.Z != bone.position[2])
|
||||
{
|
||||
XPOS.GetKeyFrame(frame).Value = bone.pos.X;
|
||||
YPOS.GetKeyFrame(frame).Value = bone.pos.Y;
|
||||
ZPOS.GetKeyFrame(frame).Value = bone.pos.Z;
|
||||
}
|
||||
if (bone.sca.X != bone.scale[0] || bone.sca.Y != bone.scale[1] || bone.sca.Z != bone.scale[2])
|
||||
{
|
||||
XSCA.GetKeyFrame(frame).Value = bone.sca.X;
|
||||
YSCA.GetKeyFrame(frame).Value = bone.sca.Y;
|
||||
ZSCA.GetKeyFrame(frame).Value = bone.sca.Z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReplaceMe(Animation a)
|
||||
@ -291,29 +267,22 @@ namespace Toolbox.Library.Animations
|
||||
|
||||
public KeyFrame GetKeyFrame(float frame, bool InsertNewKey = true)
|
||||
{
|
||||
KeyFrame key = null;
|
||||
int i;
|
||||
for (i = 0; i < Keys.Count; i++)
|
||||
if (Keys.Count == 0) return null;
|
||||
KeyFrame k1 = (KeyFrame)Keys[0], k2 = (KeyFrame)Keys[0];
|
||||
foreach (KeyFrame k in Keys)
|
||||
{
|
||||
if (Keys[i].Frame == frame)
|
||||
if (k.Frame < frame)
|
||||
{
|
||||
key = Keys[i];
|
||||
break;
|
||||
k1 = k;
|
||||
}
|
||||
if (Keys[i].Frame > frame)
|
||||
else
|
||||
{
|
||||
k2 = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (key == null && InsertNewKey)
|
||||
{
|
||||
key = new KeyFrame();
|
||||
key.Frame = frame;
|
||||
Keys.Insert(i, key);
|
||||
}
|
||||
|
||||
return key;
|
||||
return k1;
|
||||
}
|
||||
|
||||
public bool SetValue(float Value, int frame)
|
||||
@ -330,36 +299,6 @@ namespace Toolbox.Library.Animations
|
||||
return false;
|
||||
}
|
||||
|
||||
public KeyFrame GetLeft(int frame)
|
||||
{
|
||||
KeyFrame prev = Keys[0];
|
||||
|
||||
for (int i = 0; i < Keys.Count - 1; i++)
|
||||
{
|
||||
KeyFrame key = Keys[i];
|
||||
if (key.Frame > frame && prev.Frame <= frame)
|
||||
break;
|
||||
prev = key;
|
||||
}
|
||||
|
||||
return prev;
|
||||
}
|
||||
public KeyFrame GetRight(int frame)
|
||||
{
|
||||
KeyFrame cur = Keys[0];
|
||||
KeyFrame prev = Keys[0];
|
||||
|
||||
for (int i = 1; i < Keys.Count; i++)
|
||||
{
|
||||
KeyFrame key = Keys[i];
|
||||
cur = key;
|
||||
if (key.Frame > frame && prev.Frame <= frame)
|
||||
break;
|
||||
prev = key;
|
||||
}
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
int LastFound = 0;
|
||||
float LastFrame;
|
||||
@ -368,70 +307,51 @@ namespace Toolbox.Library.Animations
|
||||
if (Keys.Count == 0)
|
||||
return 0;
|
||||
|
||||
KeyFrame k1 = (KeyFrame)Keys[0], k2 = (KeyFrame)Keys[0];
|
||||
int i = 0;
|
||||
if (frame < LastFrame)
|
||||
LastFound = 0;
|
||||
for (i = LastFound; i < Keys.Count; i++)
|
||||
float startFrame = Keys.First().Frame;
|
||||
|
||||
KeyFrame LK = Keys.First();
|
||||
KeyFrame RK = Keys.Last();
|
||||
|
||||
foreach (KeyFrame keyFrame in Keys)
|
||||
{
|
||||
LastFound = i % (Keys.Count);
|
||||
KeyFrame k = Keys[LastFound];
|
||||
if (k.Frame < frame)
|
||||
if (keyFrame.Frame <= frame) LK = keyFrame;
|
||||
if (keyFrame.Frame >= frame && keyFrame.Frame < RK.Frame) RK = keyFrame;
|
||||
}
|
||||
|
||||
if (LK.Frame != RK.Frame)
|
||||
{
|
||||
k1 = k;
|
||||
}
|
||||
else
|
||||
// float FrameDiff = frame - LK.Frame;
|
||||
// float Weight = 1.0f / (RK.Frame - LK.Frame);
|
||||
// float Weight = FrameDiff / (RK.Frame - LK.Frame);
|
||||
|
||||
float FrameDiff = frame - LK.Frame;
|
||||
float Weight = FrameDiff / (RK.Frame - LK.Frame);
|
||||
|
||||
Console.WriteLine($"frame diff {FrameDiff} frame {frame} LK {LK.Frame} RK {RK} ratio {Weight}");
|
||||
|
||||
switch (InterpolationType)
|
||||
{
|
||||
k2 = k;
|
||||
break;
|
||||
case InterpolationType.CONSTANT: return LK.Value;
|
||||
case InterpolationType.STEP: return LK.Value;
|
||||
case InterpolationType.LINEAR: return InterpolationHelper.Lerp(LK.Value, RK.Value, Weight);
|
||||
case InterpolationType.HERMITE:
|
||||
// return InterpolationHelper.GetCubicValue(Weight, LK.Value, LK.Slope1, LK.Slope2, LK.Delta);
|
||||
// return CubicEval(LK.Value, LK.Slope1, LK.Slope2, LK.Delta, ratio);
|
||||
float length = RK.Frame - LK.Frame;
|
||||
|
||||
float val = InterpolationHelper.HermiteInterpolate(frame,
|
||||
LK.Frame,
|
||||
RK.Frame,
|
||||
RK.Slope1,
|
||||
LK.Slope2,
|
||||
LK.Value,
|
||||
RK.Value);
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
LastFound -= 1;
|
||||
if (LastFound < 0)
|
||||
LastFound = 0;
|
||||
if (LastFound >= Keys.Count - 2)
|
||||
LastFound = 0;
|
||||
LastFrame = frame;
|
||||
|
||||
if (k1.InterType == InterpolationType.CONSTANT)
|
||||
return k1.Value;
|
||||
if (k1.InterType == InterpolationType.STEP)
|
||||
return k1.Value;
|
||||
if (k1.InterType == InterpolationType.LINEAR)
|
||||
{
|
||||
return Lerp(k1.Value, k2.Value, k1.Frame, k2.Frame, frame);
|
||||
return LK.Value;
|
||||
}
|
||||
if (k1.InterType == InterpolationType.HERMITE)
|
||||
{
|
||||
float val = Hermite(frame, k1.Frame, k2.Frame, k1.In, k1.Out != -1 ? k1.Out : k2.In, k1.Value, k2.Value) * (k1.Degrees ? (float)Math.PI / 180 : 1);
|
||||
if (float.IsNaN(val)) val = k1._value;
|
||||
|
||||
|
||||
if (frame == k1.Frame) return k1.Value;
|
||||
if (frame == k2.Frame) return k2.Value;
|
||||
|
||||
float distance = frame - k1.Frame;
|
||||
float invDuration = 1f / (k2.Frame - k1.Frame);
|
||||
float t = distance * invDuration;
|
||||
|
||||
float p0 = k1.Value;
|
||||
float p1 = k2.Value;
|
||||
float s0 = k1.Out * distance;
|
||||
float s1 = k2.In * distance;
|
||||
float cf0 = (p0 * 2) + (p1 * -2) + (s0 * 1) + (s1 * 1);
|
||||
float cf1 = (p0 * -3) + (p1 * 3) + (s0 * -2) + (s1 * -1);
|
||||
float cf2 = (p0 * 0) + (p1 * 0) + (s0 * 1) + (s1 * 0);
|
||||
float cf3 = (p0 * 1) + (p1 * 0) + (s0 * 0) + (s1 * 0);
|
||||
|
||||
return val;//k1.Out != -1 ? k1.Out :
|
||||
|
||||
return CubicEval(cf0, cf1, cf2, cf3, t);
|
||||
}
|
||||
|
||||
return k1.Value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public KeyFrame[] GetFrame(float frame)
|
||||
{
|
||||
@ -596,9 +516,9 @@ namespace Toolbox.Library.Animations
|
||||
else
|
||||
if (node.RotType == RotationType.EULER)
|
||||
{
|
||||
float x = node.XROT.HasAnimation() ? node.XROT.GetValue(Frame) : b.rotation[0];
|
||||
float y = node.YROT.HasAnimation() ? node.YROT.GetValue(Frame) : b.rotation[1];
|
||||
float z = node.ZROT.HasAnimation() ? node.ZROT.GetValue(Frame) : b.rotation[2];
|
||||
float x = node.XROT.HasAnimation() ? node.XROT.GetValue(Frame) : b.EulerRotation.X;
|
||||
float y = node.YROT.HasAnimation() ? node.YROT.GetValue(Frame) : b.EulerRotation.Y;
|
||||
float z = node.ZROT.HasAnimation() ? node.ZROT.GetValue(Frame) : b.EulerRotation.Z;
|
||||
b.rot = EulerToQuat(z, y, x);
|
||||
}
|
||||
}
|
||||
@ -644,32 +564,13 @@ namespace Toolbox.Library.Animations
|
||||
|
||||
public static float CubicEval(float cf0, float cf1, float cf2, float cf3, float t)
|
||||
{
|
||||
return (((cf0 * t + cf1) * t + cf2) * t + cf3);
|
||||
return ((cf3 * t + cf2) * t) * t + (cf1 * t + cf0);
|
||||
|
||||
// return (((cf0 * t + cf1) * t + cf2) * t + cf3);
|
||||
}
|
||||
|
||||
public static float Hermite(float frame, float frame1, float frame2, float outslope, float inslope, float val1, float val2)
|
||||
public static float Hermite(float frame, float frame1, float frame2, float outSlope, float inSlope, float val1, float val2)
|
||||
{
|
||||
/*float offset = frame - frame1;
|
||||
float span = frame2 - frame1;
|
||||
if (offset == 0) return val1;
|
||||
if (offset == span) return val2;
|
||||
float diff = val2 - val1;
|
||||
float time = offset / span;
|
||||
|
||||
//bool prevDouble = prevframe1 >= 0 && prevframe1 == frame1 - 1;
|
||||
//bool nextDouble = next._next._index >= 0 && next._next._index == next._index + 1;
|
||||
bool oneApart = frame2 == frame1 + 1;
|
||||
|
||||
float tan = outslope, nextTan = inslope;
|
||||
if (oneApart)
|
||||
tan = (val2 - val1) / (frame2 - frame1);
|
||||
//if (oneApart)
|
||||
nextTan = (val2 - val1) / (frame2 - frame1);
|
||||
float inv = time - 1.0f; //-1 to 0
|
||||
return val1
|
||||
+ (offset * inv * ((inv * tan) + (time * nextTan)))
|
||||
+ ((time * time) * (3.0f - 2.0f * time) * diff);*/
|
||||
|
||||
if (frame == frame1) return val1;
|
||||
if (frame == frame2) return val2;
|
||||
|
||||
@ -677,7 +578,7 @@ namespace Toolbox.Library.Animations
|
||||
float invDuration = 1f / (frame2 - frame1);
|
||||
float t = distance * invDuration;
|
||||
float t1 = t - 1f;
|
||||
return (val1 + ((((val1 - val2) * ((2f * t) - 3f)) * t) * t)) + ((distance * t1) * ((t1 * outslope) + (t * inslope)));
|
||||
return (val1 + ((((val1 - val2) * ((2f * t) - 3f)) * t) * t)) + ((distance * t1) * ((t1 * outSlope) + (t * inSlope)));
|
||||
}
|
||||
|
||||
public static float Lerp(float av, float bv, float v0, float v1, float t)
|
||||
|
@ -36,17 +36,17 @@ namespace Toolbox.Library.Animations
|
||||
|
||||
if (seanim.AnimType == AnimationType.Relative)
|
||||
{
|
||||
PositionX = genericBone.position[0];
|
||||
PositionY = genericBone.position[1];
|
||||
PositionZ = genericBone.position[2];
|
||||
PositionX = genericBone.Position.X;
|
||||
PositionY = genericBone.Position.Y;
|
||||
PositionZ = genericBone.Position.Z;
|
||||
|
||||
RotationX = genericBone.rotation[0];
|
||||
RotationY = genericBone.rotation[1];
|
||||
RotationZ = genericBone.rotation[2];
|
||||
RotationX = genericBone.EulerRotation.X;
|
||||
RotationY = genericBone.EulerRotation.Y;
|
||||
RotationZ = genericBone.EulerRotation.Z;
|
||||
|
||||
ScaleX = genericBone.scale[0];
|
||||
ScaleY = genericBone.scale[1];
|
||||
ScaleZ = genericBone.scale[2];
|
||||
ScaleX = genericBone.Scale.X;
|
||||
ScaleY = genericBone.Scale.Y;
|
||||
ScaleZ = genericBone.Scale.Z;
|
||||
}
|
||||
|
||||
System.Console.WriteLine(bone);
|
||||
|
@ -68,18 +68,15 @@ namespace Toolbox.Library.Animations
|
||||
if (time == 0)
|
||||
{
|
||||
STBone b = BoneList[int.Parse(args[0])];
|
||||
b.position = new float[3];
|
||||
b.rotation = new float[3];
|
||||
b.scale = new float[3];
|
||||
b.position[0] = float.Parse(args[1]);
|
||||
b.position[1] = float.Parse(args[2]);
|
||||
b.position[2] = float.Parse(args[3]);
|
||||
b.rotation[0] = float.Parse(args[4]);
|
||||
b.rotation[1] = float.Parse(args[5]);
|
||||
b.rotation[2] = float.Parse(args[6]);
|
||||
b.scale[0] = 1f;
|
||||
b.scale[1] = 1f;
|
||||
b.scale[2] = 1f;
|
||||
b.Position = new Vector3(
|
||||
float.Parse(args[1]),
|
||||
float.Parse(args[2]),
|
||||
float.Parse(args[3]));
|
||||
b.EulerRotation = new Vector3(
|
||||
float.Parse(args[4]),
|
||||
float.Parse(args[5]),
|
||||
float.Parse(args[6]));
|
||||
b.Scale = Vector3.One;
|
||||
|
||||
b.pos = new Vector3(float.Parse(args[1]), float.Parse(args[2]), float.Parse(args[3]));
|
||||
b.rot = STSkeleton.FromEulerAngles(float.Parse(args[6]), float.Parse(args[5]), float.Parse(args[4]));
|
||||
@ -113,7 +110,13 @@ namespace Toolbox.Library.Animations
|
||||
for (int i = 0; i < Bones.bones.Count; i++)
|
||||
{
|
||||
STBone b = Bones.bones[i];
|
||||
o.AppendFormat("{0} {1} {2} {3} {4} {5} {6}\n", i, b.position[0], b.position[1], b.position[2], b.rotation[0], b.rotation[1], b.rotation[2]);
|
||||
o.AppendFormat("{0} {1} {2} {3} {4} {5} {6}\n", i,
|
||||
b.Position.X,
|
||||
b.Position.Y,
|
||||
b.Position.Z,
|
||||
b.EulerRotation.X,
|
||||
b.EulerRotation.Y,
|
||||
b.EulerRotation.Z);
|
||||
}
|
||||
o.AppendLine("end");
|
||||
}
|
||||
@ -182,18 +185,15 @@ namespace Toolbox.Library.Animations
|
||||
if (readBones && frame == 0)
|
||||
{
|
||||
STBone b = vbn.bones[int.Parse(args[0])];
|
||||
b.position = new float[3];
|
||||
b.rotation = new float[3];
|
||||
b.scale = new float[3];
|
||||
b.position[0] = float.Parse(args[1]);
|
||||
b.position[1] = float.Parse(args[2]);
|
||||
b.position[2] = float.Parse(args[3]);
|
||||
b.rotation[0] = float.Parse(args[4]);
|
||||
b.rotation[1] = float.Parse(args[5]);
|
||||
b.rotation[2] = float.Parse(args[6]);
|
||||
b.scale[0] = 1f;
|
||||
b.scale[1] = 1f;
|
||||
b.scale[2] = 1f;
|
||||
b.Position = new Vector3(
|
||||
float.Parse(args[1]),
|
||||
float.Parse(args[2]),
|
||||
float.Parse(args[3]));
|
||||
b.EulerRotation = new Vector3(
|
||||
float.Parse(args[4]),
|
||||
float.Parse(args[5]),
|
||||
float.Parse(args[6]));
|
||||
b.Scale = Vector3.One;
|
||||
|
||||
b.pos = new Vector3(float.Parse(args[1]), float.Parse(args[2]), float.Parse(args[3]));
|
||||
b.rot = STSkeleton.FromEulerAngles(float.Parse(args[6]), float.Parse(args[5]), float.Parse(args[4]));
|
||||
|
@ -73,7 +73,6 @@ namespace Toolbox.Library
|
||||
|
||||
|
||||
scene = Importer.ImportFile(FileName, settings.GetFlags());
|
||||
|
||||
if (Utils.GetExtension(FileName) == ".dae")
|
||||
GetRealNodeNames(FileName);
|
||||
|
||||
@ -220,16 +219,28 @@ namespace Toolbox.Library
|
||||
|
||||
if (scene.RootNode != null)
|
||||
{
|
||||
var idenity = Matrix4x4.Identity;
|
||||
var rootTransform = scene.RootNode.Transform;
|
||||
Matrix4 transformMat = AssimpHelper.TKMatrix(rootTransform);
|
||||
|
||||
var scale = transformMat.ExtractScale();
|
||||
var rotation = transformMat.ExtractRotation();
|
||||
var position = transformMat.ExtractTranslation();
|
||||
|
||||
STConsole.WriteLine($"-".Repeat(30));
|
||||
STConsole.WriteLine($"rootTransform {transformMat}");
|
||||
STConsole.WriteLine($"scale {scale}");
|
||||
STConsole.WriteLine($"rotation {rotation}");
|
||||
STConsole.WriteLine($"position {position}");
|
||||
STConsole.WriteLine($"-".Repeat(30));
|
||||
|
||||
var SklRoot = GetSklRoot(scene.RootNode, BoneNames);
|
||||
if (SklRoot != null)
|
||||
{
|
||||
BuildSkeletonNodes(SklRoot, BoneNames, skeleton, ref idenity);
|
||||
BuildSkeletonNodes(SklRoot, BoneNames, skeleton, ref rootTransform);
|
||||
}
|
||||
else
|
||||
{
|
||||
BuildSkeletonNodes(scene.RootNode, BoneNames, skeleton, ref idenity);
|
||||
BuildSkeletonNodes(scene.RootNode, BoneNames, skeleton, ref rootTransform);
|
||||
}
|
||||
|
||||
skeleton.update();
|
||||
@ -513,12 +524,12 @@ namespace Toolbox.Library
|
||||
if (IsBone)
|
||||
{
|
||||
var idenity = Matrix4x4.Identity;
|
||||
CreateByNode(node, skeleton, ParentArmatureName, SmoothIndex, RigidIndex, true, ref idenity);
|
||||
CreateByNode(node, skeleton, ParentArmatureName, SmoothIndex, RigidIndex, true, ref rootTransform);
|
||||
}
|
||||
else if (IsRootSkeleton && node.HasChildren)
|
||||
{
|
||||
var idenity = Matrix4x4.Identity;
|
||||
CreateByNode(node.Children[0], skeleton, ParentArmatureName, SmoothIndex, RigidIndex, true, ref idenity);
|
||||
CreateByNode(node.Children[0], skeleton, ParentArmatureName, SmoothIndex, RigidIndex, true, ref world);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -535,7 +546,7 @@ namespace Toolbox.Library
|
||||
short SmoothIndex, short RigidIndex, bool IsRoot, ref Assimp.Matrix4x4 rootTransform)
|
||||
{
|
||||
Matrix4x4 trafo = node.Transform;
|
||||
Matrix4x4 world = trafo;
|
||||
Matrix4x4 world = trafo * rootTransform;
|
||||
var transformMat = AssimpHelper.TKMatrix(world);
|
||||
|
||||
int matchedBoneIndex = skeleton.bones.FindIndex(item => item.Name == node.Name);
|
||||
@ -557,13 +568,6 @@ namespace Toolbox.Library
|
||||
bone.SmoothMatrixIndex = (short)skeleton.bones.IndexOf(bone);
|
||||
bone.RigidMatrixIndex = -1; //Todo calculate these
|
||||
|
||||
STConsole.WriteLine($"-".Repeat(30));
|
||||
STConsole.WriteLine($"Processing Bone {bone.Text}");
|
||||
STConsole.WriteLine($"SmoothMatrixIndex {bone.SmoothMatrixIndex}");
|
||||
STConsole.WriteLine($"RigidMatrixIndex {bone.RigidMatrixIndex}");
|
||||
STConsole.WriteLine($"Transform Matrix {transformMat}");
|
||||
STConsole.WriteLine($"-".Repeat(30));
|
||||
|
||||
if (IsRoot)
|
||||
{
|
||||
bone.parentIndex = -1;
|
||||
@ -584,19 +588,25 @@ namespace Toolbox.Library
|
||||
var rotation = transformMat.ExtractRotation();
|
||||
var position = transformMat.ExtractTranslation();
|
||||
|
||||
STConsole.WriteLine($"-".Repeat(30));
|
||||
STConsole.WriteLine($"Processing Bone {bone.Text}");
|
||||
STConsole.WriteLine($"scale {scale}");
|
||||
STConsole.WriteLine($"rotation {rotation}");
|
||||
STConsole.WriteLine($"position {position}");
|
||||
STConsole.WriteLine($"-".Repeat(30));
|
||||
|
||||
var rotEular = STMath.ToEulerAngles(rotation);
|
||||
|
||||
bone.position = new float[] { position.X, position.Y, position.Z };
|
||||
bone.scale = new float[] { scale.X, scale.Y, scale.Z };
|
||||
bone.rotation = new float[] { rotEular.X, rotEular.Y, rotEular.Z, 1 };
|
||||
bone.Transform = transformMat;
|
||||
}
|
||||
else
|
||||
{
|
||||
STConsole.WriteLine($"Duplicate node name found for bone {node.Name}!", Color.Red);
|
||||
}
|
||||
|
||||
var identity = Matrix4x4.Identity;
|
||||
foreach (Node child in node.Children)
|
||||
CreateByNode(child, skeleton, ParentArmatureName, SmoothIndex, RigidIndex, false, ref rootTransform);
|
||||
CreateByNode(child, skeleton, ParentArmatureName, SmoothIndex, RigidIndex, false, ref identity);
|
||||
}
|
||||
public STGenericObject CreateGenericObject(Node parentNode,Mesh msh, int Index, Matrix4 transform)
|
||||
{
|
||||
@ -623,6 +633,8 @@ namespace Toolbox.Library
|
||||
obj.HasTans = msh.HasTangentBasis;
|
||||
obj.HasBitans = msh.HasTangentBasis;
|
||||
obj.HasVertColors = msh.HasVertexColors(0);
|
||||
obj.HasVertColors2 = msh.HasVertexColors(1);
|
||||
|
||||
obj.ObjectName = msh.Name;
|
||||
if (parentNode != null && msh.Name == string.Empty)
|
||||
obj.ObjectName = parentNode.Name;
|
||||
@ -640,6 +652,18 @@ namespace Toolbox.Library
|
||||
obj.vertices = GetVertices(msh, transform, obj);
|
||||
obj.VertexBufferIndex = Index;
|
||||
|
||||
//Correct the vertex colors because assimp is broken.
|
||||
if (Geomerties.Count > Index)
|
||||
{
|
||||
Console.WriteLine($"v count {obj.vertices.Count}");
|
||||
Console.WriteLine($"color count {Geomerties[Index].ColorList.Count}");
|
||||
|
||||
for (int v = 0; v < Geomerties[Index].ColorList.Count; v++) {
|
||||
if (v < obj.vertices.Count)
|
||||
obj.vertices[v].col = Geomerties[Index].ColorList[v];
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -651,8 +675,7 @@ namespace Toolbox.Library
|
||||
{
|
||||
foreach (Face f in msh.Faces)
|
||||
{
|
||||
if (f.HasIndices)
|
||||
{
|
||||
if (f.HasIndices) {
|
||||
foreach (int indx in f.Indices)
|
||||
faces.Add(indx);
|
||||
}
|
||||
@ -710,6 +733,8 @@ namespace Toolbox.Library
|
||||
{
|
||||
Vertex vert = new Vertex();
|
||||
|
||||
Console.WriteLine($"{msh.Name} position {msh.Vertices[v]}");
|
||||
|
||||
if (msh.HasVertices)
|
||||
vert.pos = Vector3.TransformPosition(AssimpHelper.FromVector(msh.Vertices[v]), transform);
|
||||
if (msh.HasNormals)
|
||||
@ -722,10 +747,11 @@ namespace Toolbox.Library
|
||||
vert.uv2 = new Vector2(msh.TextureCoordinateChannels[2][v].X, msh.TextureCoordinateChannels[2][v].Y);
|
||||
if (msh.HasTangentBasis)
|
||||
vert.tan = new Vector4(msh.Tangents[v].X, msh.Tangents[v].Y, msh.Tangents[v].Z, 1);
|
||||
if (msh.HasVertexColors(0))
|
||||
{
|
||||
vert.col = new Vector4(msh.VertexColorChannels[0][v].R, msh.VertexColorChannels[0][v].G, msh.VertexColorChannels[0][v].B, msh.VertexColorChannels[0][v].A);
|
||||
}
|
||||
//Todo Assimp always reads vertex colors wrong!
|
||||
// if (msh.HasVertexColors(0))
|
||||
// vert.col = new Vector4(msh.VertexColorChannels[0][v].R, msh.VertexColorChannels[0][v].G, msh.VertexColorChannels[0][v].B, msh.VertexColorChannels[0][v].A);
|
||||
// if (msh.HasVertexColors(1))
|
||||
// vert.col2 = new Vector4(msh.VertexColorChannels[1][v].R, msh.VertexColorChannels[1][v].G, msh.VertexColorChannels[1][v].B, msh.VertexColorChannels[1][v].A);
|
||||
if (msh.HasTangentBasis)
|
||||
vert.bitan = new Vector4(msh.BiTangents[v].X, msh.BiTangents[v].Y, msh.BiTangents[v].Z, 1);
|
||||
vertices.Add(vert);
|
||||
|
@ -11,15 +11,8 @@ namespace Toolbox.Library
|
||||
{
|
||||
public static class AssimpHelper
|
||||
{
|
||||
public static Matrix4x4 GetBoneMatrix(STBone bone)
|
||||
{
|
||||
var pos = Matrix4x4.FromTranslation(new Vector3D(bone.position[0], bone.position[1], bone.position[2]));
|
||||
var rotx = Matrix4x4.FromRotationX(bone.rotation[0]);
|
||||
var roty = Matrix4x4.FromRotationY(bone.rotation[1]);
|
||||
var rotz = Matrix4x4.FromRotationZ(bone.rotation[2]);
|
||||
var sca = Matrix4x4.FromScaling(new Vector3D(bone.scale[0], bone.scale[1], bone.scale[2]));
|
||||
|
||||
return sca * (rotx * roty * rotz) * pos;
|
||||
public static Matrix4x4 GetBoneMatrix(STBone bone) {
|
||||
return AssimpFromTKMatrix(bone.Transform);
|
||||
}
|
||||
|
||||
public static string GetSaveFilter()
|
||||
@ -35,11 +28,6 @@ namespace Toolbox.Library
|
||||
"All files(*.*)|*.*";
|
||||
}
|
||||
|
||||
public static Syroot.Maths.Matrix3x4 CalculateInverseMatrix(STBone bone)
|
||||
{
|
||||
return FromAssimpMatrix(AssimpCalculateInverseMatrix(bone));
|
||||
}
|
||||
|
||||
public static Syroot.Maths.Matrix3x4 FromAssimpMatrix(Assimp.Matrix4x4 mat)
|
||||
{
|
||||
|
||||
@ -64,30 +52,6 @@ namespace Toolbox.Library
|
||||
return mat4;
|
||||
}
|
||||
|
||||
public static Assimp.Matrix4x4 AssimpCalculateInverseMatrix(STBone bone)
|
||||
{
|
||||
Assimp.Matrix4x4 transf;
|
||||
|
||||
//Get parent transform for a smooth matrix
|
||||
if (bone.Parent != null && bone.Parent is STBone)
|
||||
transf = AssimpCalculateInverseMatrix((STBone)bone.Parent);
|
||||
else
|
||||
transf = Assimp.Matrix4x4.Identity;
|
||||
|
||||
//Now calculate the matrix with TK matrices
|
||||
var trans = Assimp.Matrix4x4.FromTranslation(new Vector3D(bone.position[0], bone.position[1], bone.position[2]));
|
||||
var scale = Assimp.Matrix4x4.FromScaling(new Vector3D(bone.scale[0], bone.scale[1], bone.scale[2]));
|
||||
var rotX = Assimp.Matrix4x4.FromRotationX(bone.rotation[0]);
|
||||
var rotY = Assimp.Matrix4x4.FromRotationY(bone.rotation[1]);
|
||||
var rotZ = Assimp.Matrix4x4.FromRotationZ(bone.rotation[2]);
|
||||
|
||||
var result = scale * (rotX * rotY * rotZ) * trans;
|
||||
result.Inverse();
|
||||
|
||||
return transf;
|
||||
|
||||
}
|
||||
|
||||
public static Vector3 FromVector(Vector3D vec)
|
||||
{
|
||||
Vector3 v;
|
||||
|
@ -76,11 +76,19 @@ namespace Toolbox.Library
|
||||
bone.parentIndex = seBone.BoneParent;
|
||||
bone.RotationType = STBone.BoneRotationType.Euler;
|
||||
|
||||
Vector3 rotEular = ToEular(seBone.LocalRotation);
|
||||
|
||||
bone.position = new float[] { (float)seBone.LocalPosition.X, (float)seBone.LocalPosition.Y, (float)seBone.LocalPosition.Z };
|
||||
bone.scale = new float[] { (float)seBone.Scale.X, (float)seBone.Scale.Y, (float)seBone.Scale.Z };
|
||||
bone.rotation = new float[] { rotEular.X, rotEular.Y, rotEular.Z, 0 };
|
||||
bone.Position = new Vector3(
|
||||
(float)seBone.LocalPosition.X,
|
||||
(float)seBone.LocalPosition.Y,
|
||||
(float)seBone.LocalPosition.Z);
|
||||
bone.Scale = new Vector3(
|
||||
(float)seBone.Scale.X,
|
||||
(float)seBone.Scale.Y,
|
||||
(float)seBone.Scale.Z);
|
||||
bone.Rotation = new Quaternion(
|
||||
(float)seBone.LocalRotation.X,
|
||||
(float)seBone.LocalRotation.Y,
|
||||
(float)seBone.LocalRotation.Z,
|
||||
(float)seBone.LocalRotation.W);
|
||||
|
||||
return bone;
|
||||
}
|
||||
@ -208,31 +216,5 @@ namespace Toolbox.Library
|
||||
(float)(value.B / 255),
|
||||
(float)(value.A / 255));
|
||||
}
|
||||
|
||||
private static Vector3 ToEular(SELib.Utilities.Quaternion selibQuat)
|
||||
{
|
||||
OpenTK.Quaternion q = new Quaternion((float)selibQuat.X, (float)selibQuat.Y, (float)selibQuat.Z, (float)selibQuat.W);
|
||||
Matrix4 mat = Matrix4.CreateFromQuaternion(q);
|
||||
float x, y, z;
|
||||
y = (float)Math.Asin(Clamp(mat.M13, -1, 1));
|
||||
|
||||
if (Math.Abs(mat.M13) < 0.99999)
|
||||
{
|
||||
x = (float)Math.Atan2(-mat.M23, mat.M33);
|
||||
z = (float)Math.Atan2(-mat.M12, mat.M11);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = (float)Math.Atan2(mat.M32, mat.M22);
|
||||
z = 0;
|
||||
}
|
||||
return new Vector3(x, y, z) * -1;
|
||||
}
|
||||
private static float Clamp(float v, float min, float max)
|
||||
{
|
||||
if (v < min) return min;
|
||||
if (v > max) return max;
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,28 +29,91 @@ namespace Toolbox.Library
|
||||
public short RigidMatrixIndex;
|
||||
public short SmoothMatrixIndex;
|
||||
|
||||
public float[] position = new float[] { 0, 0, 0 };
|
||||
public float[] rotation = new float[] { 0, 0, 0 };
|
||||
public float[] scale = new float[] { 1, 1, 1 };
|
||||
private Matrix4 transform;
|
||||
private Quaternion rotation;
|
||||
private Vector3 eulerRotation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transformation of the bone.
|
||||
/// Setting this will adjust the
|
||||
/// <see cref="Scale"/>,
|
||||
/// <see cref="Rotation"/>, and
|
||||
/// <see cref="Position"/> properties.
|
||||
/// </summary>
|
||||
public Matrix4 Transform
|
||||
{
|
||||
set
|
||||
{
|
||||
// Scale = value.ExtractScale();
|
||||
// Rotation = value.ExtractRotation();
|
||||
// Position = value.ExtractTranslation();
|
||||
transform = value;
|
||||
}
|
||||
get
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the bone in world space.
|
||||
/// </summary>
|
||||
public Vector3 Position { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scale of the bone in world space.
|
||||
/// </summary>
|
||||
public Vector3 Scale { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rotation of the bone in world space.
|
||||
/// </summary>
|
||||
public Quaternion Rotation
|
||||
{
|
||||
get { return rotation; }
|
||||
set
|
||||
{
|
||||
if (RotationType == BoneRotationType.Euler)
|
||||
{
|
||||
eulerRotation = new Vector3(value.X, value.Y, value.Z);
|
||||
rotation = STMath.FromEulerAngles(eulerRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
eulerRotation = STMath.ToEulerAngles(Rotation);
|
||||
rotation = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Rotation"/> using euler method.
|
||||
/// </summary>
|
||||
public Vector3 EulerRotation
|
||||
{
|
||||
get { return eulerRotation; }
|
||||
set {
|
||||
eulerRotation = value;
|
||||
rotation = STMath.FromEulerAngles(value);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 pos = Vector3.Zero, sca = new Vector3(1f, 1f, 1f);
|
||||
public Quaternion rot = Quaternion.FromMatrix(Matrix3.Zero);
|
||||
public Matrix4 Transform, invert;
|
||||
|
||||
public Matrix4 invert;
|
||||
|
||||
public Matrix4 GetTransform()
|
||||
{
|
||||
Vector3 mPos = new Vector3(position[0], position[1],position[2]);
|
||||
return Matrix4.CreateScale(Scale) *
|
||||
Matrix4.CreateFromQuaternion(Rotation) *
|
||||
Matrix4.CreateTranslation(Position);
|
||||
}
|
||||
|
||||
Quaternion mRot;
|
||||
if (RotationType == STBone.BoneRotationType.Quaternion)
|
||||
mRot = (STSkeleton.FromQuaternionAngles(rotation[2], rotation[1],rotation[0], rotation[3]));
|
||||
else
|
||||
mRot = (STSkeleton.FromEulerAngles(rotation[2],rotation[1],rotation[0]));
|
||||
|
||||
Vector3 mSca = new Vector3(scale[0], scale[1],scale[2]);
|
||||
|
||||
return Matrix4.CreateScale(mSca) * Matrix4.CreateFromQuaternion(mRot) * Matrix4.CreateTranslation(mPos);
|
||||
public void FromTransform(Matrix4 transform)
|
||||
{
|
||||
Scale = transform.ExtractScale();
|
||||
Position = transform.ExtractTranslation();
|
||||
Rotation = transform.ExtractRotation();
|
||||
}
|
||||
|
||||
//Used for shifting models with the bones in the shader
|
||||
@ -71,38 +134,6 @@ namespace Toolbox.Library
|
||||
return sca;
|
||||
}
|
||||
|
||||
public void FromTransform(Matrix4 Transform)
|
||||
{
|
||||
var pos = Transform.ExtractTranslation();
|
||||
var quat = Transform.ExtractRotation();
|
||||
var scale = Transform.ExtractScale();
|
||||
|
||||
position[0] = pos.X;
|
||||
position[1] = pos.X;
|
||||
position[2] = pos.Z;
|
||||
|
||||
var eul = Toolbox.Library.Animations.ANIM.quattoeul(quat);
|
||||
rotation = new float[] { eul.X, eul.Y, eul.Z, 1 };
|
||||
|
||||
scale[0] = scale.X;
|
||||
scale[1] = scale.X;
|
||||
scale[2] = scale.Z;
|
||||
}
|
||||
|
||||
private void ApplyTransforms()
|
||||
{
|
||||
position = new float[] { pos.X, pos .Y, pos .Z};
|
||||
if (RotationType == BoneRotationType.Quaternion)
|
||||
rotation = new float[] { rot.X, rot.Y, rot.Z, rot.W };
|
||||
else
|
||||
{
|
||||
var eul = Toolbox.Library.Animations.ANIM.quattoeul(rot);
|
||||
rotation = new float[] { eul.X, eul.Y, eul.Z, 1 };
|
||||
}
|
||||
|
||||
scale = new float[] { sca.X, sca.Y, sca.Z };
|
||||
}
|
||||
|
||||
public int GetIndex()
|
||||
{
|
||||
if (skeletonParent != null)
|
||||
@ -111,34 +142,6 @@ namespace Toolbox.Library
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void ConvertToQuaternion()
|
||||
{
|
||||
if (RotationType == BoneRotationType.Quaternion)
|
||||
return;
|
||||
|
||||
RotationType = STBone.BoneRotationType.Quaternion;
|
||||
|
||||
ApplyTransforms();
|
||||
|
||||
//Update matrices
|
||||
skeletonParent.reset();
|
||||
skeletonParent.update();
|
||||
}
|
||||
|
||||
public void ConvertToEular()
|
||||
{
|
||||
if (RotationType == BoneRotationType.Euler)
|
||||
return;
|
||||
|
||||
RotationType = STBone.BoneRotationType.Euler;
|
||||
|
||||
ApplyTransforms();
|
||||
|
||||
//Update matrices
|
||||
skeletonParent.reset();
|
||||
skeletonParent.update();
|
||||
}
|
||||
|
||||
public override void OnClick(TreeView treeView)
|
||||
{
|
||||
|
||||
@ -183,9 +186,9 @@ namespace Toolbox.Library
|
||||
public STBone(STSkeleton skl)
|
||||
{
|
||||
skeletonParent = skl;
|
||||
|
||||
ImageKey = "bone";
|
||||
SelectedImageKey = "bone";
|
||||
|
||||
Checked = true;
|
||||
}
|
||||
|
||||
@ -193,6 +196,7 @@ namespace Toolbox.Library
|
||||
{
|
||||
ImageKey = "bone";
|
||||
SelectedImageKey = "bone";
|
||||
Checked = true;
|
||||
}
|
||||
|
||||
public void RenderLegacy()
|
||||
|
@ -287,6 +287,10 @@ namespace Toolbox.Library
|
||||
return bone;
|
||||
}
|
||||
|
||||
public Matrix4 GetBoneTransform(int index) {
|
||||
return GetBoneTransform(bones[index]);
|
||||
}
|
||||
|
||||
public Matrix4 GetBoneTransform(STBone Bone)
|
||||
{
|
||||
if (Bone == null)
|
||||
@ -314,17 +318,19 @@ namespace Toolbox.Library
|
||||
{
|
||||
for (int i = 0; i < bones.Count; i++)
|
||||
{
|
||||
bones[i].pos = new Vector3(bones[i].position[0], bones[i].position[1], bones[i].position[2]);
|
||||
|
||||
if (bones[i].RotationType == STBone.BoneRotationType.Quaternion)
|
||||
{
|
||||
bones[i].rot = (FromQuaternionAngles(bones[i].rotation[2], bones[i].rotation[1], bones[i].rotation[0], bones[i].rotation[3]));
|
||||
}
|
||||
else
|
||||
{
|
||||
bones[i].rot = (FromEulerAngles(bones[i].rotation[2], bones[i].rotation[1], bones[i].rotation[0]));
|
||||
}
|
||||
bones[i].sca = new Vector3(bones[i].scale[0], bones[i].scale[1], bones[i].scale[2]);
|
||||
bones[i].pos = new Vector3(
|
||||
bones[i].Position.X,
|
||||
bones[i].Position.Y,
|
||||
bones[i].Position.Z);
|
||||
bones[i].rot = new Quaternion(
|
||||
bones[i].Rotation.X,
|
||||
bones[i].Rotation.Y,
|
||||
bones[i].Rotation.Z,
|
||||
bones[i].Rotation.W);
|
||||
bones[i].sca = new Vector3(
|
||||
bones[i].Scale.X,
|
||||
bones[i].Scale.Y,
|
||||
bones[i].Scale.Z);
|
||||
}
|
||||
update(true);
|
||||
for (int i = 0; i < bones.Count; i++)
|
||||
|
@ -57,6 +57,7 @@ namespace Toolbox.Library.IO
|
||||
{
|
||||
if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
|
||||
Console.WriteLine("bytes to struct isBigEndian " + isBigEndian);
|
||||
AdjustBigEndianByteOrder(typeof(T), buffer, isBigEndian);
|
||||
|
||||
fixed (byte* pBuffer = buffer)
|
||||
@ -82,6 +83,8 @@ namespace Toolbox.Library.IO
|
||||
if (!isBigEndian)
|
||||
return;
|
||||
|
||||
Console.WriteLine("type " + type + " " + type.IsPrimitive);
|
||||
|
||||
if (type.IsPrimitive)
|
||||
{
|
||||
if (type == typeof(short) || type == typeof(ushort) ||
|
||||
@ -93,6 +96,8 @@ namespace Toolbox.Library.IO
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("GetFields " + type.GetFields().Length);
|
||||
|
||||
foreach (var field in type.GetFields())
|
||||
{
|
||||
var fieldType = field.FieldType;
|
||||
|
@ -116,18 +116,19 @@ namespace Toolbox.Library.IO
|
||||
|
||||
public static Matrix4x4 CalculateTransformMatrix(STBone bone)
|
||||
{
|
||||
var trans = Matrix4x4.CreateTranslation(new Vector3(bone.position[0], bone.position[1], bone.position[2]));
|
||||
var scale = Matrix4x4.CreateScale(new Vector3(bone.scale[0], bone.scale[1], bone.scale[2]));
|
||||
var trans = Matrix4x4.CreateTranslation(new Vector3(bone.Position.X, bone.Position.Y, bone.Position.Z));
|
||||
var scale = Matrix4x4.CreateScale(new Vector3(bone.Scale.X, bone.Scale.Y, bone.Scale.Z));
|
||||
|
||||
Matrix4x4 quat = Matrix4x4.Identity;
|
||||
|
||||
if (bone.RotationType == STBone.BoneRotationType.Euler)
|
||||
quat = Matrix4x4.CreateFromQuaternion(QuatFromEular(bone.rotation[0], bone.rotation[1], bone.rotation[2]));
|
||||
quat = Matrix4x4.CreateFromQuaternion(QuatFromEular(bone.EulerRotation.X, bone.EulerRotation.Y, bone.EulerRotation.Z));
|
||||
else
|
||||
quat = Matrix4x4.CreateFromQuaternion(QuatFromQuat(bone.rotation[0], bone.rotation[1], bone.rotation[2], bone.rotation[3]));
|
||||
quat = Matrix4x4.CreateFromQuaternion(QuatFromQuat(bone.Rotation.X, bone.Rotation.Y, bone.Rotation.Z, bone.Rotation.W));
|
||||
|
||||
return Matrix4x4.Multiply(quat, trans);
|
||||
}
|
||||
|
||||
public static Matrices CalculateInverseMatrix(STBone bone)
|
||||
{
|
||||
var matrices = new Matrices();
|
||||
@ -138,18 +139,7 @@ namespace Toolbox.Library.IO
|
||||
else
|
||||
matrices.transform = Matrix4x4.Identity;
|
||||
|
||||
//Now calculate the matrix with TK matrices
|
||||
var trans = Matrix4x4.CreateTranslation(new Vector3(bone.position[0], bone.position[1], bone.position[2]));
|
||||
var scale = Matrix4x4.CreateScale(new Vector3(bone.scale[0], bone.scale[1], bone.scale[2]));
|
||||
|
||||
Matrix4x4 quat = Matrix4x4.Identity;
|
||||
|
||||
if (bone.RotationType == STBone.BoneRotationType.Euler)
|
||||
quat = Matrix4x4.CreateFromQuaternion(QuatFromEular(bone.rotation[0], bone.rotation[1], bone.rotation[2]));
|
||||
else
|
||||
quat = Matrix4x4.CreateFromQuaternion(QuatFromQuat(bone.rotation[0], bone.rotation[1], bone.rotation[2], bone.rotation[3]));
|
||||
|
||||
matrices.transform = Matrix4x4.Multiply(Matrix4x4.Multiply(quat, trans), matrices.transform);
|
||||
matrices.transform = Matrix4x4.Multiply(CalculateTransformMatrix(bone), matrices.transform);
|
||||
|
||||
Matrix4x4 Inverse;
|
||||
Matrix4x4.Invert(matrices.transform, out Inverse);
|
||||
@ -242,19 +232,46 @@ namespace Toolbox.Library.IO
|
||||
M32 = mat.M23,
|
||||
M33 = mat.M33,
|
||||
M34 = mat.M43,
|
||||
};
|
||||
}
|
||||
|
||||
/* M11 = mat.M11,
|
||||
M12 = mat.M12,
|
||||
M13 = mat.M13,
|
||||
M14 = mat.M14,
|
||||
M21 = mat.M21,
|
||||
public static OpenTK.Matrix4 ToTKMatrix4x4(this Matrix4x4 mat)
|
||||
{
|
||||
if (mat.M11 == -0) mat.M11 = 0;
|
||||
if (mat.M12 == -0) mat.M12 = 0;
|
||||
if (mat.M13 == -0) mat.M13 = 0;
|
||||
if (mat.M14 == -0) mat.M14 = 0;
|
||||
if (mat.M21 == -0) mat.M21 = 0;
|
||||
if (mat.M22 == -0) mat.M22 = 0;
|
||||
if (mat.M23 == -0) mat.M23 = 0;
|
||||
if (mat.M24 == -0) mat.M24 = 0;
|
||||
if (mat.M31 == -0) mat.M31 = 0;
|
||||
if (mat.M32 == -0) mat.M32 = 0;
|
||||
if (mat.M33 == -0) mat.M33 = 0;
|
||||
if (mat.M34 == -0) mat.M34 = 0;
|
||||
if (mat.M41 == -0) mat.M41 = 0;
|
||||
if (mat.M42 == -0) mat.M42 = 0;
|
||||
if (mat.M43 == -0) mat.M43 = 0;
|
||||
if (mat.M44 == -0) mat.M44 = 0;
|
||||
|
||||
return new OpenTK.Matrix4()
|
||||
{
|
||||
M11 = mat.M11,
|
||||
M12 = mat.M21,
|
||||
M13 = mat.M31,
|
||||
M14 = mat.M41,
|
||||
M21 = mat.M12,
|
||||
M22 = mat.M22,
|
||||
M23 = mat.M23,
|
||||
M24 = mat.M24,
|
||||
M31 = mat.M31,
|
||||
M32 = mat.M32,
|
||||
M23 = mat.M32,
|
||||
M24 = mat.M42,
|
||||
M31 = mat.M13,
|
||||
M32 = mat.M23,
|
||||
M33 = mat.M33,
|
||||
M34 = mat.M34,*/
|
||||
M34 = mat.M44,
|
||||
M41 = mat.M14,
|
||||
M42 = mat.M24,
|
||||
M43 = mat.M34,
|
||||
M44 = mat.M44,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ namespace Toolbox.Library.Rendering
|
||||
|
||||
}
|
||||
|
||||
private static void SetBoneUniforms(GLControl control, ShaderProgram shader, STSkeleton Skeleton, STGenericObject mesh)
|
||||
public virtual void SetBoneUniforms(GLControl control, ShaderProgram shader, STSkeleton Skeleton, STGenericObject mesh)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var bone in Skeleton.bones)
|
||||
@ -214,20 +214,6 @@ namespace Toolbox.Library.Rendering
|
||||
Matrix4 transform = bone.invert * bone.Transform;
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(shader.programs[control], String.Format("bones[{0}]", i++)), false, ref transform);
|
||||
}
|
||||
|
||||
/* foreach (var FaceGroup in fshp.Shape.FaceGroups)
|
||||
{
|
||||
if (FaceGroup.BoneIndexList == null)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < FaceGroup.BoneIndexList.Length; i++)
|
||||
{
|
||||
GL.Uniform1(GL.GetUniformLocation(shader.programs[control], String.Format("boneIds[{0}]", i)), FaceGroup.BoneIndexList[i]);
|
||||
|
||||
Matrix4 transform = fmdl.Skeleton.Renderable.bones[(int)FaceGroup.BoneIndexList[i]].invert * fmdl.Skeleton.Renderable.bones[(int)FaceGroup.BoneIndexList[i]].Transform;
|
||||
GL.UniformMatrix4(GL.GetUniformLocation(shader.programs[control], String.Format("bones[{0}]", i)), false, ref transform);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private void SetUniformBlocks(STGenericMaterial mat, ShaderProgram shader, STGenericObject m)
|
||||
@ -423,6 +409,9 @@ namespace Toolbox.Library.Rendering
|
||||
if (group.faces.Count <= 3)
|
||||
return;
|
||||
|
||||
if (group.Material != null)
|
||||
Material = group.Material;
|
||||
|
||||
SetRenderData(Material, shader, m);
|
||||
SetUniforms(Material, shader, m);
|
||||
SetUniformBlocks(Material, shader, m);
|
||||
@ -430,7 +419,7 @@ namespace Toolbox.Library.Rendering
|
||||
SetVertexAttributes(m, shader);
|
||||
SetTextureUniforms(Material, m, shader);
|
||||
|
||||
if (m.IsSelected || m.GetMaterial().IsSelected)
|
||||
if (m.IsSelected || Material.IsSelected)
|
||||
{
|
||||
DrawModelSelection(group, shader);
|
||||
}
|
||||
|
@ -347,11 +347,6 @@ namespace Toolbox
|
||||
editor.Show();
|
||||
|
||||
((ObjectEditor)editor).SelectFirstNode();
|
||||
|
||||
if (file is TreeNodeFile)
|
||||
{
|
||||
((TreeNodeFile)file).OnAfterAdded();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -360,8 +355,10 @@ namespace Toolbox
|
||||
else
|
||||
AddObjectEditorFile(((TreeNode)file), (ObjectEditor)editor, false);
|
||||
}
|
||||
|
||||
SetFormatSettings(GetActiveIFileFormat());
|
||||
|
||||
if (file is TreeNodeFile)
|
||||
((TreeNodeFile)file).OnAfterAdded();
|
||||
}
|
||||
|
||||
private void AddObjectEditorFile(TreeNode file, ObjectEditor editor, bool ClearFiles)
|
||||
|
Loading…
Reference in New Issue
Block a user