diff --git a/Smash Forge/Filetypes/Application/MaterialXML.cs b/Smash Forge/Filetypes/Application/MaterialXML.cs index 8ce5e194..b190ca93 100644 --- a/Smash Forge/Filetypes/Application/MaterialXML.cs +++ b/Smash Forge/Filetypes/Application/MaterialXML.cs @@ -115,12 +115,11 @@ private static void WriteMatParams(XmlDocument doc, Nud.Material mat, XmlNode ma { float[] values = mat.GetPropertyValues(materialProperty); // Only print 4 values, to avoid lots of trailing zeroes. - for (int count = 0, max = Math.Min(4, values.Length);;) + for (int i = 0; i < 4 && i < values.Length; i++) { - paramnode.InnerText += values[count++].ToString("G9"); - if (count >= max) - break; - paramnode.InnerText += " "; + if (i > 0) + paramnode.InnerText += " "; + paramnode.InnerText += values[i].ToString("G9"); } } @@ -152,7 +151,7 @@ private static void AddUintAttribute(XmlDocument doc, string name, uint value, X public static void ImportMaterialAsXml(Nud n, string filename) { - // Creates a list of materials and then trys to apply the materials to the polygons. + // Creates a list of materials and then tries to apply the materials to the polygons. int polyCount = CalculatePolygonCount(n); XmlDocument doc = new XmlDocument(); @@ -172,7 +171,7 @@ public static void ImportMaterialAsXml(Nud n, string filename) { if (!(polynode.Name.Equals("polygon"))) continue; - + matCountForPolyId.Add(polynode.ChildNodes.Count); if (matCountForPolyId.Count > polyCount) @@ -222,7 +221,7 @@ private static void ApplyMaterials(Nud n, List materialList, List< } matIndex += 1; } - + polyIndex += 1; } } @@ -308,7 +307,7 @@ private static void ReadTextures(Nud.Material material, XmlNode textureNode) { if (!(textureNode.Name.Equals("texture"))) return; - + Nud.MatTexture matTexture = new Nud.MatTexture(); material.textures.Add(matTexture); @@ -346,7 +345,7 @@ private static void ReadMatParams(XmlNode polyNode, Nud.Material material, XmlNo string name = GetNodeName(materialNode); List valueList = ParamValuesFromMaterialPropertyText(materialNode, name); - // Parameters should always have 4 values. + // Parameters should always have 4 values. if (valueList.Count != 4) throw new ParamArrayLengthException(polyNode.ChildNodes.Count, name); @@ -359,7 +358,7 @@ private static void ReadMatParams(XmlNode polyNode, Nud.Material material, XmlNo { MessageBox.Show(String.Format("Polygon{0} contains more than 1 instance of {1}. \n" + "Only the first instance of {1} will be added.", polyNode.ChildNodes.Count.ToString(), name)); - } + } } private static List ParamValuesFromMaterialPropertyText(XmlNode materialPropertyNode, string propertyName) @@ -387,7 +386,7 @@ private static List ParamValuesFromMaterialPropertyText(XmlNode materialP else if (float.TryParse(stringValue, out newValue)) valueList.Add(newValue); else - valueList.Add(0.0f); + valueList.Add(0.0f); } return valueList; diff --git a/Smash Forge/Filetypes/Models/Nuds/Mesh.cs b/Smash Forge/Filetypes/Models/Nuds/Mesh.cs index 1754ec5b..cd1b59f3 100644 --- a/Smash Forge/Filetypes/Models/Nuds/Mesh.cs +++ b/Smash Forge/Filetypes/Models/Nuds/Mesh.cs @@ -26,13 +26,13 @@ public enum BoneFlags public int boneflag = (int)BoneFlags.Rigged; public short singlebind = -1; - public int sortBias = 0; public bool billboardY = false; public bool billboard = false; public bool useNsc = false; public bool sortByObjHierarchy = true; - public float[] boundingSphere = new float[8]; + public float[] boundingSphere = new float[4]; + public float sortBias = 0; public float sortingDistance = 0; public Mesh() @@ -71,88 +71,45 @@ public void addVertex(Vertex v) public void GenerateBoundingSphere() { - Vector3 cen1 = new Vector3(0,0,0), cen2 = new Vector3(0,0,0); - double rad1 = 0, rad2 = 0; - - //Get first vert - int vertCount = 0; - Vector3 vert0 = new Vector3(); + bool initial = false; + Vector3 min = new Vector3(); + Vector3 max = new Vector3(); foreach (Polygon p in Nodes) { foreach (Vertex v in p.vertices) { - vert0 = v.pos; - vertCount++; - break; - } - break; - } - - if (vertCount == 0) - return; - - //Calculate average and min/max - Vector3 min = new Vector3(vert0); - Vector3 max = new Vector3(vert0); - - vertCount = 0; - foreach (Polygon p in Nodes) - { - foreach(Vertex v in p.vertices) - { - for (int i = 0; i < 3; i++) + if (!initial) { - min[i] = Math.Min(min[i], v.pos[i]); - max[i] = Math.Max(max[i], v.pos[i]); + min = new Vector3(v.pos); + max = new Vector3(v.pos); + initial = true; + } + else + { + for (int i = 0; i < 3; i++) + { + min[i] = Math.Min(min[i], v.pos[i]); + max[i] = Math.Max(max[i], v.pos[i]); + } } - - cen1 += v.pos; - vertCount++; } } - cen1 /= vertCount; - for (int i = 0; i < 3; i++) - cen2[i] = (min[i]+max[i])/2; - - //Calculate the radius of each - double dist1, dist2; + Vector3 center = (min + max) / 2; + float radius = 0.0f; foreach (Polygon p in Nodes) { foreach (Vertex v in p.vertices) { - dist1 = ((Vector3)(v.pos - cen1)).Length; - if (dist1 > rad1) - rad1 = dist1; - - dist2 = ((Vector3)(v.pos - cen2)).Length; - if (dist2 > rad2) - rad2 = dist2; + radius = Math.Max(radius, (v.pos - center).LengthSquared); } } + // Use LengthSquared since it's faster than Length, then Sqrt afterwards. + radius = (float)Math.Sqrt(radius); - // Use the one with the lowest radius. - Vector3 temp; - double radius; - if (rad1 < rad2) - { - temp = cen1; - radius = rad1; - } - else - { - temp = cen2; - radius = rad2; - } - - // Set for (int i = 0; i < 3; i++) - { - boundingSphere[i] = temp[i]; - boundingSphere[i+4] = temp[i]; - } - boundingSphere[3] = (float)radius; - boundingSphere[7] = 0; + boundingSphere[i] = center[i]; + boundingSphere[3] = radius; } public float CalculateSortingDistance(Vector3 cameraPosition) @@ -168,44 +125,11 @@ public float CalculateSortingDistance(Vector3 cameraPosition) } Vector3 distanceVector = new Vector3(cameraPosition - meshCenter); - return distanceVector.Length + boundingSphere[3] + sortBias; - } - - private int CalculateSortBias() - { - if (!(Text.Contains("SORTBIAS"))) - return 0; - - // Isolate the integer value from the mesh name. - string sortBiasKeyWord = "SORTBIAS"; - string sortBiasText = GetSortBiasNumbers(sortBiasKeyWord); - - int sortBiasValue = 0; - int.TryParse(sortBiasText, out sortBiasValue); - - return sortBiasValue; - } - - private string GetSortBiasNumbers(string sortBiasKeyWord) - { - // HACK: Just ignore the 'm' prefix. - string modifiedText = Text.Replace("m", ""); - - string numbers = ""; - for (int i = modifiedText.IndexOf(sortBiasKeyWord) + sortBiasKeyWord.Length; i < Text.Length; i++) - { - if (modifiedText[i] != '_') - numbers += modifiedText[i]; - else - break; - } - - return numbers; + return distanceVector.Length + boundingSphere[3] - sortBias; } public void SetMeshAttributesFromName() { - sortBias = CalculateSortBias(); billboard = Text.Contains("BILLBOARD"); billboardY = Text.Contains("BILLBOARDYAXIS"); useNsc = Text.Contains("NSC"); diff --git a/Smash Forge/Filetypes/Models/Nuds/NUD.cs b/Smash Forge/Filetypes/Models/Nuds/NUD.cs index f9b0c4ae..2f96156a 100644 --- a/Smash Forge/Filetypes/Models/Nuds/NUD.cs +++ b/Smash Forge/Filetypes/Models/Nuds/NUD.cs @@ -134,7 +134,7 @@ private string GetTextureIdsWithoutNutOrDummyTex(NUT nut) { bool validTextureId = false; - // Checks to see if the texture is in the nut. + // Checks to see if the texture is in the nut. foreach (NutTexture nutTex in nut.Nodes) { if (matTex.hash == nutTex.HashId) @@ -173,7 +173,7 @@ private string GetTextureIdsWithoutNutOrDummyTex(NUT nut) public void DepthSortMeshes(Vector3 cameraPosition) { - // Meshes can be rendered in the order they appear in the NUD, by bounding spheres, and offsets. + // Meshes can be rendered in the order they appear in the NUD, by bounding spheres, and offsets. List unsortedMeshes = new List(); foreach (Mesh m in Nodes) { @@ -182,7 +182,7 @@ public void DepthSortMeshes(Vector3 cameraPosition) unsortedMeshes.Add(m); } - // Order by the distance from the camera to the closest point on the bounding sphere. + // Order by the distance from the camera to the closest point on the bounding sphere. // More distance objects will be rendered first. depthSortedMeshes = unsortedMeshes.OrderBy(m => -m.sortingDistance).ToList(); } @@ -267,7 +267,7 @@ private void DrawBoundingSpheres() { GL.UseProgram(0); - // Draw NUD bounding box. + // Draw NUD bounding box. GL.Color4(Color.GhostWhite); ShapeDrawing.DrawCube(new Vector3(boundingSphere[0], boundingSphere[1], boundingSphere[2]), boundingSphere[3], true); @@ -283,7 +283,7 @@ private void DrawBoundingSpheres() { if (mesh.useNsc && mesh.singlebind != -1) { - // Use the center of the bone as the bounding box center for NSC meshes. + // Use the center of the bone as the bounding box center for NSC meshes. Vector3 center = ((ModelContainer)Parent).VBN.bones[mesh.singlebind].pos; ShapeDrawing.DrawCube(center, mesh.boundingSphere[3], true); } @@ -300,96 +300,51 @@ public void GenerateBoundingSpheres() foreach (Mesh m in Nodes) m.GenerateBoundingSphere(); - Vector3 cen1 = new Vector3(0,0,0), cen2 = new Vector3(0,0,0); - double rad1 = 0, rad2 = 0; - - //Get first vert - int vertCount = 0; - Vector3 vert0 = new Vector3(); - foreach (Mesh m in Nodes) - { - foreach (Polygon p in m.Nodes) - { - foreach (Vertex v in p.vertices) - { - vert0 = v.pos; - vertCount++; - break; - } - break; - } - break; - } - - if (vertCount == 0) //No vertices - return; - - //Calculate average and min/max - Vector3 min = new Vector3(vert0); - Vector3 max = new Vector3(vert0); - - vertCount = 0; + bool initial = false; + Vector3 min = new Vector3(); + Vector3 max = new Vector3(); foreach (Mesh m in Nodes) { foreach (Polygon p in m.Nodes) { foreach (Vertex v in p.vertices) { - for (int i = 0; i < 3; i++) + if (!initial) { - min[i] = Math.Min(min[i], v.pos[i]); - max[i] = Math.Max(max[i], v.pos[i]); + min = new Vector3(v.pos); + max = new Vector3(v.pos); + initial = true; + } + else + { + for (int i = 0; i < 3; i++) + { + min[i] = Math.Min(min[i], v.pos[i]); + max[i] = Math.Max(max[i], v.pos[i]); + } } - - cen1 += v.pos; - vertCount++; } } } - cen1 /= vertCount; - for (int i = 0; i < 3; i++) - cen2[i] = (min[i]+max[i])/2; - - //Calculate the radius of each - double dist1, dist2; + Vector3 center = (min + max) / 2; + float radius = 0.0f; foreach (Mesh m in Nodes) { foreach (Polygon p in m.Nodes) { foreach (Vertex v in p.vertices) { - dist1 = ((Vector3)(v.pos - cen1)).Length; - if (dist1 > rad1) - rad1 = dist1; - - dist2 = ((Vector3)(v.pos - cen2)).Length; - if (dist2 > rad2) - rad2 = dist2; + radius = Math.Max(radius, (v.pos - center).LengthSquared); } } } + // Use LengthSquared since it's faster than Length, then Sqrt afterwards. + radius = (float)Math.Sqrt(radius); - //Use the one with the lowest radius - Vector3 temp; - double radius; - if (rad1 < rad2) - { - temp = cen1; - radius = rad1; - } - else - { - temp = cen2; - radius = rad2; - } - - //Set for (int i = 0; i < 3; i++) - { - boundingSphere[i] = temp[i]; - } - boundingSphere[3] = (float)radius; + boundingSphere[i] = center[i]; + boundingSphere[3] = radius; } public void SetPropertiesFromXMB(XMBFile xmb) @@ -457,7 +412,7 @@ private void DrawSelectionOutlines(Shader shader, Camera camera) private void DrawShadedPolygons(Shader shader, Camera camera, bool drawPolyIds = false) { - // For proper alpha blending, draw in reverse order and draw opaque objects first. + // For proper alpha blending, draw in reverse order and draw opaque objects first. List opaque = new List(); List transparent = new List(); @@ -605,7 +560,7 @@ private static void SetNscUniform(Polygon p, Shader shader) shader.SetMatrix4x4("nscMatrix", ref nscMatrix); } - + private void SetXMBUniforms(Shader shader, Polygon p) { shader.SetBoolToInt("isStage", modelType.Equals("stage")); @@ -716,7 +671,7 @@ public void DrawPoints(Camera camera, VBN vbn, PrimitiveType type) //vertexIndexEbo.Bind(); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); - + GL.PointSize(6f); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); GL.DrawElements(type, p.displayFaceSize, DrawElementsType.UnsignedInt, 0); @@ -769,7 +724,7 @@ public void ApplyMta(MTA m, int frame) if (md.frames.Count > 0 && md.frames.Count > frm) { material.UpdatePropertyAnim(md.name, md.frames[frm].values); - } + } } } } @@ -802,10 +757,12 @@ public void ApplyMta(MTA m, int frame) // Helpers for reading private struct ObjectData { + public float sortBias; + public string name; + public ushort boneflag; public short singlebind; public int polyCount; public int positionb; - public string name; } public struct PolyData @@ -866,26 +823,30 @@ public override void Read(string filename) ObjectData[] obj = new ObjectData[polysets]; List boundingSpheres = new List(); - int[] boneflags = new int[polysets]; for (int i = 0; i < polysets; i++) { - float[] boundingSphere = new float[8]; + float[] boundingSphere = new float[4]; boundingSphere[0] = fileData.ReadFloat(); boundingSphere[1] = fileData.ReadFloat(); boundingSphere[2] = fileData.ReadFloat(); boundingSphere[3] = fileData.ReadFloat(); - boundingSphere[4] = fileData.ReadFloat(); - boundingSphere[5] = fileData.ReadFloat(); - boundingSphere[6] = fileData.ReadFloat(); - boundingSphere[7] = fileData.ReadFloat(); boundingSpheres.Add(boundingSphere); + + // For some reason, the XYZ values of the sphere are repeated. + fileData.ReadFloat(); + fileData.ReadFloat(); + fileData.ReadFloat(); + + obj[i].sortBias = fileData.ReadFloat(); + int temp = fileData.Pos() + 4; fileData.Seek(nameStart + fileData.ReadInt()); obj[i].name = (fileData.ReadString()); // read name string fileData.Seek(temp); + fileData.ReadUShort(); //Seems to be always 0 - boneflags[i] = fileData.ReadUShort(); //Controls whether it's single-bound, weighted, or unbound + obj[i].boneflag = fileData.ReadUShort(); //Controls whether it's single-bound, weighted, or unbound obj[i].singlebind = fileData.ReadShort(); //Bone index if it is single-bound, otherwise -1 obj[i].polyCount = fileData.ReadUShort(); obj[i].positionb = fileData.ReadInt(); @@ -898,9 +859,10 @@ public override void Read(string filename) Mesh m = new Mesh(); m.Text = o.name; Nodes.Add(m); - m.boneflag = boneflags[meshIndex]; - m.singlebind = o.singlebind; m.boundingSphere = boundingSpheres[meshIndex++]; + m.sortBias = o.sortBias; + m.boneflag = o.boneflag; + m.singlebind = o.singlebind; for (int i = 0; i < o.polyCount; i++) { @@ -1290,8 +1252,11 @@ public override byte[] Rebuild() foreach (Mesh m in Nodes) { - foreach (float f in m.boundingSphere) - d.WriteFloat(f); + for (int i = 0; i < 4; i++) + d.WriteFloat(m.boundingSphere[i]); + for (int i = 0; i < 3; i++) + d.WriteFloat(m.boundingSphere[i]); + d.WriteFloat(m.sortBias); d.WriteInt(tempstring.Size()); @@ -1322,7 +1287,7 @@ public override byte[] Rebuild() p.uvCount = maxUV; obj.WriteByte(p.UVSize); - // MATERIAL SECTION + // MATERIAL SECTION int[] texoff = WriteMaterial(tex, p.materials, str); obj.WriteInt(texoff[0] + 0x30 + Nodes.Count * 0x30 + polyCount * 0x30); @@ -1726,7 +1691,7 @@ public MBN toMBN() foreach (Mesh mesh in Nodes) { MBN.Mesh nmesh = new MBN.Mesh(); - + int pi = 0; int fadd = vertBank.Count; nmesh.nodeList = new List>(); @@ -1754,7 +1719,7 @@ public MBN toMBN() mv.weight.Add(v.boneWeights.Count > 1 ? v.boneWeights[1] : 0); vertBank.Add(mv); } - // Node list + // Node list nmesh.nodeList.Add(nodeList); // polygons List fac = new List(); @@ -1796,7 +1761,7 @@ private void MergeDuplicateVertices() List vbank = new List(); // only check last 50 verts - may miss far apart ones but is faster foreach (int f in p.vertexIndices) { - int newFaceIndex = -1; + int newFaceIndex = -1; int i = 0; // Has to loop through all the new vertices each time, which is very slow.