From b1244fb01985e48142c0a42f5d6f9f1b62d08dd5 Mon Sep 17 00:00:00 2001 From: MediaMoots <81146974+MediaMoots@users.noreply.github.com> Date: Fri, 11 Aug 2023 06:09:59 +0800 Subject: [PATCH 1/2] Support changing skincount (#655) --- .../BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs | 275 +++++++++++++++++- .../BFRES/Shape/BfresShapeEditor.Designer.cs | 22 +- .../GUI/BFRES/Shape/BfresShapeEditor.cs | 39 ++- 3 files changed, 309 insertions(+), 27 deletions(-) diff --git a/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs b/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs index 52b2e0ba..be6d1508 100644 --- a/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs +++ b/File_Format_Library/FileFormats/BFRES/Bfres Structs/SubFiles/FMDL/FSHP.cs @@ -993,8 +993,34 @@ namespace Bfres.Structs break; } UpdateVertexData(); + UpdateEditor(); } } + public void UpdateVertexSkinCount(int NewSkinCount) + { + Cursor.Current = Cursors.WaitCursor; + + // Convert to and from rigid skinning + if ((VertexSkinCount == 1 && GetAreVerticiesRigidSkinned()) || + (NewSkinCount == 1 && !GetAreVerticiesRigidSkinned())) + { + ConvertSkinningMethod(); + } + + VertexSkinCount = (byte)NewSkinCount; + ChangeBoneListCount(NewSkinCount); + UpdateVertexAttributeBySkinCount(NewSkinCount); + BoneIndices = GetIndices(GetParentModel().Skeleton); + + OptmizeAttributeFormats(); + SaveShape(IsWiiU); + SaveVertexBuffer(IsWiiU); + + Cursor.Current = Cursors.Default; + + UpdateVertexData(); + UpdateEditor(); + } public void CreateIndexList(STGenericObject ob, FMDL mdl = null, bool ForceSkinLimt = false, int LimitAmount = 4) { BoneIndices = new List(); @@ -1182,7 +1208,164 @@ namespace Bfres.Structs } } } + public void ChangeBoneListCount(int NewSkinCount) + { + foreach (Vertex v in vertices) + { + for (int i = 0; i < NewSkinCount; i++) + { + if (v.boneIds.Count < VertexSkinCount) + { + v.boneIds.Add(0); + if (v.boneWeights.Count > 0) + { + v.boneWeights.Add(0); + } + } + else if (v.boneIds.Count > VertexSkinCount) + { + v.boneIds.RemoveAt(v.boneIds.Count - 1); + + if (v.boneWeights.Count > 0) + { + v.boneWeights.RemoveAt(v.boneIds.Count - 1); + } + } + } + } + } + public void UpdateVertexAttributeBySkinCount(int NewSkinCount) + { + int attCount = (int)Math.Ceiling(NewSkinCount / 4.0); + int existingAttCount = vertexAttributes.FindAll(att => att.Name.Contains("_w")).Count; + + // Get first existing _w and _i + VertexAttribute wFirstAtt = vertexAttributes.Find(att => att.Name == "_w0"); + VertexAttribute iFirstAtt = vertexAttributes.Find(att => att.Name == "_i0"); + + // No existing weight attribute groups + if (wFirstAtt == null || iFirstAtt == null) + { + return; + } + + // Add missing attributes + for (int i = 0; i < attCount; i++) + { + VertexAttribute wAtt = vertexAttributes.Find(att => att.Name == "_w" + i.ToString()); + VertexAttribute iAtt = vertexAttributes.Find(att => att.Name == "_i" + i.ToString()); + + if (wAtt == null) + { + VertexAttribute newAtt = new FSHP.VertexAttribute(); + newAtt.Name = "_w" + i.ToString(); + newAtt.Format = wFirstAtt.Format; + vertexAttributes.Add(newAtt); + } + + if (iAtt == null) + { + VertexAttribute newAtt = new FSHP.VertexAttribute(); + newAtt.Name = "_i" + i.ToString(); + newAtt.Format = iFirstAtt.Format; + vertexAttributes.Add(newAtt); + } + } + + // Remove extra attributes + for (int i = existingAttCount - 1; i >= 0; i--) + { + if (i < attCount) + { + continue; + } + vertexAttributes.RemoveAll(att => att.Name == "_w" + i.ToString()); + vertexAttributes.RemoveAll(att => att.Name == "_i" + i.ToString()); + } + } + public void ConvertSkinningMethod() + { + bool isConvertToSmooth = GetAreVerticiesRigidSkinned(); + + // Get Model + FMDL mdl = (FMDL)Parent.Parent; + if (mdl == null) + { + return; + } + + foreach (Vertex v in vertices) + { + for (int i = 0; i < v.boneIds.Count; i++) + { + // Convert to smooth skinning + if (isConvertToSmooth) + { + STBone rigidBone = mdl.Skeleton.bones.Find(bone => bone.RigidMatrixIndex == v.boneIds[i]); + if (rigidBone == null || rigidBone.SmoothMatrixIndex == -1) + { + continue; + } + + var smoothBoneIndex = Array.FindIndex(mdl.Skeleton.Node_Array, boneIndex => + mdl.Skeleton.bones[boneIndex].Text == rigidBone.Text); + if (smoothBoneIndex == -1) + { + throw new Exception("Convert To Smooth: Smooth Bone index not found for vertex" + i.ToString()); + } + + v.boneIds[i] = smoothBoneIndex; + } + else // Convert to rigid skinning + { + STBone smoothBone = mdl.Skeleton.bones[mdl.Skeleton.Node_Array[v.boneIds[i]]]; + if (smoothBone == null || smoothBone.RigidMatrixIndex == -1) + { + continue; + } + v.boneIds[i] = smoothBone.RigidMatrixIndex; + } + } + } + } + public bool GetAreVerticiesRigidSkinned() + { + FMDL mdl = (FMDL)Parent.Parent; + if (mdl == null) + { + return false; + } + + foreach (Vertex v in vertices) + { + for (int i = 0; i < v.boneIds.Count; i++) + { + STBone foundBone = mdl.Skeleton.bones.Find(x => x.RigidMatrixIndex == v.boneIds[i]); + if (foundBone != null) + { + return true; + } + } + } + return false; + } + public int GetLowestPossibleVertexSkinCount() + { + int lowestSkinCount = 0; + + // Find any weights and get the lowest possibe weight value + foreach (var v in vertices) + { + int maxWeightCount = v.boneWeights.FindLastIndex(w => w > 0) + 1; + if (lowestSkinCount < maxWeightCount) + { + lowestSkinCount = maxWeightCount; + } + } + + return lowestSkinCount; + } public void CreateNewBoundingBoxes(FMDL model) { boundingBoxes.Clear(); @@ -1729,41 +1912,53 @@ namespace Bfres.Structs { case ResGFX.AttribFormat.Format_32_32_32_32_Single: case ResGFX.AttribFormat.Format_32_32_32_Single: + case ResGFX.AttribFormat.Format_32_Single: attribute.Format = ResGFX.AttribFormat.Format_32_32_Single; break; case ResGFX.AttribFormat.Format_32_32_32_32_SInt: case ResGFX.AttribFormat.Format_32_32_32_SInt: + case ResGFX.AttribFormat.Format_32_SInt: attribute.Format = ResGFX.AttribFormat.Format_32_32_SInt; break; case ResGFX.AttribFormat.Format_32_32_32_32_UInt: case ResGFX.AttribFormat.Format_32_32_32_UInt: + case ResGFX.AttribFormat.Format_32_UInt: attribute.Format = ResGFX.AttribFormat.Format_32_32_UInt; break; case ResGFX.AttribFormat.Format_16_16_16_16_Single: + case ResGFX.AttribFormat.Format_16_Single: attribute.Format = ResGFX.AttribFormat.Format_16_16_Single; break; case ResGFX.AttribFormat.Format_16_16_16_16_SInt: + case ResGFX.AttribFormat.Format_16_SInt: attribute.Format = ResGFX.AttribFormat.Format_16_16_SInt; break; case ResGFX.AttribFormat.Format_16_16_16_16_UInt: + case ResGFX.AttribFormat.Format_16_UInt: attribute.Format = ResGFX.AttribFormat.Format_16_16_UInt; break; case ResGFX.AttribFormat.Format_8_8_8_8_UInt: + case ResGFX.AttribFormat.Format_8_UInt: attribute.Format = ResGFX.AttribFormat.Format_8_8_UInt; break; case ResGFX.AttribFormat.Format_8_8_8_8_SInt: + case ResGFX.AttribFormat.Format_8_SInt: attribute.Format = ResGFX.AttribFormat.Format_8_8_SInt; break; case ResGFX.AttribFormat.Format_8_8_8_8_SNorm: + case ResGFX.AttribFormat.Format_8_SNorm: attribute.Format = ResGFX.AttribFormat.Format_8_8_SNorm; break; case ResGFX.AttribFormat.Format_8_8_8_8_UNorm: + case ResGFX.AttribFormat.Format_8_UNorm: attribute.Format = ResGFX.AttribFormat.Format_8_8_UNorm; break; case ResGFX.AttribFormat.Format_8_8_8_8_SIntToSingle: + case ResGFX.AttribFormat.Format_8_SIntToSingle: attribute.Format = ResGFX.AttribFormat.Format_8_8_SIntToSingle; break; case ResGFX.AttribFormat.Format_8_8_8_8_UIntToSingle: + case ResGFX.AttribFormat.Format_8_UIntToSingle: attribute.Format = ResGFX.AttribFormat.Format_8_8_UIntToSingle; break; } @@ -1773,16 +1968,79 @@ namespace Bfres.Structs switch (attribute.Format) { case ResGFX.AttribFormat.Format_32_32_32_32_Single: + case ResGFX.AttribFormat.Format_32_32_Single: + case ResGFX.AttribFormat.Format_32_Single: attribute.Format = ResGFX.AttribFormat.Format_32_32_32_Single; break; case ResGFX.AttribFormat.Format_32_32_32_32_SInt: + case ResGFX.AttribFormat.Format_32_32_SInt: + case ResGFX.AttribFormat.Format_32_SInt: attribute.Format = ResGFX.AttribFormat.Format_32_32_32_SInt; break; case ResGFX.AttribFormat.Format_32_32_32_32_UInt: + case ResGFX.AttribFormat.Format_32_32_UInt: + case ResGFX.AttribFormat.Format_32_UInt: attribute.Format = ResGFX.AttribFormat.Format_32_32_32_UInt; break; } } + if (VertexSkinCount >= 4) + { + switch (attribute.Format) + { + case ResGFX.AttribFormat.Format_32_32_32_32_Single: + case ResGFX.AttribFormat.Format_32_32_Single: + case ResGFX.AttribFormat.Format_32_Single: + attribute.Format = ResGFX.AttribFormat.Format_32_32_32_32_Single; + break; + case ResGFX.AttribFormat.Format_32_32_32_SInt: + case ResGFX.AttribFormat.Format_32_32_SInt: + case ResGFX.AttribFormat.Format_32_SInt: + attribute.Format = ResGFX.AttribFormat.Format_32_32_32_32_SInt; + break; + case ResGFX.AttribFormat.Format_32_32_32_UInt: + case ResGFX.AttribFormat.Format_32_32_UInt: + case ResGFX.AttribFormat.Format_32_UInt: + attribute.Format = ResGFX.AttribFormat.Format_32_32_32_32_UInt; + break; + case ResGFX.AttribFormat.Format_16_16_Single: + case ResGFX.AttribFormat.Format_16_Single: + attribute.Format = ResGFX.AttribFormat.Format_16_16_16_16_Single; + break; + case ResGFX.AttribFormat.Format_16_16_SInt: + case ResGFX.AttribFormat.Format_16_SInt: + attribute.Format = ResGFX.AttribFormat.Format_16_16_16_16_SInt; + break; + case ResGFX.AttribFormat.Format_16_16_UInt: + case ResGFX.AttribFormat.Format_16_UInt: + attribute.Format = ResGFX.AttribFormat.Format_16_16_16_16_UInt; + break; + case ResGFX.AttribFormat.Format_8_8_UInt: + case ResGFX.AttribFormat.Format_8_UInt: + attribute.Format = ResGFX.AttribFormat.Format_8_8_8_8_UInt; + break; + case ResGFX.AttribFormat.Format_8_8_SInt: + case ResGFX.AttribFormat.Format_8_SInt: + attribute.Format = ResGFX.AttribFormat.Format_8_8_8_8_SInt; + break; + case ResGFX.AttribFormat.Format_8_8_SNorm: + case ResGFX.AttribFormat.Format_8_SNorm: + attribute.Format = ResGFX.AttribFormat.Format_8_8_8_8_SNorm; + break; + case ResGFX.AttribFormat.Format_8_8_UNorm: + case ResGFX.AttribFormat.Format_8_UNorm: + attribute.Format = ResGFX.AttribFormat.Format_8_8_8_8_UNorm; + break; + case ResGFX.AttribFormat.Format_8_8_SIntToSingle: + case ResGFX.AttribFormat.Format_8_SIntToSingle: + attribute.Format = ResGFX.AttribFormat.Format_8_8_8_8_SIntToSingle; + break; + case ResGFX.AttribFormat.Format_8_8_UIntToSingle: + case ResGFX.AttribFormat.Format_8_UIntToSingle: + attribute.Format = ResGFX.AttribFormat.Format_8_8_8_8_UIntToSingle; + break; + } + } } public void UpdateVertices() @@ -1806,6 +2064,8 @@ namespace Bfres.Structs boneInd.Add(new List()); } + int TargetVertexSkinCount = Math.Max(4, (int)VertexSkinCount); + foreach (Vertex vtx in vertices) { if (VertexSkinCount == 0 || VertexSkinCount == 1) @@ -1828,8 +2088,8 @@ namespace Bfres.Structs colors.Add(new Syroot.Maths.Vector4F(vtx.col.X, vtx.col.Y, vtx.col.Z, vtx.col.W)); // Init arrays based on skincount - float[] weightsA = new float[Math.Max(4, (int)VertexSkinCount)]; - int[] indicesA = new int[Math.Max(4, (int)VertexSkinCount)]; + float[] weightsA = new float[TargetVertexSkinCount]; + int[] indicesA = new int[TargetVertexSkinCount]; // Cache bone weights for (int i = 0; i < VertexSkinCount; i++) @@ -1845,8 +2105,9 @@ namespace Bfres.Structs //Produce identical results for the weight output as BFRES_Vertex.py //This should prevent encoding back and exploding int MaxWeight = 255; - for (int i = 0; i < Math.Max(4, (int)VertexSkinCount); i++) + for (int i = 0; i < TargetVertexSkinCount; i++) { + // If vertex has no weight for current skin count set an empty value if (VertexSkinCount < i + 1 || vtx.boneWeights.Count < i + 1) { weightsA[i] = 0; @@ -1881,19 +2142,21 @@ namespace Bfres.Structs Syroot.Maths.Vector4F vWeight4 = new Syroot.Maths.Vector4F(); Syroot.Maths.Vector4F vBoneInd4 = new Syroot.Maths.Vector4F(); - for (int i = 0; i < Math.Max(4, (int)VertexSkinCount); i++) + for (int i = 0; i < TargetVertexSkinCount; i++) { vWeight4[v4Index] = weightsA[i]; vBoneInd4[v4Index] = indicesA[i]; - // Save a v4 set each time v4Index hits 3 (.w) - if (v4Index == 3) + // Save a v4 set each time v4Index hits 3 (.w) or its the last index + if (v4Index == 3 || i == TargetVertexSkinCount - 1) { if (weights.Count > v4ListIndex) weights[v4ListIndex].Add(vWeight4); if (boneInd.Count > v4ListIndex) boneInd[v4ListIndex].Add(vBoneInd4); + vWeight4 = new Syroot.Maths.Vector4F(); + vBoneInd4 = new Syroot.Maths.Vector4F(); v4ListIndex++; v4Index = 0; continue; diff --git a/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.Designer.cs b/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.Designer.cs index c46448da..83839270 100644 --- a/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.Designer.cs +++ b/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.Designer.cs @@ -186,24 +186,14 @@ // shapeVertexSkinCountUD // this.shapeVertexSkinCountUD.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.shapeVertexSkinCountUD.Increment = new decimal(new int[] { - 0, - 0, - 0, - 0}); + this.shapeVertexSkinCountUD.Increment = 1; + this.shapeVertexSkinCountUD.Value = 0; this.shapeVertexSkinCountUD.Location = new System.Drawing.Point(83, 59); - this.shapeVertexSkinCountUD.Maximum = new decimal(new int[] { - 255, - 0, - 0, - 0}); - this.shapeVertexSkinCountUD.Minimum = new decimal(new int[] { - -2147483648, - 0, - 0, - -2147483648}); + this.shapeVertexSkinCountUD.Maximum = 16; + this.shapeVertexSkinCountUD.Minimum = -2147483648; + this.shapeVertexSkinCountUD.ValueChanged += SkinCountUD_ValueChanged; this.shapeVertexSkinCountUD.Name = "shapeVertexSkinCountUD"; - this.shapeVertexSkinCountUD.ReadOnly = true; + this.shapeVertexSkinCountUD.ReadOnly = false; this.shapeVertexSkinCountUD.Size = new System.Drawing.Size(121, 20); this.shapeVertexSkinCountUD.TabIndex = 39; // diff --git a/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs b/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs index 9de85a57..5b7406f8 100644 --- a/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs +++ b/File_Format_Library/GUI/BFRES/Shape/BfresShapeEditor.cs @@ -42,7 +42,22 @@ namespace FirstPlugin activeShape = fshp; - shapeVertexSkinCountUD.Value = (decimal)fshp.VertexSkinCount; + // Disable skin count adjustment if no skinning + shapeVertexSkinCountUD.ReadOnly = fshp.VertexSkinCount == 0; + + // Setup skin count adjustment + int lowestVertexSkinCount = fshp.GetLowestPossibleVertexSkinCount(); + if (shapeVertexSkinCountUD.Minimum > lowestVertexSkinCount && + shapeVertexSkinCountUD.Value > lowestVertexSkinCount) + { + shapeVertexSkinCountUD.Minimum = lowestVertexSkinCount; + shapeVertexSkinCountUD.Value = fshp.VertexSkinCount; + } + else + { + shapeVertexSkinCountUD.Value = fshp.VertexSkinCount; + shapeVertexSkinCountUD.Minimum = lowestVertexSkinCount; + } FMDL fmdl = fshp.GetParentModel(); @@ -67,14 +82,12 @@ namespace FirstPlugin if (fshp.VertexBufferU != null) { - vertexBufferSkinCountUD.Maximum = (decimal)fshp.VertexBufferU.VertexSkinCount; - vertexBufferSkinCountUD.Value = (decimal)fshp.VertexBufferU.VertexSkinCount; + vertexBufferSkinCountUD.Value = fshp.VertexBufferU.VertexSkinCount; vertexBufferList1.LoadVertexBuffers(fshp, fshp.VertexBufferU); } else { - vertexBufferSkinCountUD.Maximum = (decimal)fshp.VertexBuffer.VertexSkinCount; - vertexBufferSkinCountUD.Value = (decimal)fshp.VertexBuffer.VertexSkinCount; + vertexBufferSkinCountUD.Value = fshp.VertexBuffer.VertexSkinCount; vertexBufferList1.LoadVertexBuffers(fshp, fshp.VertexBuffer); } @@ -292,5 +305,21 @@ namespace FirstPlugin } } } + + private void SkinCountUD_ValueChanged(object sender, System.EventArgs e) + { + if (!(sender is NumericUpDownInt valueSelector)) + { + return; + } + + // Skip if already the same or 0 + if (activeShape.VertexSkinCount == valueSelector.Value || valueSelector.Value == 0) + { + return; + } + + activeShape.UpdateVertexSkinCount((int)valueSelector.Value); + } } } From fe52c1cbc60c8ab7f974a9263fce1baf36d69c10 Mon Sep 17 00:00:00 2001 From: MediaMoots <81146974+MediaMoots@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:41:58 +0800 Subject: [PATCH 2/2] Fix extra frame added to smd animation export (#659) --- Switch_Toolbox_Library/FileFormats/Animation/SMD.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Switch_Toolbox_Library/FileFormats/Animation/SMD.cs b/Switch_Toolbox_Library/FileFormats/Animation/SMD.cs index c2ed987d..5e461b8a 100644 --- a/Switch_Toolbox_Library/FileFormats/Animation/SMD.cs +++ b/Switch_Toolbox_Library/FileFormats/Animation/SMD.cs @@ -347,7 +347,7 @@ namespace Toolbox.Library.Animations file.WriteLine("skeleton"); anim.SetFrame(0); - for (int i = 0; i <= anim.FrameCount; i++) + for (int i = 0; i < anim.FrameCount; i++) { anim.NextFrame(Skeleton, false, true);