diff --git a/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs index 3a75d8d..345949c 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; using SharpDX; namespace CodeWalker.GameFiles @@ -29,6 +30,9 @@ namespace CodeWalker.GameFiles public bool HasChanged { get; set; } = false; public List SaveWarnings = null; + public bool BuildStructsOnSave { get; set; } = true; + + public PathBVH BVH { get; set; } @@ -92,102 +96,7 @@ namespace CodeWalker.GameFiles Nav = rd.ReadBlock(); - if (Nav != null) - { - Vector3 posoffset = Nav.SectorTree?.AABBMin.XYZ() ?? Vector3.Zero; - Vector3 aabbsize = Nav.AABBSize; - - if (Nav.Vertices != null) - { - var verts = Nav.Vertices.GetFullList(); - Vertices = new List(verts.Count); - for (int i = 0; i < verts.Count; i++) - { - var ov = verts[i].ToVector3(); - Vertices.Add(posoffset + ov * aabbsize); - } - } - if (Nav.Indices != null) - { - Indices = Nav.Indices.GetFullList(); - } - if (Nav.Edges != null) - { - var edges = Nav.Edges.GetFullList(); - Edges = new List(edges.Count); - for (int i = 0; i < edges.Count; i++) - { - YnvEdge edge = new YnvEdge(); - edge.Init(this, edges[i]); - Edges.Add(edge); - } - } - if (Nav.Polys != null) - { - var polys = Nav.Polys.GetFullList(); - Polys = new List(polys.Count); - for (int i = 0; i < polys.Count; i++) - { - YnvPoly poly = new YnvPoly(); - poly.Init(this, polys[i]); - poly.Index = i; - Polys.Add(poly); - } - } - if (Nav.Portals != null) - { - var portals = Nav.Portals; - Portals = new List(portals.Length); - for (int i = 0; i < portals.Length; i++) - { - YnvPortal portal = new YnvPortal(); - portal.Init(this, portals[i]); - portal.Index = i; - portal.PositionFrom = posoffset + portal._RawData.PositionFrom.ToVector3() * aabbsize; - portal.PositionTo = posoffset + portal._RawData.PositionTo.ToVector3() * aabbsize; - Portals.Add(portal); - } - } - - - ////### add points to the list and calculate positions... - var treestack = new Stack(); - var pointi = 0; - if (Nav.SectorTree != null) - { - treestack.Push(Nav.SectorTree); - } - while (treestack.Count > 0) - { - var sector = treestack.Pop(); - if (sector.Data != null) - { - var points = sector.Data.Points; - if (points != null) - { - if (Points == null) - { - Points = new List(); - } - for (int i = 0; i < points.Length; i++) - { - YnvPoint point = new YnvPoint(); - point.Init(this, points[i]); - point.Index = pointi; pointi++; - point.Position = posoffset + point._RawData.Position * aabbsize; - Points.Add(point); - } - } - } - if (sector.SubTree1 != null) treestack.Push(sector.SubTree1); - if (sector.SubTree2 != null) treestack.Push(sector.SubTree2); - if (sector.SubTree3 != null) treestack.Push(sector.SubTree3); - if (sector.SubTree4 != null) treestack.Push(sector.SubTree4); - } - - } - - + InitFromNav(); UpdateAllNodePositions(); @@ -200,16 +109,119 @@ namespace CodeWalker.GameFiles LoadQueued = true; } + public void InitFromNav() + { + if (Nav == null) return; + + Vector3 posoffset = Nav.SectorTree?.AABBMin.XYZ() ?? Vector3.Zero; + Vector3 aabbsize = Nav.AABBSize; + + if (Nav.Vertices != null) + { + var verts = Nav.Vertices.GetFullList(); + Vertices = new List(verts.Count); + for (int i = 0; i < verts.Count; i++) + { + var ov = verts[i].ToVector3(); + Vertices.Add(posoffset + ov * aabbsize); + } + } + if (Nav.Indices != null) + { + Indices = Nav.Indices.GetFullList(); + } + if (Nav.Edges != null) + { + var edges = Nav.Edges.GetFullList(); + Edges = new List(edges.Count); + for (int i = 0; i < edges.Count; i++) + { + YnvEdge edge = new YnvEdge(); + edge.Init(this, edges[i]); + Edges.Add(edge); + } + } + if (Nav.Polys != null) + { + var polys = Nav.Polys.GetFullList(); + Polys = new List(polys.Count); + for (int i = 0; i < polys.Count; i++) + { + YnvPoly poly = new YnvPoly(); + poly.Init(this, polys[i]); + poly.Index = i; + Polys.Add(poly); + } + } + if (Nav.Portals != null) + { + var portals = Nav.Portals; + Portals = new List(portals.Length); + for (int i = 0; i < portals.Length; i++) + { + YnvPortal portal = new YnvPortal(); + portal.Init(this, portals[i]); + portal.Index = i; + portal.PositionFrom = posoffset + portal._RawData.PositionFrom.ToVector3() * aabbsize; + portal.PositionTo = posoffset + portal._RawData.PositionTo.ToVector3() * aabbsize; + Portals.Add(portal); + } + } + + + ////### add points to the list and calculate positions... + var treestack = new Stack(); + var pointi = 0; + if (Nav.SectorTree != null) + { + treestack.Push(Nav.SectorTree); + } + while (treestack.Count > 0) + { + var sector = treestack.Pop(); + if (sector.Data != null) + { + var points = sector.Data.Points; + if (points != null) + { + if (Points == null) + { + Points = new List(); + } + for (int i = 0; i < points.Length; i++) + { + YnvPoint point = new YnvPoint(); + point.Init(this, points[i]); + point.Index = pointi; pointi++; + point.Position = posoffset + point._RawData.Position * aabbsize; + Points.Add(point); + } + } + } + if (sector.SubTree1 != null) treestack.Push(sector.SubTree1); + if (sector.SubTree2 != null) treestack.Push(sector.SubTree2); + if (sector.SubTree3 != null) treestack.Push(sector.SubTree3); + if (sector.SubTree4 != null) treestack.Push(sector.SubTree4); + } + + } + + + public byte[] Save() { - BuildStructs(); + if (BuildStructsOnSave) + { + BuildStructs(); + } byte[] data = ResourceBuilder.Build(Nav, 2); //ynv is version 2... return data; } + private void BuildStructs() { Vector3 posoffset = Nav.SectorTree?.AABBMin.XYZ() ?? Vector3.Zero; @@ -326,7 +338,8 @@ namespace CodeWalker.GameFiles { var point = Points[i]; var pdata = point.RawData; - pdata.Position = point.Position; + pdata.Position = (point.Position - posoffset) * aabbsizeinv; + point.RawData = pdata; } } @@ -398,8 +411,9 @@ namespace CodeWalker.GameFiles root.SetAABBs(orig.AABBMin.XYZ(), orig.AABBMax.XYZ()); uint pointindex = 0; + var pointflags = new bool[Points?.Count ?? 0]; - BuildSectorTree(root, depth, ref pointindex); + BuildSectorTree(root, depth, ref pointindex, pointflags); Nav.SectorTree = root; @@ -418,13 +432,12 @@ namespace CodeWalker.GameFiles } - private void BuildSectorTree(NavMeshSector node, int depth, ref uint pointindex) + private void BuildSectorTree(NavMeshSector node, int depth, ref uint pointindex, bool[] pointflags) { Vector3 min = node.AABBMin.XYZ(); Vector3 max = node.AABBMax.XYZ(); Vector3 cen = (min + max) * 0.5f; - //totbytes += (uint)node.BlockLength; if (depth <= 0) { @@ -434,7 +447,6 @@ namespace CodeWalker.GameFiles data.PointsStartID = pointindex; - //totbytes += (uint)data.BlockLength; if (Polys != null) { @@ -452,7 +464,6 @@ namespace CodeWalker.GameFiles { data.PolyIDs = polyids.ToArray(); } - //totbytes += (uint)(polyids.Count * 2); } if (Points != null) @@ -461,17 +472,18 @@ namespace CodeWalker.GameFiles for (int i = 0; i < Points.Count; i++) { var point = Points[i]; - if (IsInBox(point.Position, min, max)) + if (IsInBox(point.Position, min, max, true) && (pointflags[i] == false)) { points.Add(point.RawData); + pointflags[i] = true; } } if (points.Count > 0) { data.Points = points.ToArray(); + data.PointsCount = (ushort)points.Count; pointindex += (uint)points.Count; } - //totbytes += (uint)(points.Count * 8); } } @@ -487,18 +499,97 @@ namespace CodeWalker.GameFiles node.SubTree2.SetAABBs(new Vector3(cen.X, min.Y, 0.0f), new Vector3(max.X, cen.Y, 0.0f)); node.SubTree3.SetAABBs(new Vector3(min.X, min.Y, min.Z), new Vector3(cen.X, cen.Y, cen.Z)); node.SubTree4.SetAABBs(new Vector3(min.X, cen.Y, 0.0f), new Vector3(cen.X, max.Y, 0.0f)); - BuildSectorTree(node.SubTree1, cdepth, ref pointindex); - BuildSectorTree(node.SubTree2, cdepth, ref pointindex); - BuildSectorTree(node.SubTree3, cdepth, ref pointindex); - BuildSectorTree(node.SubTree4, cdepth, ref pointindex); + BuildSectorTree(node.SubTree1, cdepth, ref pointindex, pointflags); + BuildSectorTree(node.SubTree2, cdepth, ref pointindex, pointflags); + BuildSectorTree(node.SubTree3, cdepth, ref pointindex, pointflags); + BuildSectorTree(node.SubTree4, cdepth, ref pointindex, pointflags); } } - private bool IsInBox(Vector3 p, Vector3 min, Vector3 max) + + + + + + public void WriteXml(StringBuilder sb, int indent) { - return (p.X >= min.X) && (p.X < max.X) && - (p.Y >= min.Y) && (p.Y < max.Y);// && - //(p.Z >= min.Z) && (p.Z < max.Z); + YnvXml.StringTag(sb, indent, "ContentFlags", Nav.ContentFlags.ToString()); + YnvXml.ValueTag(sb, indent, "AreaID", AreaID.ToString()); + YnvXml.SelfClosingTag(sb, indent, "BBMin " + FloatUtil.GetVector3XmlString(Nav.AABBMin)); + YnvXml.SelfClosingTag(sb, indent, "BBMax " + FloatUtil.GetVector3XmlString(Nav.AABBMax)); + YnvXml.SelfClosingTag(sb, indent, "BBSize " + FloatUtil.GetVector3XmlString(Nav.AABBSize)); + YnvXml.WriteItemArray(sb, AllPolys, indent, "Polygons"); + YnvXml.WriteItemArray(sb, AllPortals, indent, "Portals"); + YnvXml.WriteItemArray(sb, AllPoints, indent, "Points"); + } + public void ReadXml(XmlNode node) + { + Nav = new NavMesh(); + Nav.SectorTree = new NavMeshSector(); + Nav.ContentFlags = Xml.GetChildEnumInnerText(node, "ContentFlags"); + Nav.AreaID = Xml.GetChildUIntAttribute(node, "AreaID"); + Nav.AABBMin = Xml.GetChildVector3Attributes(node, "BBMin"); + Nav.AABBMax = Xml.GetChildVector3Attributes(node, "BBMax"); + Nav.AABBSize = Xml.GetChildVector3Attributes(node, "BBSize"); + Polys = XmlYnv.ReadItemList(node, "Polygons"); + Portals = XmlYnv.ReadItemList(node, "Portals"); + Points = XmlYnv.ReadItemList(node, "Points"); + + if (Polys != null) + { + for (int i = 0; i < Polys.Count; i++) + { + var poly = Polys[i]; + poly.Ynv = this; + poly.Index = i; + poly.AreaID = (ushort)AreaID; + } + } + if (Portals != null) + { + for (int i = 0; i < Portals.Count; i++) + { + var portal = Portals[i]; + portal.Ynv = this; + portal.Index = i; + portal.AreaIDFrom = (ushort)AreaID; + portal.AreaIDTo = (ushort)AreaID; + } + } + if (Points != null) + { + for (int i = 0; i < Points.Count; i++) + { + var point = Points[i]; + point.Ynv = this; + point.Index = i; + } + } + + + bool vehicle = ((Nav.ContentFlags & NavMeshFlags.Vehicle) != 0); + Nav.VersionUnk1 = 0x00010011; + Nav.VersionUnk2 = vehicle ? 0 : 0x85CB3561; + Nav.Transform = Matrix.Identity; + + + } + + + + + + + private bool IsInBox(Vector3 p, Vector3 min, Vector3 max, bool outer) + { + if (outer) + return (p.X >= min.X) && (p.X <= max.X) && + (p.Y >= min.Y) && (p.Y <= max.Y);// && + //(p.Z >= min.Z) && (p.Z < max.Z); + else + return (p.X >= min.X) && (p.X < max.X) && + (p.Y >= min.Y) && (p.Y < max.Y);// && + //(p.Z >= min.Z) && (p.Z < max.Z); } private bool BoxOverlaps(Vector3 bmin, Vector3 bmax, Vector3 min, Vector3 max) { @@ -528,19 +619,6 @@ namespace CodeWalker.GameFiles - public void UpdateContentFlags(bool vehicle) - { - NavMeshFlags f = NavMeshFlags.None; - //if (Nav.VerticesCount > 0) f = f | NavMeshFlags.Vertices; - //if (Nav.PortalsCount > 0) f = f | NavMeshFlags.Portals; - if (Polys?.Count > 0) f = f | NavMeshFlags.Vertices; - if (Portals?.Count > 0) f = f | NavMeshFlags.Portals; - if (vehicle) f = f | NavMeshFlags.Vehicle; - else f = f | NavMeshFlags.Unknown8; - Nav.ContentFlags = f; - } - - public void UpdateAllNodePositions() { @@ -636,6 +714,19 @@ namespace CodeWalker.GameFiles + public void UpdateContentFlags(bool vehicle) + { + NavMeshFlags f = NavMeshFlags.None; + if (Polys?.Count > 0) f = f | NavMeshFlags.Polygons; + if (Portals?.Count > 0) f = f | NavMeshFlags.Portals; + if (vehicle) f = f | NavMeshFlags.Vehicle; + else f = f | NavMeshFlags.Unknown8; //what exactly is this? + Nav.ContentFlags = f; + } + + + + public void BuildBVH() { var nodes = new List(); @@ -662,7 +753,7 @@ namespace CodeWalker.GameFiles - [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoly + [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoly : IMetaXmlItem { public NavMeshPoly _RawData; public NavMeshPoly RawData { get { return _RawData; } set { _RawData = value; } } @@ -673,45 +764,45 @@ namespace CodeWalker.GameFiles public ushort PartID { get { return _RawData.PartID; } set { _RawData.PartID = value; } } public uint PortalLinkID { get { return _RawData.PortalLinkID; } set { _RawData.PortalLinkID = value; } } public byte PortalLinkCount { get { return _RawData.PortalLinkCount; } set { _RawData.PortalLinkCount = value; } } - public byte Flags1 { get { return (byte)(_RawData.Unknown_00h & 0xFF); } set { _RawData.Unknown_00h = (ushort)((_RawData.Unknown_00h & 0xFF00) | (value & 0xFF)); } } - public byte Flags2 { get { return (byte)((_RawData.Unknown_24h.Value >> 0) & 0xFF); } set { _RawData.Unknown_24h = ((_RawData.Unknown_24h.Value & 0xFFFFFF00u) | ((value & 0xFFu) << 0)); } } - public byte Flags3 { get { return (byte)((_RawData.Unknown_24h.Value >> 9) & 0xFF); } set { _RawData.Unknown_24h = ((_RawData.Unknown_24h.Value & 0xFFFE01FFu) | ((value & 0xFFu) << 9)); } } - public byte Flags4 { get { return (byte)((_RawData.Unknown_28h.Value >> 16) & 0xFF); } set { _RawData.Unknown_28h = ((_RawData.Unknown_28h.Value & 0x0000FFFFu) | ((value & 0xFFu) << 16)); } } - public bool B00_AvoidUnk { get { return (_RawData.Unknown_00h & 1) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 0, value); } } - public bool B01_AvoidUnk { get { return (_RawData.Unknown_00h & 2) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 1, value); } } - public bool B02_IsFootpath { get { return (_RawData.Unknown_00h & 4) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 2, value); } } - public bool B03_IsUnderground { get { return (_RawData.Unknown_00h & 8) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 3, value); } } - //public bool B04_Unused { get { return (_RawData.Unknown_00h & 16) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 4, value); } } - //public bool B05_Unused { get { return (_RawData.Unknown_00h & 32) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 5, value); } } - public bool B06_SteepSlope { get { return (_RawData.Unknown_00h & 64) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 6, value); } } - public bool B07_IsWater { get { return (_RawData.Unknown_00h & 128) > 0; } set { _RawData.Unknown_00h = (ushort)BitUtil.UpdateBit(_RawData.Unknown_00h, 7, value); } } - public bool B08_UndergroundUnk0 { get { return (_RawData.Unknown_24h.Value & 1) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 0, value); } } - public bool B09_UndergroundUnk1 { get { return (_RawData.Unknown_24h.Value & 2) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 1, value); } } - public bool B10_UndergroundUnk2 { get { return (_RawData.Unknown_24h.Value & 4) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 2, value); } } - public bool B11_UndergroundUnk3 { get { return (_RawData.Unknown_24h.Value & 8) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 3, value); } } - //public bool B12_Unused { get { return (_RawData.Unknown_24h.Value & 16) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 4, value); } } - public bool B13_HasPathNode { get { return (_RawData.Unknown_24h.Value & 32) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 5, value); } } - public bool B14_IsInterior { get { return (_RawData.Unknown_24h.Value & 64) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 6, value); } } - public bool B15_InteractionUnk { get { return (_RawData.Unknown_24h.Value & 128) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 7, value); } } - //public bool B16_Unused { get { return (_RawData.Unknown_24h.Value & 256) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 8, value); } } - public bool B17_IsFlatGround { get { return (_RawData.Unknown_24h.Value & 512) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 9, value); } } - public bool B18_IsRoad { get { return (_RawData.Unknown_24h.Value & 1024) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 10, value); } } - public bool B19_IsCellEdge { get { return (_RawData.Unknown_24h.Value & 2048) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 11, value); } } - public bool B20_IsTrainTrack { get { return (_RawData.Unknown_24h.Value & 4096) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 12, value); } } - public bool B21_IsShallowWater { get { return (_RawData.Unknown_24h.Value & 8192) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 13, value); } } - public bool B22_FootpathUnk1 { get { return (_RawData.Unknown_24h.Value & 16384) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 14, value); } } - public bool B23_FootpathUnk2 { get { return (_RawData.Unknown_24h.Value & 32768) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 15, value); } } - public bool B24_FootpathMall { get { return (_RawData.Unknown_24h.Value & 65536) > 0; } set { _RawData.Unknown_24h = BitUtil.UpdateBit(_RawData.Unknown_24h.Value, 16, value); } } - public bool B25_SlopeSouth { get { return (_RawData.Unknown_28h.Value & 65536) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 16, value); } } - public bool B26_SlopeSouthEast { get { return (_RawData.Unknown_28h.Value & 131072) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 17, value); } } - public bool B27_SlopeEast { get { return (_RawData.Unknown_28h.Value & 262144) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 18, value); } } - public bool B28_SlopeNorthEast { get { return (_RawData.Unknown_28h.Value & 524288) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 19, value); } } - public bool B29_SlopeNorth { get { return (_RawData.Unknown_28h.Value & 1048576) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 20, value); } } - public bool B30_SlopeNorthWest { get { return (_RawData.Unknown_28h.Value & 2097152) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 21, value); } } - public bool B31_SlopeWest { get { return (_RawData.Unknown_28h.Value & 4194304) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 22, value); } } - public bool B32_SlopeSouthWest { get { return (_RawData.Unknown_28h.Value & 8388608) > 0; } set { _RawData.Unknown_28h = BitUtil.UpdateBit(_RawData.Unknown_28h.Value, 23, value); } } - public byte UnkX { get { return _RawData.Unknown_28h_8a; } set { _RawData.Unknown_28h_8a = value; } } - public byte UnkY { get { return _RawData.Unknown_28h_8b; } set { _RawData.Unknown_28h_8b = value; } } + public byte Flags1 { get { return _RawData.Flags1; } set { _RawData.Flags1 = value; } } + public byte Flags2 { get { return _RawData.Flags2; } set { _RawData.Flags2 = value; } } + public byte Flags3 { get { return _RawData.Flags3; } set { _RawData.Flags3 = value; } } + public byte Flags4 { get { return _RawData.Flags4; } set { _RawData.Flags4 = value; } } + public bool B00_AvoidUnk { get { return (_RawData.PolyFlags0 & 1) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 0, value); } } + public bool B01_AvoidUnk { get { return (_RawData.PolyFlags0 & 2) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 1, value); } } + public bool B02_IsFootpath { get { return (_RawData.PolyFlags0 & 4) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 2, value); } } + public bool B03_IsUnderground { get { return (_RawData.PolyFlags0 & 8) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 3, value); } } + public bool B04_Unused { get { return (_RawData.PolyFlags0 & 16) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 4, value); } } + public bool B05_Unused { get { return (_RawData.PolyFlags0 & 32) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 5, value); } } + public bool B06_SteepSlope { get { return (_RawData.PolyFlags0 & 64) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 6, value); } } + public bool B07_IsWater { get { return (_RawData.PolyFlags0 & 128) > 0; } set { _RawData.PolyFlags0 = (ushort)BitUtil.UpdateBit(_RawData.PolyFlags0, 7, value); } } + public bool B08_UndergroundUnk0 { get { return (_RawData.PolyFlags1 & 1) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 0, value); } } + public bool B09_UndergroundUnk1 { get { return (_RawData.PolyFlags1 & 2) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 1, value); } } + public bool B10_UndergroundUnk2 { get { return (_RawData.PolyFlags1 & 4) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 2, value); } } + public bool B11_UndergroundUnk3 { get { return (_RawData.PolyFlags1 & 8) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 3, value); } } + public bool B12_Unused { get { return (_RawData.PolyFlags1 & 16) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 4, value); } } + public bool B13_HasPathNode { get { return (_RawData.PolyFlags1 & 32) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 5, value); } } + public bool B14_IsInterior { get { return (_RawData.PolyFlags1 & 64) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 6, value); } } + public bool B15_InteractionUnk { get { return (_RawData.PolyFlags1 & 128) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 7, value); } } + public bool B16_Unused { get { return (_RawData.PolyFlags1 & 256) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 8, value); } } + public bool B17_IsFlatGround { get { return (_RawData.PolyFlags1 & 512) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 9, value); } } + public bool B18_IsRoad { get { return (_RawData.PolyFlags1 & 1024) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 10, value); } } + public bool B19_IsCellEdge { get { return (_RawData.PolyFlags1 & 2048) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 11, value); } } + public bool B20_IsTrainTrack { get { return (_RawData.PolyFlags1 & 4096) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 12, value); } } + public bool B21_IsShallowWater { get { return (_RawData.PolyFlags1 & 8192) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 13, value); } } + public bool B22_FootpathUnk1 { get { return (_RawData.PolyFlags1 & 16384) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 14, value); } } + public bool B23_FootpathUnk2 { get { return (_RawData.PolyFlags1 & 32768) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 15, value); } } + public bool B24_FootpathMall { get { return (_RawData.PolyFlags1 & 65536) > 0; } set { _RawData.PolyFlags1 = BitUtil.UpdateBit(_RawData.PolyFlags1, 16, value); } } + public bool B25_SlopeSouth { get { return (_RawData.PolyFlags2 & 65536) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 16, value); } } + public bool B26_SlopeSouthEast { get { return (_RawData.PolyFlags2 & 131072) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 17, value); } } + public bool B27_SlopeEast { get { return (_RawData.PolyFlags2 & 262144) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 18, value); } } + public bool B28_SlopeNorthEast { get { return (_RawData.PolyFlags2 & 524288) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 19, value); } } + public bool B29_SlopeNorth { get { return (_RawData.PolyFlags2 & 1048576) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 20, value); } } + public bool B30_SlopeNorthWest { get { return (_RawData.PolyFlags2 & 2097152) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 21, value); } } + public bool B31_SlopeWest { get { return (_RawData.PolyFlags2 & 4194304) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 22, value); } } + public bool B32_SlopeSouthWest { get { return (_RawData.PolyFlags2 & 8388608) > 0; } set { _RawData.PolyFlags2 = BitUtil.UpdateBit(_RawData.PolyFlags2, 23, value); } } + public byte UnkX { get { return _RawData.UnkX; } set { _RawData.UnkX = value; } } + public byte UnkY { get { return _RawData.UnkY; } set { _RawData.UnkY = value; } } public Vector3 Position { get; set; } @@ -805,7 +896,7 @@ namespace CodeWalker.GameFiles public Color4 GetColour() { var colour = new Color4(); - var u0 = _RawData.Unknown_00h; + var u0 = _RawData.PolyFlags0; if ((u0 & 1) > 0) colour.Red += 0.01f;//avoid? loiter? if ((u0 & 2) > 0) colour.Red += 0.01f; //avoid? if ((u0 & 4) > 0) colour.Green += 0.25f; //ped/footpath @@ -816,7 +907,7 @@ namespace CodeWalker.GameFiles if ((u0 & 128) > 0) colour.Blue += 0.25f; //water //if (u0 >= 256) colour.Green += 1.0f;//other bits unused... - var u2 = _RawData.Unknown_24h.Value; + var u2 = _RawData.PolyFlags1; //colour.Green = (u2 & 15) / 15.0f; //maybe underground amount..? //if ((u2 & 1) > 0) colour.Blue += 1.0f; //peds interact with something? underground? //if ((u2 & 2) > 0) colour.Green += 1.0f;//underneath something? @@ -837,7 +928,7 @@ namespace CodeWalker.GameFiles if ((u2 & 65536) > 0) colour.Green = 0.2f;//footpaths - mall areas? eg mall, vinewood blvd //if (u2 >= 131072) { }//other bits unused - var u5 = _RawData.Unknown_28h.Value; //32 bits + var u5 = _RawData.PolyFlags2; //32 bits //colour.Red = poly.Unknown_28h_8a / 255.0f; //heuristic vals..? //colour.Green = poly.Unknown_28h_8b / 255.0f; //heuristic vals..? //if ((u5 & 65536) > 0) colour.Red += 1.0f; //slope facing -Y (south) @@ -904,13 +995,83 @@ namespace CodeWalker.GameFiles } + public void WriteXml(StringBuilder sb, int indent) + { + byte[] flags = { Flags1, Flags2, Flags3, Flags4, UnkX, UnkY }; + YnvXml.WriteRawArray(sb, flags, indent, "Flags", ""); + YnvXml.WriteRawArray(sb, Vertices, indent, "Vertices", "", YnvXml.FormatVector3, 1); + var cind = indent + 1; + YnvXml.OpenTag(sb, indent, "Edges"); + foreach (var e in Edges) + { + YnvXml.Indent(sb, cind); + sb.AppendFormat("{0}:{1}, {2}:{3}", e.AreaID1, e.PolyID1, e.AreaID2, e.PolyID2); + sb.AppendLine(); + } + YnvXml.CloseTag(sb, indent, "Edges"); + if ((PortalLinks != null) && (PortalLinks.Length > 0)) + { + YnvXml.WriteRawArray(sb, PortalLinks, indent, "Portals", ""); + } + } + public void ReadXml(XmlNode node) + { + var flags = Xml.GetChildRawByteArrayNullable(node, "Flags", 10); + if (flags != null) + { + Flags1 = (flags.Length > 0) ? flags[0] : (byte)0; + Flags2 = (flags.Length > 1) ? flags[1] : (byte)0; + Flags3 = (flags.Length > 2) ? flags[2] : (byte)0; + Flags4 = (flags.Length > 3) ? flags[3] : (byte)0; + UnkX = (flags.Length > 4) ? flags[4] : (byte)0; + UnkY = (flags.Length > 5) ? flags[5] : (byte)0; + } + Vertices = Xml.GetChildRawVector3Array(node, "Vertices"); + Indices = new ushort[Vertices?.Length ?? 0];//needs to be present for later + var edgesstr = Xml.GetChildInnerText(node, "Edges"); + var edgesstrarr = edgesstr.Trim().Split('\n'); + var edges = new List(); + foreach (var edgestr in edgesstrarr) + { + var estrparts = edgestr.Trim().Split(','); + if (estrparts.Length != 2) + { continue; } + var estrp0 = estrparts[0].Trim().Split(':'); + var estrp1 = estrparts[1].Trim().Split(':'); + if (estrp0.Length != 2) + { continue; } + if (estrp1.Length != 2) + { continue; } + + uint aid1, aid2, pid1, pid2; + uint.TryParse(estrp0[0].Trim(), out aid1); + uint.TryParse(estrp0[1].Trim(), out pid1); + uint.TryParse(estrp1[0].Trim(), out aid2); + uint.TryParse(estrp1[1].Trim(), out pid2); + + var e = new YnvEdge(); + e.AreaID1 = aid1; + e.AreaID2 = aid2; + e.PolyID1 = pid1; + e.PolyID2 = pid2; + edges.Add(e); + } + if (edges.Count > 0) + { + Edges = edges.ToArray(); + } + + PortalLinks = Xml.GetChildRawUshortArrayNullable(node, "Portals"); + } + + public override string ToString() { return AreaID.ToString() + ", " + Index.ToString(); } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPortal : BasePathNode + [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPortal : BasePathNode, IMetaXmlItem { public NavMeshPortal _RawData; @@ -930,7 +1091,7 @@ namespace CodeWalker.GameFiles } set { - Angle = (byte)(value * 255.0f / ((float)Math.PI * 2.0f)); + Angle = (byte)Math.Round(value * 255.0f / ((float)Math.PI * 2.0f)); } } public Quaternion Orientation @@ -945,15 +1106,15 @@ namespace CodeWalker.GameFiles } public int Index { get; set; } - public byte Type { get { return _RawData.Type; } set { _RawData.Type = value; } } - public ushort AreaIDFrom { get { return _RawData.AreaIDFrom; } set { _RawData.AreaIDFrom = value; } } - public ushort AreaIDTo { get { return _RawData.AreaIDTo; } set { _RawData.AreaIDTo = value; } } + public byte Type { get { return _RawData.Type; } set { _RawData.Type = value; } }//1,2,3 + public ushort AreaIDFrom { get { return _RawData.AreaIDFrom; } set { _RawData.AreaIDFrom = value; } }//always Ynv.AreaID + public ushort AreaIDTo { get { return _RawData.AreaIDTo; } set { _RawData.AreaIDTo = value; } }//always Ynv.AreaID public ushort PolyIDFrom1 { get { return _RawData.PolyIDFrom1; } set { _RawData.PolyIDFrom1 = value; } } public ushort PolyIDFrom2 { get { return _RawData.PolyIDFrom2; } set { _RawData.PolyIDFrom2 = value; } } public ushort PolyIDTo1 { get { return _RawData.PolyIDTo1; } set { _RawData.PolyIDTo1 = value; } } public ushort PolyIDTo2 { get { return _RawData.PolyIDTo2; } set { _RawData.PolyIDTo2 = value; } } - public ushort Unk1 { get { return _RawData.FlagsUnk; } set { _RawData.FlagsUnk = value; } } - public byte Unk2 { get { return _RawData.AreaUnk; } set { _RawData.AreaUnk = value; } } + public ushort Unk1 { get { return _RawData.FlagsUnk; } set { _RawData.FlagsUnk = value; } }//always 0 + public byte Unk2 { get { return _RawData.AreaUnk; } set { _RawData.AreaUnk = value; } }//always 0 public void Init(YnvFile ynv, NavMeshPortal portal) @@ -974,13 +1135,32 @@ namespace CodeWalker.GameFiles Orientation = orientation; } + public void WriteXml(StringBuilder sb, int indent) + { + YnvXml.ValueTag(sb, indent, "Type", Type.ToString()); + YnvXml.ValueTag(sb, indent, "Angle", FloatUtil.ToString(Direction)); + YnvXml.ValueTag(sb, indent, "PolyFrom", PolyIDFrom1.ToString()); + YnvXml.ValueTag(sb, indent, "PolyTo", PolyIDTo1.ToString()); + YnvXml.SelfClosingTag(sb, indent, "PositionFrom " + FloatUtil.GetVector3XmlString(PositionFrom)); + YnvXml.SelfClosingTag(sb, indent, "PositionTo " + FloatUtil.GetVector3XmlString(PositionTo)); + } + public void ReadXml(XmlNode node) + { + Type = (byte)Xml.GetChildUIntAttribute(node, "Type"); + Direction = Xml.GetChildFloatAttribute(node, "Angle"); + PolyIDFrom1 = PolyIDFrom2 = (ushort)Xml.GetChildUIntAttribute(node, "PolyFrom"); + PolyIDTo1 = PolyIDTo2 = (ushort)Xml.GetChildUIntAttribute(node, "PolyTo"); + PositionFrom = Xml.GetChildVector3Attributes(node, "PositionFrom"); + PositionTo = Xml.GetChildVector3Attributes(node, "PositionTo"); + } + public override string ToString() { return Index.ToString(); } } - [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoint : BasePathNode + [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoint : BasePathNode, IMetaXmlItem { public NavMeshPoint _RawData; @@ -997,7 +1177,7 @@ namespace CodeWalker.GameFiles } set { - Angle = (byte)(value * 255.0f / ((float)Math.PI * 2.0f)); + Angle = (byte)Math.Round(value * 255.0f / ((float)Math.PI * 2.0f)); } } public Quaternion Orientation @@ -1012,7 +1192,7 @@ namespace CodeWalker.GameFiles } public int Index { get; set; } - public byte Type { get { return _RawData.Type; } set { _RawData.Type = value; } } + public byte Type { get { return _RawData.Type; } set { _RawData.Type = value; } }//0,1,2,3,4,5,128,171,254 public void Init(YnvFile ynv, NavMeshPoint point) { @@ -1030,11 +1210,23 @@ namespace CodeWalker.GameFiles Orientation = orientation; } + public void WriteXml(StringBuilder sb, int indent) + { + YnvXml.ValueTag(sb, indent, "Type", Type.ToString()); + YnvXml.ValueTag(sb, indent, "Angle", FloatUtil.ToString(Direction)); + YnvXml.SelfClosingTag(sb, indent, "Position " + FloatUtil.GetVector3XmlString(Position)); + } + public void ReadXml(XmlNode node) + { + Type = (byte)Xml.GetChildUIntAttribute(node, "Type"); + Direction = Xml.GetChildFloatAttribute(node, "Angle"); + Position = Xml.GetChildVector3Attributes(node, "Position"); + } + public override string ToString() { return Index.ToString() + ": " + Type.ToString(); } - } @@ -1090,4 +1282,83 @@ namespace CodeWalker.GameFiles } } + + + + + + + + + + public class YnvXml : MetaXmlBase + { + + public static string GetXml(YnvFile ynv) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(XmlHeader); + + if ((ynv != null) && (ynv.Nav != null)) + { + var name = "NavMesh"; + + OpenTag(sb, 0, name); + + ynv.WriteXml(sb, 1); + + CloseTag(sb, 0, name); + } + + return sb.ToString(); + } + + + } + + + public class XmlYnv + { + + public static YnvFile GetYnv(string xml) + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return GetYnv(doc); + } + + public static YnvFile GetYnv(XmlDocument doc) + { + YnvFile ynv = new YnvFile(); + ynv.ReadXml(doc.DocumentElement); + return ynv; + } + + + + + + public static List ReadItemList(XmlNode node, string name) where T : IMetaXmlItem, new() + { + var vnode2 = node.SelectSingleNode(name); + if (vnode2 != null) + { + var inodes = vnode2.SelectNodes("Item"); + if (inodes?.Count > 0) + { + var vlist = new List(); + foreach (XmlNode inode in inodes) + { + var v = new T(); + v.ReadXml(inode); + vlist.Add(v); + } + return vlist; + } + } + return null; + } + + } + } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 6a2752f..4455a7e 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -195,6 +195,7 @@ namespace CodeWalker.GameFiles //TestYdds(); //TestYfts(); //TestYpts(); + //TestYnvs(); //TestYmaps(); //TestPlacements(); //TestDrawables(); @@ -4057,6 +4058,80 @@ namespace CodeWalker.GameFiles if (errorfiles.Count > 0) { } } + public void TestYnvs() + { + bool xmltest = true; + var savetest = false; + var errorfiles = new List(); + foreach (RpfFile file in AllRpfs) + { + foreach (RpfEntry entry in file.AllEntries) + { + //try + { + if (entry.NameLower.EndsWith(".ynv")) + { + UpdateStatus(string.Format(entry.Path)); + YnvFile ynv = null; + try + { + ynv = RpfMan.GetFile(entry); + } + catch (Exception ex) + { + UpdateStatus("Error! " + ex.ToString()); + errorfiles.Add(entry); + } + if (xmltest && (ynv != null) && (ynv.Nav != null)) + { + var xml = YnvXml.GetXml(ynv); + if (xml != null) + { } + var ynv2 = XmlYnv.GetYnv(xml); + if (ynv2 != null) + { } + var ynv2b = ynv2.Save(); + if (ynv2b != null) + { } + var ynv3 = new YnvFile(); + RpfFile.LoadResourceFile(ynv3, ynv2b, 2); + var xml3 = YnvXml.GetXml(ynv3); + if (xml.Length != xml3.Length) + { } + var xmllines = xml.Split('\n'); + var xml3lines = xml3.Split('\n'); + if (xmllines.Length != xml3lines.Length) + { } + } + if (savetest && (ynv != null) && (ynv.Nav != null)) + { + var fentry = entry as RpfFileEntry; + if (fentry == null) + { continue; } //shouldn't happen + + var bytes = ynv.Save(); + + string origlen = TextUtil.GetBytesReadable(fentry.FileSize); + string bytelen = TextUtil.GetBytesReadable(bytes.Length); + + var ynv2 = new YnvFile(); + RpfFile.LoadResourceFile(ynv2, bytes, 2); + + if (ynv2.Nav == null) + { continue; } + + } + } + } + //catch (Exception ex) + //{ + // UpdateStatus("Error! " + ex.ToString()); + //} + } + } + if (errorfiles.Count > 0) + { } + } public void TestYmaps() { foreach (RpfFile file in AllRpfs) diff --git a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs index 7c15540..b05c7cb 100644 --- a/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs +++ b/CodeWalker.Core/GameFiles/MetaTypes/MetaXml.cs @@ -62,6 +62,11 @@ namespace CodeWalker.GameFiles YndFile ynd = RpfFile.GetFile(e, data); return GetXml(ynd, out filename); } + else if (fnl.EndsWith(".ynv")) + { + YnvFile ynv = RpfFile.GetFile(e, data); + return GetXml(ynv, out filename); + } else if (fnl.EndsWith(".ycd")) { YcdFile ycd = RpfFile.GetFile(e, data); @@ -172,6 +177,12 @@ namespace CodeWalker.GameFiles filename = fn + ".xml"; return YndXml.GetXml(ynd); } + public static string GetXml(YnvFile ynv, out string filename) + { + var fn = (ynv?.RpfFileEntry?.Name) ?? ""; + filename = fn + ".xml"; + return YnvXml.GetXml(ynv); + } public static string GetXml(YcdFile ycd, out string filename) { var fn = (ycd?.RpfFileEntry?.Name) ?? ""; @@ -2127,15 +2138,16 @@ namespace CodeWalker.GameFiles CacheFile = 4, AudioRel = 5, Ynd = 6, - Ycd = 7, - Ybn = 8, - Ytd = 9, - Ydr = 10, - Ydd = 11, - Yft = 12, - Ypt = 13, - Yld = 14, - Awc = 15, + Ynv = 7, + Ycd = 8, + Ybn = 9, + Ytd = 10, + Ydr = 11, + Ydd = 12, + Yft = 13, + Ypt = 14, + Yld = 15, + Awc = 16, } } diff --git a/CodeWalker.Core/GameFiles/Resources/Nav.cs b/CodeWalker.Core/GameFiles/Resources/Nav.cs index cf5bd53..1b85322 100644 --- a/CodeWalker.Core/GameFiles/Resources/Nav.cs +++ b/CodeWalker.Core/GameFiles/Resources/Nav.cs @@ -30,6 +30,7 @@ using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; namespace CodeWalker.GameFiles { @@ -44,12 +45,12 @@ namespace CodeWalker.GameFiles public NavMeshFlags ContentFlags { get; set; } - public uint VersionUnk1 { get; set; } // 0x00010011 + public uint VersionUnk1 { get; set; } = 0x00010011; // 0x00010011 public uint Unused_018h { get; set; } // 0x00000000 public uint Unused_01Ch { get; set; } // 0x00000000 public Matrix Transform { get; set; } //(1,0,0,NaN),(0,1,0,NaN),(0,0,1,NaN),(0,0,0,NaN) public Vector3 AABBSize { get; set; } - public float AABBUnk { get; set; } // 0x7F800001 //NaN + public uint AABBUnk { get; set; } = 0x7F800001; // 0x7F800001 //NaN public ulong VerticesPointer { get; set; } public uint Unused_078h { get; set; } // 0x00000000 public uint Unused_07Ch { get; set; } // 0x00000000 @@ -71,7 +72,7 @@ namespace CodeWalker.GameFiles public uint Unused_154h { get; set; } // 0x00000000 public uint Unused_158h { get; set; } // 0x00000000 public uint Unused_15Ch { get; set; } // 0x00000000 - public uint VersionUnk2 { get; set; } //2244687201 (0x85CB3561) for grid ynv's + public MetaHash VersionUnk2 { get; set; } //2244687201 (0x85CB3561) for grid ynv's public uint Unused_164h { get; set; } // 0x00000000 public uint Unused_168h { get; set; } // 0x00000000 public uint Unused_16Ch { get; set; } // 0x00000000 @@ -92,6 +93,37 @@ namespace CodeWalker.GameFiles private ResourceSystemStructBlock PortalLinksBlock = null; + public Vector3 AABBMin + { + get + { + if (SectorTree != null) return SectorTree.AABBMin.XYZ(); + return AABBSize * -0.5f;//shouldn't get here + } + set + { + if (SectorTree != null) + { + SectorTree.AABBMin = new Vector4(value, 0.0f); + } + } + } + public Vector3 AABBMax + { + get + { + if (SectorTree != null) return SectorTree.AABBMax.XYZ(); + return AABBSize * 0.5f;//shouldn't get here + } + set + { + if (SectorTree != null) + { + SectorTree.AABBMax = new Vector4(value, 0.0f); + } + } + } + public override void Read(ResourceDataReader reader, params object[] parameters) @@ -104,7 +136,7 @@ namespace CodeWalker.GameFiles Unused_01Ch = reader.ReadUInt32(); Transform = reader.ReadMatrix(); AABBSize = reader.ReadVector3(); - AABBUnk = reader.ReadSingle(); + AABBUnk = reader.ReadUInt32(); VerticesPointer = reader.ReadUInt64(); Unused_078h = reader.ReadUInt32(); Unused_07Ch = reader.ReadUInt32(); @@ -142,6 +174,43 @@ namespace CodeWalker.GameFiles PortalLinks = reader.ReadUshortsAt(PortalLinksPointer, PortalLinksCount); + + + ////testing! + //if (VersionUnk1 != 0x00010011) + //{ } + //if (Unused_018h != 0) + //{ } + //if (Unused_01Ch != 0) + //{ } + //if (AABBUnk != 0x7F800001) + //{ } + //if (Unused_078h != 0) + //{ } + //if (Unused_07Ch != 0) + //{ } + //if (Unused_154h != 0) + //{ } + //if (Unused_158h != 0) + //{ } + //if (Unused_15Ch != 0) + //{ } + //if (Unused_164h != 0) + //{ } + //if (Unused_168h != 0) + //{ } + //if (Unused_16Ch != 0) + //{ } + //switch (VersionUnk2.Hash) + //{ + // case 0: //vehicle + // break; + // case 0x85CB3561: //grid + // break; + // default: + // break; + //} + //UpdateCounts(); } public override void Write(ResourceDataWriter writer, params object[] parameters) @@ -157,38 +226,7 @@ namespace CodeWalker.GameFiles PortalLinksPointer = (ulong)(PortalLinksBlock?.FilePosition ?? 0); - - //uint totbytes = 0; - //Stack sectorstack = new Stack(); - //if (SectorTree != null) sectorstack.Push(SectorTree); - //while (sectorstack.Count > 0) - //{ - // var sector = sectorstack.Pop(); - // if (sector.SubTree1 != null) sectorstack.Push(sector.SubTree1); - // if (sector.SubTree2 != null) sectorstack.Push(sector.SubTree2); - // if (sector.SubTree3 != null) sectorstack.Push(sector.SubTree3); - // if (sector.SubTree4 != null) sectorstack.Push(sector.SubTree4); - // if (sector.Data != null) - // { - // var sdata = sector.Data; - // totbytes += (uint)(sdata.PolyIDsBlock?.BlockLength ?? 0); - // totbytes += (uint)(sdata.PointsBlock?.BlockLength ?? 0); - // } - //} - //totbytes += PadSize(VerticesCount * (uint)Vertices.ItemSize); - //totbytes += PadSize(EdgesIndicesCount * (uint)Indices.ItemSize); - //totbytes += PadSize(EdgesIndicesCount * (uint)Edges.ItemSize); - //totbytes += PadSize(PolysCount * (uint)Polys.ItemSize); - ////totbytes += (uint)BlockLength; - //totbytes += (uint)Vertices.ListParts.BlockLength;//Vertices.ListPartsCount * 16; - //totbytes += (uint)Indices.ListParts.BlockLength;//Indices.ListPartsCount * 16; - //totbytes += (uint)Edges.ListParts.BlockLength;//Edges.ListPartsCount * 16; - //totbytes += (uint)Polys.ListParts.BlockLength;//Polys.ListPartsCount * 16; - //totbytes += (uint)(PortalsBlock?.BlockLength ?? 0);//PortalsCount * 28; - //totbytes += (uint)(PortalLinksBlock?.BlockLength ?? 0);//PortalLinksCount * 2; - //int remaining = ((int)TotalBytes) - ((int)totbytes); - //if (totbytes != TotalBytes) - //{ } + UpdateCounts(); writer.Write((uint)ContentFlags); @@ -225,13 +263,6 @@ namespace CodeWalker.GameFiles writer.Write(Unused_16Ch); } - private uint PadSize(uint s) - { - const uint align = 16; - if ((s % align) != 0) s += (align - (s % align)); - return s; - } - public override IResourceBlock[] GetReferences() { var list = new List(base.GetReferences()); @@ -261,6 +292,54 @@ namespace CodeWalker.GameFiles + + + public void UpdateCounts() + { + EdgesIndicesCount = Indices?.ItemCount ?? 0; + VerticesCount = Vertices?.ItemCount ?? 0; + PolysCount = Polys?.ItemCount ?? 0; + PortalsCount = (uint)(Portals?.Length ?? 0); + PortalLinksCount = (uint)(PortalLinks?.Length ?? 0); + + + + uint totbytes = 0; + uint pointcount = 0; + var treestack = new Stack(); + if (SectorTree != null) + { + treestack.Push(SectorTree); + } + while (treestack.Count > 0) + { + var sector = treestack.Pop(); + totbytes += sector.ByteCount; + pointcount += sector.PointCount; + if (sector.SubTree1 != null) treestack.Push(sector.SubTree1); + if (sector.SubTree2 != null) treestack.Push(sector.SubTree2); + if (sector.SubTree3 != null) treestack.Push(sector.SubTree3); + if (sector.SubTree4 != null) treestack.Push(sector.SubTree4); + } + + totbytes += Vertices?.ByteCount ?? 0; + totbytes += Indices?.ByteCount ?? 0; + totbytes += Edges?.ByteCount ?? 0; + totbytes += Polys?.ByteCount ?? 0; + totbytes += PortalsCount * 28; + if ((TotalBytes != totbytes) && (TotalBytes != 0)) + { } + TotalBytes = totbytes; + + + + if ((PointsCount != pointcount) && (PointsCount != 0)) + { } + PointsCount = pointcount; + } + + + public void SetDefaults(bool vehicle) { VersionUnk1 = 0x00010011; @@ -449,6 +528,16 @@ namespace CodeWalker.GameFiles private ResourceSystemStructBlock ListOffsetsBlock = null; public int ItemSize { get { return System.Runtime.InteropServices.Marshal.SizeOf(); } } + public uint ByteCount + { + get + { + return ItemCount * (uint)ItemSize; + } + } + + + public override void Read(ResourceDataReader reader, params object[] parameters) { VFT = reader.ReadUInt32(); @@ -635,9 +724,9 @@ namespace CodeWalker.GameFiles public void FromVector3(Vector3 v) { const float usmax = ushort.MaxValue; - X = (ushort)(v.X * usmax); - Y = (ushort)(v.Y * usmax); - Z = (ushort)(v.Z * usmax); + X = (ushort)Math.Round(v.X * usmax); + Y = (ushort)Math.Round(v.Y * usmax); + Z = (ushort)Math.Round(v.Z * usmax); } public static NavMeshVertex Create(Vector3 v) @@ -665,12 +754,12 @@ namespace CodeWalker.GameFiles public Vector3 Min { get { return new Vector3(MinX / 4.0f, MinY / 4.0f, MinZ / 4.0f); } - set { var v = value * 4.0f; MinX = (short)v.X; MinY = (short)v.Y; MinZ = (short)v.Z; } + set { var v = value * 4.0f; MinX = (short)Math.Floor(v.X); MinY = (short)Math.Floor(v.Y); MinZ = (short)Math.Floor(v.Z); } } public Vector3 Max { get { return new Vector3(MaxX / 4.0f, MaxY / 4.0f, MaxZ / 4.0f); } - set { var v = value * 4.0f; MaxX = (short)v.X; MaxY = (short)v.Y; MaxZ = (short)v.Z; } + set { var v = value * 4.0f; MaxX = (short)Math.Ceiling(v.X); MaxY = (short)Math.Ceiling(v.Y); MaxZ = (short)Math.Ceiling(v.Z); } } public override string ToString() @@ -726,17 +815,17 @@ namespace CodeWalker.GameFiles [TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshPoly { - public ushort Unknown_00h { get; set; } + public ushort PolyFlags0 { get; set; } public ushort IndexFlags { get; set; } public ushort IndexID { get; set; } - public ushort AreaID { get; set; } + public ushort AreaID { get; set; } //always current ynv's AreaID public uint Unused_08h { get; set; } // 0x00000000 public uint Unused_0Ch { get; set; } // 0x00000000 public uint Unused_10h { get; set; } // 0x00000000 public uint Unused_14h { get; set; } // 0x00000000 public NavMeshAABB CellAABB { get; set; } - public FlagsUint Unknown_24h { get; set; } - public FlagsUint Unknown_28h { get; set; } + public uint PolyFlags1 { get; set; } + public uint PolyFlags2 { get; set; } public uint PartFlags { get; set; } @@ -749,22 +838,29 @@ namespace CodeWalker.GameFiles public uint PortalLinkID { get { return ((PartFlags >> 15) & 0x1FFFF); } set { PartFlags = ((PartFlags & 0x7FFF) | ((value & 0x1FFFF) << 15)); } } - public ushort Unknown_28h_16 { get { return (ushort)((Unknown_28h.Value & 0xFFFF)); } set { Unknown_28h = (Unknown_28h.Value & 0xFFFF0000) | (value & 0xFFFFu); } } - public byte Unknown_28h_8a { get { return (byte)((Unknown_28h.Value >> 0) & 0xFF); } set { Unknown_28h = (Unknown_28h.Value & 0xFFFFFF00) | ((value & 0xFFu)<<0); } } - public byte Unknown_28h_8b { get { return (byte)((Unknown_28h.Value >> 8) & 0xFF); } set { Unknown_28h = (Unknown_28h.Value & 0xFFFF00FF) | ((value & 0xFFu)<<8); } } + public byte UnkX { get { return (byte)((PolyFlags2 >> 0) & 0xFF); } set { PolyFlags2 = (PolyFlags2 & 0xFFFFFF00) | ((value & 0xFFu)<<0); } } + public byte UnkY { get { return (byte)((PolyFlags2 >> 8) & 0xFF); } set { PolyFlags2 = (PolyFlags2 & 0xFFFF00FF) | ((value & 0xFFu)<<8); } } + + public byte Flags1 { get { return (byte)(PolyFlags0 & 0xFF); } set { PolyFlags0 = (ushort)((PolyFlags0 & 0xFF00) | (value & 0xFF)); } } + public byte Flags2 { get { return (byte)((PolyFlags1 >> 0) & 0xFF); } set { PolyFlags1 = ((PolyFlags1 & 0xFFFFFF00u) | ((value & 0xFFu) << 0)); } } + public byte Flags3 { get { return (byte)((PolyFlags1 >> 9) & 0xFF); } set { PolyFlags1 = ((PolyFlags1 & 0xFFFE01FFu) | ((value & 0xFFu) << 9)); } } + public byte Flags4 { get { return (byte)((PolyFlags2 >> 16) & 0xFF); } set { PolyFlags2 = ((PolyFlags2 & 0xFF00FFFFu) | ((value & 0xFFu) << 16)); } } + + //public uint UnkFlags0 { get { return (uint)((PolyFlags0 >> 8) & 0xFF); } } //always 0 + //public uint UnkFlags1 { get { return (uint)((PolyFlags1 >> 17) & 0xFFFF); } } //always 0 + //public uint UnkFlags2 { get { return (uint)((PolyFlags2 >> 24) & 0xFF); } } //always 0 public override string ToString() { return - //Unknown_28h.Bin + ", (" + Unknown_28h_8a.ToString() + ", " + Unknown_28h_8b.ToString() + "), " + - Unknown_00h.ToString() + ", " + + PolyFlags0.ToString() + ", " + //IndexFlags.ToString() + ", " + IndexCount.ToString() + ", " + //IndexUnk.ToString() + ", " + IndexID.ToString() + ", " + AreaID.ToString() + ", " + CellAABB.ToString() + ", " + - Unknown_24h.Hex + ", " + - Unknown_28h.Hex + ", " + + //PolyFlags1.ToString() + ", " + + //PolyFlags2.ToString() + ", " + //PartFlags.ToString() + ", " + //PartUnk1.ToString() + ", " + PartID.ToString() + ", " + PortalLinkCount.ToString() + ", " + @@ -799,6 +895,29 @@ namespace CodeWalker.GameFiles public NavMeshSector SubTree3 { get; set; } public NavMeshSector SubTree4 { get; set; } + public uint ByteCount + { + get + { + uint totbytes = (uint)BlockLength; + if (Data != null) + { + totbytes += ((uint)Data.BlockLength); + totbytes += ((uint)Data.PolyIDsCount * 2); + totbytes += ((uint)Data.PointsCount * 8); + } + return totbytes; + } + } + public uint PointCount + { + get + { + if (Data == null) return 0; + return Data.PointsCount; + } + } + public override void Read(ResourceDataReader reader, params object[] parameters) { AABBMin = reader.ReadVector4(); @@ -951,7 +1070,7 @@ namespace CodeWalker.GameFiles public ushort Y { get; set; } public ushort Z { get; set; } public byte Angle { get; set; } - public byte Type { get; set; } + public byte Type { get; set; }//0,1,2,3,4,5,128,171,254 public Vector3 Position @@ -964,9 +1083,9 @@ namespace CodeWalker.GameFiles set { const float usmax = ushort.MaxValue; - X = (ushort)(value.X * usmax); - Y = (ushort)(value.Y * usmax); - Z = (ushort)(value.Z * usmax); + X = (ushort)Math.Round(value.X * usmax); + Y = (ushort)Math.Round(value.Y * usmax); + Z = (ushort)Math.Round(value.Z * usmax); } } @@ -982,20 +1101,20 @@ namespace CodeWalker.GameFiles [TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshPortal { - public byte Type { get; set; } + public byte Type { get; set; }//1,2,3 public byte Angle { get; set; } - public ushort FlagsUnk { get; set; } + public ushort FlagsUnk { get; set; }//always 0 public NavMeshVertex PositionFrom { get; set; } public NavMeshVertex PositionTo { get; set; } public ushort PolyIDFrom1 { get; set; } - public ushort PolyIDFrom2 { get; set; } + public ushort PolyIDFrom2 { get; set; } //always same as PolyIDFrom1 public ushort PolyIDTo1 { get; set; } - public ushort PolyIDTo2 { get; set; } + public ushort PolyIDTo2 { get; set; } //always same as PolyIDTo1 public uint AreaFlags { get; set; } - public ushort AreaIDFrom { get { return (ushort)(AreaFlags & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xFFFFC000) | (value & 0x3FFFu); } } - public ushort AreaIDTo { get { return (ushort)((AreaFlags >> 14) & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xF0003FFF) | ((value & 0x3FFFu) << 14); } } - public byte AreaUnk { get { return (byte)((AreaFlags >> 28) & 0xF); } set { AreaFlags = (AreaFlags & 0x0FFFFFFF) | ((value & 0xFu) << 28); } } + public ushort AreaIDFrom { get { return (ushort)(AreaFlags & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xFFFFC000) | (value & 0x3FFFu); } }//always Ynv.AreaID + public ushort AreaIDTo { get { return (ushort)((AreaFlags >> 14) & 0x3FFF); } set { AreaFlags = (AreaFlags & 0xF0003FFF) | ((value & 0x3FFFu) << 14); } }//always Ynv.AreaID + public byte AreaUnk { get { return (byte)((AreaFlags >> 28) & 0xF); } set { AreaFlags = (AreaFlags & 0x0FFFFFFF) | ((value & 0xFu) << 28); } }//always 0 public override string ToString() { @@ -1013,7 +1132,7 @@ namespace CodeWalker.GameFiles [Flags] public enum NavMeshFlags : uint { None = 0, - Vertices = 1, + Polygons = 1, Portals = 2, Vehicle = 4, Unknown8 = 8, diff --git a/CodeWalker/ExploreForm.cs b/CodeWalker/ExploreForm.cs index f8c18f2..c3e5d15 100644 --- a/CodeWalker/ExploreForm.cs +++ b/CodeWalker/ExploreForm.cs @@ -274,7 +274,7 @@ namespace CodeWalker InitFileType(".pso", "Metadata (PSO)", 6, FileTypeAction.ViewJPso, true); InitFileType(".gfx", "Scaleform Flash", 7); InitFileType(".ynd", "Path Nodes", 8, FileTypeAction.ViewYnd, true); - InitFileType(".ynv", "Nav Mesh", 9, FileTypeAction.ViewModel); + InitFileType(".ynv", "Nav Mesh", 9, FileTypeAction.ViewModel, true); InitFileType(".yvr", "Vehicle Record", 9, FileTypeAction.ViewYvr); InitFileType(".ywr", "Waypoint Record", 9, FileTypeAction.ViewYwr); InitFileType(".fxc", "Compiled Shaders", 9, FileTypeAction.ViewFxc); @@ -2603,6 +2603,10 @@ namespace CodeWalker { mformat = MetaFormat.Ynd; } + if (fnamel.EndsWith(".ynv.xml")) + { + mformat = MetaFormat.Ynv; + } if (fnamel.EndsWith(".ycd.xml")) { mformat = MetaFormat.Ycd; @@ -2711,6 +2715,17 @@ namespace CodeWalker data = ynd.Save(); break; } + case MetaFormat.Ynv: + { + var ynv = XmlYnv.GetYnv(doc); + if (ynv.Nav == null) + { + MessageBox.Show(fname + ": Schema not supported.", "Cannot import YNV XML"); + continue; + } + data = ynv.Save(); + break; + } case MetaFormat.Ycd: { var ycd = XmlYcd.GetYcd(doc); diff --git a/CodeWalker/Project/Panels/EditYnvPanel.Designer.cs b/CodeWalker/Project/Panels/EditYnvPanel.Designer.cs index e98b59f..8bcea4d 100644 --- a/CodeWalker/Project/Panels/EditYnvPanel.Designer.cs +++ b/CodeWalker/Project/Panels/EditYnvPanel.Designer.cs @@ -47,7 +47,7 @@ this.YnvFlagsUnknownCheckBox = new System.Windows.Forms.CheckBox(); this.YnvFlagsVehicleCheckBox = new System.Windows.Forms.CheckBox(); this.YnvFlagsPortalsCheckBox = new System.Windows.Forms.CheckBox(); - this.YnvFlagsVerticesCheckBox = new System.Windows.Forms.CheckBox(); + this.YnvFlagsPolygonsCheckBox = new System.Windows.Forms.CheckBox(); this.YnvVersionUnkHashTextBox = new System.Windows.Forms.TextBox(); this.label1 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); @@ -200,7 +200,7 @@ this.YnvFlagsGroupBox.Controls.Add(this.YnvFlagsUnknownCheckBox); this.YnvFlagsGroupBox.Controls.Add(this.YnvFlagsVehicleCheckBox); this.YnvFlagsGroupBox.Controls.Add(this.YnvFlagsPortalsCheckBox); - this.YnvFlagsGroupBox.Controls.Add(this.YnvFlagsVerticesCheckBox); + this.YnvFlagsGroupBox.Controls.Add(this.YnvFlagsPolygonsCheckBox); this.YnvFlagsGroupBox.Location = new System.Drawing.Point(247, 48); this.YnvFlagsGroupBox.Name = "YnvFlagsGroupBox"; this.YnvFlagsGroupBox.Size = new System.Drawing.Size(103, 115); @@ -241,16 +241,16 @@ this.YnvFlagsPortalsCheckBox.UseVisualStyleBackColor = true; this.YnvFlagsPortalsCheckBox.CheckedChanged += new System.EventHandler(this.YnvFlagsPortalsCheckBox_CheckedChanged); // - // YnvFlagsVerticesCheckBox + // YnvFlagsPolygonsCheckBox // - this.YnvFlagsVerticesCheckBox.AutoSize = true; - this.YnvFlagsVerticesCheckBox.Location = new System.Drawing.Point(12, 19); - this.YnvFlagsVerticesCheckBox.Name = "YnvFlagsVerticesCheckBox"; - this.YnvFlagsVerticesCheckBox.Size = new System.Drawing.Size(64, 17); - this.YnvFlagsVerticesCheckBox.TabIndex = 0; - this.YnvFlagsVerticesCheckBox.Text = "Vertices"; - this.YnvFlagsVerticesCheckBox.UseVisualStyleBackColor = true; - this.YnvFlagsVerticesCheckBox.CheckedChanged += new System.EventHandler(this.YnvFlagsVerticesCheckBox_CheckedChanged); + this.YnvFlagsPolygonsCheckBox.AutoSize = true; + this.YnvFlagsPolygonsCheckBox.Location = new System.Drawing.Point(12, 19); + this.YnvFlagsPolygonsCheckBox.Name = "YnvFlagsPolygonsCheckBox"; + this.YnvFlagsPolygonsCheckBox.Size = new System.Drawing.Size(69, 17); + this.YnvFlagsPolygonsCheckBox.TabIndex = 0; + this.YnvFlagsPolygonsCheckBox.Text = "Polygons"; + this.YnvFlagsPolygonsCheckBox.UseVisualStyleBackColor = true; + this.YnvFlagsPolygonsCheckBox.CheckedChanged += new System.EventHandler(this.YnvFlagsVerticesCheckBox_CheckedChanged); // // YnvVersionUnkHashTextBox // @@ -402,7 +402,7 @@ private System.Windows.Forms.CheckBox YnvFlagsUnknownCheckBox; private System.Windows.Forms.CheckBox YnvFlagsVehicleCheckBox; private System.Windows.Forms.CheckBox YnvFlagsPortalsCheckBox; - private System.Windows.Forms.CheckBox YnvFlagsVerticesCheckBox; + private System.Windows.Forms.CheckBox YnvFlagsPolygonsCheckBox; private System.Windows.Forms.TextBox YnvVersionUnkHashTextBox; private System.Windows.Forms.Label label1; private System.Windows.Forms.Label label2; diff --git a/CodeWalker/Project/Panels/EditYnvPanel.cs b/CodeWalker/Project/Panels/EditYnvPanel.cs index 9d63133..0612f8f 100644 --- a/CodeWalker/Project/Panels/EditYnvPanel.cs +++ b/CodeWalker/Project/Panels/EditYnvPanel.cs @@ -87,7 +87,7 @@ namespace CodeWalker.Project.Panels YnvAreaIDYUpDown.Value = Ynv.CellY; YnvAreaIDInfoLabel.Text = "ID: " + Ynv.AreaID.ToString(); YnvAABBSizeTextBox.Text = FloatUtil.GetVector3String(nv.AABBSize); - YnvFlagsVerticesCheckBox.Checked = nv.ContentFlags.HasFlag(NavMeshFlags.Vertices); + YnvFlagsPolygonsCheckBox.Checked = nv.ContentFlags.HasFlag(NavMeshFlags.Polygons); YnvFlagsPortalsCheckBox.Checked = nv.ContentFlags.HasFlag(NavMeshFlags.Portals); YnvFlagsVehicleCheckBox.Checked = nv.ContentFlags.HasFlag(NavMeshFlags.Vehicle); YnvFlagsUnknownCheckBox.Checked = nv.ContentFlags.HasFlag(NavMeshFlags.Unknown8); @@ -97,7 +97,7 @@ namespace CodeWalker.Project.Panels YnvPortalLinkCountLabel.Text = "Portal link count: " + nv.PortalLinksCount.ToString(); YnvPointCountLabel.Text = "Point count: " + nv.PointsCount.ToString(); YnvByteCountLabel.Text = "Byte count: " + nv.TotalBytes.ToString(); - YnvVersionUnkHashTextBox.Text = nv.VersionUnk2.ToString(); + YnvVersionUnkHashTextBox.Text = nv.VersionUnk2.Hash.ToString(); YnvAdjAreaIDsTextBox.Text = GetAdjAreaIDsString(nv.AdjAreaIDs.Values); populatingui = false; } @@ -154,7 +154,7 @@ namespace CodeWalker.Project.Panels { if (populatingui) return; if (Ynv?.Nav == null) return; - var verts = YnvFlagsVerticesCheckBox.Checked ? NavMeshFlags.Vertices : NavMeshFlags.None; + var verts = YnvFlagsPolygonsCheckBox.Checked ? NavMeshFlags.Polygons : NavMeshFlags.None; var ports = YnvFlagsPortalsCheckBox.Checked ? NavMeshFlags.Portals : NavMeshFlags.None; var vehcs = YnvFlagsVehicleCheckBox.Checked ? NavMeshFlags.Vehicle : NavMeshFlags.None; var unk8s = YnvFlagsUnknownCheckBox.Checked ? NavMeshFlags.Unknown8 : NavMeshFlags.None;