From bab1bee46097b8d07bcf50b13f1301b4b7b3f0aa Mon Sep 17 00:00:00 2001 From: dexyfex Date: Sat, 5 May 2018 01:58:23 +1000 Subject: [PATCH] Nav mesh progress --- CodeWalker.Core/CodeWalker.Core.csproj | 2 + .../FileTypes/Builders/YndBuilder.cs | 12 + .../FileTypes/Builders/YnvBuilder.cs | 443 ++++++++++++++++++ .../GameFiles/FileTypes/YnvFile.cs | 174 ++++++- CodeWalker.Core/GameFiles/Resources/Nav.cs | 116 ++++- CodeWalker.Core/World/Space.cs | 15 +- 6 files changed, 738 insertions(+), 24 deletions(-) create mode 100644 CodeWalker.Core/GameFiles/FileTypes/Builders/YndBuilder.cs create mode 100644 CodeWalker.Core/GameFiles/FileTypes/Builders/YnvBuilder.cs diff --git a/CodeWalker.Core/CodeWalker.Core.csproj b/CodeWalker.Core/CodeWalker.Core.csproj index 943760a..e613082 100644 --- a/CodeWalker.Core/CodeWalker.Core.csproj +++ b/CodeWalker.Core/CodeWalker.Core.csproj @@ -48,6 +48,8 @@ + + diff --git a/CodeWalker.Core/GameFiles/FileTypes/Builders/YndBuilder.cs b/CodeWalker.Core/GameFiles/FileTypes/Builders/YndBuilder.cs new file mode 100644 index 0000000..fba7708 --- /dev/null +++ b/CodeWalker.Core/GameFiles/FileTypes/Builders/YndBuilder.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeWalker.Core.GameFiles.FileTypes.Builders +{ + public class YndBuilder + { + } +} diff --git a/CodeWalker.Core/GameFiles/FileTypes/Builders/YnvBuilder.cs b/CodeWalker.Core/GameFiles/FileTypes/Builders/YnvBuilder.cs new file mode 100644 index 0000000..1173ae4 --- /dev/null +++ b/CodeWalker.Core/GameFiles/FileTypes/Builders/YnvBuilder.cs @@ -0,0 +1,443 @@ +using CodeWalker.GameFiles; +using CodeWalker.World; +using SharpDX; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CodeWalker.Core.GameFiles.FileTypes.Builders +{ + public class YnvBuilder + { + + + private List PolyList = new List(); + + private SpaceNavGrid NavGrid = null; + private List YnvFiles = null; + + public YnvPoly AddPoly(Vector3[] verts) + { + if ((verts == null) || (verts.Length < 3)) + { return null; } + + YnvPoly poly = new YnvPoly(); + poly.AreaID = 0x3FFF; + poly.Index = PolyList.Count; + poly.Vertices = verts; + + PolyList.Add(poly); + + return poly; + } + + + + + + + public List Build(bool forVehicle) + { + NavGrid = new SpaceNavGrid(); + YnvFiles = new List(); + + if (forVehicle) //for vehicle YNV, only need a single ynv, no splitting + { + //TODO! + } + else //for static world ynv, need to split polys and generate a set of ynv's. + { + //1: split polys going over nav grid borders, first by X then by Y + var splitpolysX = SplitPolys(PolyList, true); + var splitpolysY = SplitPolys(splitpolysX, false); + + //2: assign polys into their new ynv's + AddPolysIntoGrid(splitpolysY); + + + //3: fix up generated ynv's + FinalizeYnvs(YnvFiles); + + } + + return YnvFiles; + } + + + + + + private List SplitPolys(List polys, bool xaxis) + { + var newpolys = new List(); + + var verts1 = new List(); + var verts2 = new List(); + var edges1 = new List(); + var edges2 = new List(); + + var polysplits = new Dictionary(); + + foreach (var poly in polys) //split along borders + { + var verts = poly.Vertices; + if (verts == null) + { continue; }//ignore empty polys.. + if (verts.Length < 3) + { continue; }//not enough verts for a triangle! + + Vector2I gprev = NavGrid.GetCellPos(verts[0]); + int split1 = 0; + int split2 = 0; + for (int i = 1; i < verts.Length; i++) + { + Vector2I g = NavGrid.GetCellPos(verts[i]); + int g1 = xaxis ? g.X : g.Y; + int g2 = xaxis ? gprev.X : gprev.Y; + if (g1 != g2) //this poly is crossing a border + { + if (split1 == 0) { split1 = i; } + else { split2 = i; break; } + } + gprev = g; + } + if (split1 > 0) + { + var split2beg = (split2 > 0) ? split2 - 1 : verts.Length - 1; + var split2end = split2beg + 1; + var sv11 = verts[split1 - 1]; + var sv12 = verts[split1]; + var sv21 = verts[split2beg]; + var sv22 = verts[split2]; + var sp1 = GetSplitPos(sv11, sv12, xaxis); + var sp2 = GetSplitPos(sv21, sv22, xaxis); + + //if ((sp1 == sp2) || (sp1 == sv11) || (sp1 == sv12) || (sp2 == sv21) || (sp2 == sv22)) + if (!IsValidSplit(sp1, sp2, sv11, sv12, sv21, sv22)) + { + //split did nothing, just leave this poly alone + newpolys.Add(poly); + } + else + { + //split it! + var poly1 = new YnvPoly(); + var poly2 = new YnvPoly(); + poly1.RawData = poly.RawData; + poly2.RawData = poly.RawData; + verts1.Clear(); + verts2.Clear(); + + for (int i = 0; i < split1; i++) verts1.Add(verts[i]); + verts1.Add(sp1); + verts1.Add(sp2); + for (int i = split2end; i < verts.Length; i++) verts1.Add(verts[i]); + + verts2.Add(sp1); + for (int i = split1; i < split2end; i++) verts2.Add(verts[i]); + verts2.Add(sp2); + + poly1.Vertices = verts1.ToArray(); + poly2.Vertices = verts2.ToArray(); + + + //save this information for the edge splitting pass + var polysplit = new YnvPolySplit(); + polysplit.Orig = poly; + polysplit.New1 = poly1; + polysplit.New2 = poly2; + polysplit.Split1 = split1; + polysplit.Split2 = split2end; + polysplits[poly] = polysplit; + + + newpolys.Add(poly1); + newpolys.Add(poly2); + } + } + else + { + //no need to split + newpolys.Add(poly); + } + } + + + foreach (var polysplit in polysplits.Values) //build new edges for split polys + { + //the two edges that were split each need to be turned into two new edges (1 for each poly). + //also, the split itself needs to be added as a new edge to the original poly. + + var poly = polysplit.Orig; + var poly1 = polysplit.New1; + var poly2 = polysplit.New2; + var edges = poly.Edges; + var verts = poly.Vertices; + var ec = edges?.Length ?? 0; + if (ec <= 0) + { continue; }//shouldn't happen - no edges? + if (ec != poly.Vertices?.Length) + { continue; }//shouldn't happen + + var split1beg = polysplit.Split1 - 1; + var split1end = polysplit.Split1; + var split2beg = polysplit.Split2 - 1; + var split2end = polysplit.Split2; + + edges1.Clear(); + edges2.Clear(); + + var se1 = edges[split1beg]; //the two original edges that got split + var se2 = edges[split2beg]; + var sp1 = TryGetSplit(polysplits, se1.Poly1);//could use Poly2, but they should be the same.. + var sp2 = TryGetSplit(polysplits, se2.Poly1); + var sv1a = verts[split1beg]; + var sv2a = verts[split2beg]; + var sp1a = sp1?.GetNearest(sv1a); + var sp1b = sp1?.GetOther(sp1a); + var sp2b = sp2?.GetNearest(sv2a); + var sp2a = sp2?.GetOther(sp2b); + var edge1a = new YnvEdge(se1, sp1a); + var edge1b = new YnvEdge(se1, sp1b); + var edge2a = new YnvEdge(se2, sp2a); + var edge2b = new YnvEdge(se2, sp2b); + var splita = new YnvEdge(se1, poly2); + var splitb = new YnvEdge(se1, poly1); + + for (int i = 0; i < split1beg; i++) edges1.Add(edges[i]);//untouched edges + edges1.Add(edge1a); + edges1.Add(splita); + edges1.Add(edge2a); + for (int i = split2end; i < ec; i++) edges1.Add(edges[i]);//untouched edges + + edges2.Add(edge1b); + for (int i = split1end; i < split2beg; i++) edges2.Add(edges[i]);//untouched edges + edges2.Add(edge2b); + edges2.Add(splitb); + + + poly1.Edges = edges1.ToArray(); + poly2.Edges = edges2.ToArray(); + + if (poly1.Edges.Length != poly1.Vertices.Length) + { }//debug + if (poly2.Edges.Length != poly2.Vertices.Length) + { }//debug + + } + + foreach (var poly in newpolys) //fix any untouched edges that joined to split polys + { + if (poly.Edges?.Length != poly.Vertices?.Length) + { continue; }//shouldn't happen (no edges?) + for (int i = 0; i < poly.Edges.Length; i++) + { + var edge = poly.Edges[i]; + var vert = poly.Vertices[i]; + if (edge == null) + { continue; }//shouldn't happen + if (edge.Poly1 != edge.Poly2) + { continue; }//shouldn't happen? + if (edge.Poly1 == null) + { continue; }//probably this edge joins to nothing + + + YnvPolySplit polysplit; + if (polysplits.TryGetValue(edge.Poly1, out polysplit)) + { + var newpoly = polysplit.GetNearest(vert); + if (newpoly == null) + { }//debug + edge.Poly1 = newpoly; + edge.Poly2 = newpoly; + } + + } + } + + + return newpolys; + } + + private Vector3 GetSplitPos(Vector3 a, Vector3 b, bool xaxis) + { + Vector3 ca = NavGrid.GetCellRel(a); + Vector3 cb = NavGrid.GetCellRel(b); + float fa = xaxis ? ca.X : ca.Y; + float fb = xaxis ? cb.X : cb.Y; + float f = 0; + if (fb > fa) + { + float ib = (float)Math.Floor(fb); + f = (ib - fa) / (fb - fa); + } + else + { + float ia = (float)Math.Floor(fa); + f = (fa - ia) / (fa - fb); + } + if (f < 0.0f) + { }//debug + if (f > 1.0f) + { }//debug + return a + (b - a) * Math.Min(Math.Max(f, 0.0f), 1.0f); + } + + private bool IsValidSplit(Vector3 s1, Vector3 s2, Vector3 v1a, Vector3 v1b, Vector3 v2a, Vector3 v2b) + { + if (XYEqual(s1, s2)) return false; + if (XYEqual(s1, v1a)) return false; + if (XYEqual(s1, v1b)) return false; + if (XYEqual(s2, v2a)) return false; + if (XYEqual(s2, v2b)) return false; + return true; + } + + private bool XYEqual(Vector3 v1, Vector3 v2) + { + return ((v1.X == v2.X) && (v1.Y == v2.Y)); + } + + private class YnvPolySplit + { + public YnvPoly Orig; + public YnvPoly New1; + public YnvPoly New2; + public int Split1; + public int Split2; + public YnvPoly GetNearest(Vector3 v) + { + if (New1?.Vertices == null) return New2; + if (New2?.Vertices == null) return New1; + float len1 = float.MaxValue; + float len2 = float.MaxValue; + for (int i = 0; i < New1.Vertices.Length; i++) + { + len1 = Math.Min(len1, (v - New1.Vertices[i]).LengthSquared()); + } + if (len1 == 0.0f) return New1; + for (int i = 0; i < New2.Vertices.Length; i++) + { + len2 = Math.Min(len2, (v - New2.Vertices[i]).LengthSquared()); + } + if (len2 == 0.0f) return New2; + return (len1 <= len2) ? New1 : New2; + } + public YnvPoly GetOther(YnvPoly p) + { + if (p == New1) return New2; + return New1; + } + } + private YnvPolySplit TryGetSplit(Dictionary polysplits, YnvPoly poly) + { + if (poly == null) return null; + YnvPolySplit r = null; + polysplits.TryGetValue(poly, out r); + return r; + } + + + + private void AddPolysIntoGrid(List polys) + { + foreach (var poly in polys) + { + poly.CalculatePosition(); + var pos = poly.Position; + var cell = NavGrid.GetCell(pos); + + var ynv = cell.Ynv; + if (ynv == null) + { + ynv = new YnvFile(); + ynv.Name = "navmesh[" + cell.FileX.ToString() + "][" + cell.FileY.ToString() + "]"; + ynv.Nav = new NavMesh(); + ynv.Nav.SetDefaults(false); + ynv.Nav.AABBSize = new Vector3(NavGrid.CellSize, NavGrid.CellSize, 0.0f); + ynv.Nav.SectorTree = new NavMeshSector(); + ynv.Nav.SectorTree.AABBMin = new Vector4(NavGrid.GetCellMin(cell), 0.0f); + ynv.Nav.SectorTree.AABBMax = new Vector4(NavGrid.GetCellMax(cell), 0.0f); + ynv.AreaID = cell.X + cell.Y * 100; + ynv.Polys = new List(); + cell.Ynv = ynv; + YnvFiles.Add(ynv); + } + + poly.AreaID = (ushort)ynv.AreaID; + poly.Index = ynv.Polys.Count; + poly.Ynv = ynv; + ynv.Polys.Add(poly); + + } + } + + private void FinalizeYnvs(List ynvs) + { + + foreach (var ynv in ynvs) + { + //find zmin and zmax and update AABBSize and SectorTree root + float zmin = float.MaxValue; + float zmax = float.MinValue; + foreach (var poly in ynv.Polys) + { + foreach (var vert in poly.Vertices) + { + zmin = Math.Min(zmin, vert.Z); + zmax = Math.Max(zmax, vert.Z); + } + } + var yn = ynv.Nav; + var ys = yn.SectorTree; + yn.AABBSize = new Vector3(yn.AABBSize.X, yn.AABBSize.Y, zmax - zmin); + ys.AABBMin = new Vector4(ys.AABBMin.X, ys.AABBMin.Y, zmin, 0.0f); + ys.AABBMax = new Vector4(ys.AABBMax.X, ys.AABBMax.Y, zmax, 0.0f); + + + ynv.UpdateContentFlags(false); + + + + //fix up flags on edges that cross ynv borders + foreach (var poly in ynv.Polys) + { + bool border = false; + if (poly.Edges == null) + { continue; } + foreach (var edge in poly.Edges) + { + if (edge.Poly1 != null) + { + if (edge.Poly1.AreaID != poly.AreaID) + { + //edge._RawData._Poly2.Unk3 = 4;// edge._RawData._Poly2.Unk3 | 4; + + //DEBUG don't join edges + edge.Poly1 = null; + edge.Poly2 = null; + edge.AreaID1 = 0x3FFF; + edge.AreaID2 = 0x3FFF; + edge._RawData._Poly1.PolyID = 0x3FFF; + edge._RawData._Poly2.PolyID = 0x3FFF; + edge._RawData._Poly1.Unk2 = 1; + edge._RawData._Poly2.Unk2 = 1; + edge._RawData._Poly1.Unk3 = 0; + edge._RawData._Poly2.Unk3 = 0; + + //border = true; + } + } + } + poly.B19_IsCellEdge = border; + } + + + } + + } + + } +} diff --git a/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs index f24ceb0..5118acb 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/YnvFile.cs @@ -14,7 +14,7 @@ namespace CodeWalker.GameFiles public List Vertices { get; set; } public List Indices { get; set; } - public List Edges { get; set; } + public List Edges { get; set; } public List Polys { get; set; } public List Portals { get; set; } public List Points { get; set; } @@ -103,7 +103,14 @@ namespace CodeWalker.GameFiles } if (Nav.Edges != null) { - Edges = Nav.Edges.GetFullList(); + 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) { @@ -207,9 +214,8 @@ namespace CodeWalker.GameFiles var portallinks = new List(); var vertdict = new Dictionary(); - var blankedgepart1 = new NavMeshEdgePart() { Value = 0x0FFFE1 };//1, -, 1, 0 - var blankedgepart2 = new NavMeshEdgePart() { Value = 0x2FFFE1 };//1, -, 1, 1 - var blankedge = new NavMeshEdge() { Unknown_0h = blankedgepart1, Unknown_4h = blankedgepart2 }; + var areadict = new Dictionary(); + var arealist = new List(); if (Polys != null) //rebuild vertices, indices, edges and polys lists from poly data. { @@ -217,10 +223,12 @@ namespace CodeWalker.GameFiles { var poly = Polys[i]; var vc = poly.Vertices?.Length ?? 0; + //poly.AreaID = (ushort)Nav.AreaID; + poly._RawData.IndexID = (ushort)indslist.Count; for (int n = 0; n < vc; n++) { Vector3 v = poly.Vertices[n]; - NavMeshEdge e = ((poly.Edges != null) && (n < poly.Edges.Length)) ? poly.Edges[n] : blankedge; + YnvEdge e = ((poly.Edges != null) && (n < poly.Edges.Length)) ? poly.Edges[n] : null; ushort ind; if (!vertdict.TryGetValue(v, out ind)) { @@ -233,7 +241,40 @@ namespace CodeWalker.GameFiles poly.Indices[n] = ind; } indslist.Add(ind); - edgelist.Add(e); + + NavMeshEdge edge; + if (e != null) + { + if (e.Poly1 != null) + { + e.PolyID1 = (uint)e.Poly1.Index; + e.AreaID1 = e.Poly1.AreaID; + if (e.AreaID1 == 0x3FFF) + { }//debug + } + if (e.Poly2 != null) + { + e.PolyID2 = (uint)e.Poly2.Index; + e.AreaID2 = e.Poly2.AreaID; + if (e.AreaID2 == 0x3FFF) + { }//debug + } + if ((e.AreaID1 == 0) || (e.AreaID2 == 0)) + { }//debug + e._RawData._Poly1.AreaIDInd = EnsureEdgeAreaID(e.AreaID1, areadict, arealist); + e._RawData._Poly2.AreaIDInd = EnsureEdgeAreaID(e.AreaID2, areadict, arealist); + edge = e.RawData; + } + else + { + var areaind = EnsureEdgeAreaID(0x3FFF, areadict, arealist); + edge = new NavMeshEdge();//create an empty edge + edge._Poly1.PolyID = 0x3FFF; + edge._Poly2.PolyID = 0x3FFF; + edge._Poly1.AreaIDInd = areaind; + edge._Poly2.AreaIDInd = areaind; + } + edgelist.Add(edge); } poly._RawData.IndexCount = vc; poly._RawData.PortalLinkID = (uint)portallinks.Count;//these shouldn't be directly editable! @@ -243,6 +284,7 @@ namespace CodeWalker.GameFiles portallinks.AddRange(poly.PortalLinks); } poly.Index = i;//this should be redundant... + poly.CalculateAABB();//make sure this is up to date! polylist.Add(poly.RawData); } } @@ -293,18 +335,25 @@ namespace CodeWalker.GameFiles Nav.Vertices.RebuildList(vertlist); + Nav.VerticesCount = Nav.Vertices.ItemCount; Nav.Indices.RebuildList(indslist); Nav.Edges.RebuildList(edgelist); + Nav.EdgesIndicesCount = Nav.Indices.ItemCount; Nav.Polys.RebuildList(polylist); + Nav.PolysCount = Nav.Polys.ItemCount; Nav.Portals = (portallist.Count > 0) ? portallist.ToArray() : null; Nav.PortalsCount = (uint)(Nav.Portals?.Length ?? 0); Nav.PortalLinks = (portallinks.Count > 0) ? portallinks.ToArray() : null; Nav.PortalLinksCount = (uint)(Nav.PortalLinks?.Length ?? 0); + var adjAreaIds = new NavMeshUintArray(); + adjAreaIds.Set(arealist.ToArray()); + Nav.AdjAreaIDs = adjAreaIds; + for (int i = 0; i < Nav.Polys.ListParts.Count; i++) //reassign part id's on all the polys... { @@ -337,12 +386,27 @@ namespace CodeWalker.GameFiles } + private uint EnsureEdgeAreaID(uint areaid, Dictionary areadict, List arealist) + { + uint ind; + if (!areadict.TryGetValue(areaid, out ind)) + { + ind = (uint)arealist.Count; + areadict[areaid] = ind; + arealist.Add(areaid); + } + return ind; + } + + private void BuildSectorTree(NavMeshSector node, int depth, ref uint pointindex) { Vector3 min = node.AABBMin.XYZ(); Vector3 max = node.AABBMax.XYZ(); Vector3 cen = (min + max) * 0.5f; + //totbytes += (uint)node.BlockLength; + if (depth <= 0) { //go through polys and points and create new lists for this node @@ -351,6 +415,8 @@ namespace CodeWalker.GameFiles data.PointsStartID = pointindex; + //totbytes += (uint)data.BlockLength; + if (Polys != null) { List polyids = new List(); @@ -367,6 +433,7 @@ namespace CodeWalker.GameFiles { data.PolyIDs = polyids.ToArray(); } + //totbytes += (uint)(polyids.Count * 2); } if (Points != null) @@ -385,6 +452,7 @@ namespace CodeWalker.GameFiles data.Points = points.ToArray(); pointindex += (uint)points.Count; } + //totbytes += (uint)(points.Count * 8); } } @@ -441,6 +509,18 @@ 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() @@ -566,9 +646,9 @@ namespace CodeWalker.GameFiles [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoly { public NavMeshPoly _RawData; + public NavMeshPoly RawData { get { return _RawData; } set { _RawData = value; } } public YnvFile Ynv { get; set; } - public NavMeshPoly RawData { get { return _RawData; } set { _RawData = value; } } public ushort AreaID { get { return _RawData.AreaID; } set { _RawData.AreaID = value; } } public ushort PartID { get { return _RawData.PartID; } set { _RawData.PartID = value; } } @@ -620,7 +700,7 @@ namespace CodeWalker.GameFiles public ushort[] Indices { get; set; } public Vector3[] Vertices { get; set; } - public NavMeshEdge[] Edges { get; set; } + public YnvEdge[] Edges { get; set; } public ushort[] PortalLinks { get; set; } @@ -656,7 +736,7 @@ namespace CodeWalker.GameFiles Indices = new ushort[ic]; Vertices = new Vector3[ic]; - Edges = new NavMeshEdge[ic]; + Edges = new YnvEdge[ic]; int i = 0; for (int id = startid; id < endid; id++) @@ -781,9 +861,28 @@ namespace CodeWalker.GameFiles pcenter += Vertices[i]; } } - Position = pcenter * (1.0f / ((float)Vertices?.Length)); + float c = ((float)Vertices?.Length); + if (c == 0.0f) c = 1.0f; + Position = pcenter * (1.0f / c); } + public void CalculateAABB() + { + Vector3 min = Vector3.Zero; + Vector3 max = Vector3.Zero; + if ((Vertices != null) && (Vertices.Length > 0)) + { + min = new Vector3(float.MaxValue); + max = new Vector3(float.MinValue); + for (int i = 0; i < Vertices.Length; i++) + { + min = Vector3.Min(min, Vertices[i]); + max = Vector3.Max(max, Vertices[i]); + } + } + + _RawData.CellAABB = new NavMeshAABB() { Min = min, Max = max }; + } public override string ToString() @@ -919,4 +1018,57 @@ namespace CodeWalker.GameFiles } + + + [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvEdge + { + public NavMeshEdge _RawData; + public NavMeshEdge RawData { get { return _RawData; } set { _RawData = value; } } + public YnvFile Ynv { get; set; } + + + public uint AreaID1 { get; set; } + public uint AreaID2 { get; set; } + public uint PolyID1 { get { return _RawData._Poly1.PolyID; } set { _RawData._Poly1.PolyID = value; } } + public uint PolyID2 { get { return _RawData._Poly2.PolyID; } set { _RawData._Poly2.PolyID = value; } } + public YnvPoly Poly1 { get; set; } + public YnvPoly Poly2 { get; set; } + + + public YnvEdge() { } + public YnvEdge(YnvEdge copy, YnvPoly poly) + { + _RawData = copy._RawData; + _RawData._Poly1.PolyID = 0x3FFF; + _RawData._Poly2.PolyID = 0x3FFF; + Poly1 = poly; + Poly2 = poly; + AreaID1 = 0x3FFF; + AreaID2 = 0x3FFF; + } + + public void Init(YnvFile ynv, NavMeshEdge edge) + { + Ynv = ynv; + RawData = edge; + + if (ynv.Nav == null) return; + var n = ynv.Nav; + + var ai1 = edge.Poly1.AreaIDInd; + var ai2 = edge.Poly2.AreaIDInd; + + AreaID1 = (ai1 < n.AdjAreaIDs.Count) ? n.AdjAreaIDs.Get(ai1) : 16383; + AreaID2 = (ai2 < n.AdjAreaIDs.Count) ? n.AdjAreaIDs.Get(ai2) : 16383; + + } + + public override string ToString() + { + return AreaID1.ToString() + ", " + AreaID2.ToString() + ", " + PolyID1.ToString() + ", " + PolyID2.ToString() + ", " + + _RawData._Poly1.Unk2.ToString() + ", " + _RawData._Poly2.Unk2.ToString() + ", " + + _RawData._Poly1.Unk3.ToString() + ", " + _RawData._Poly2.Unk3.ToString(); + } + + } } diff --git a/CodeWalker.Core/GameFiles/Resources/Nav.cs b/CodeWalker.Core/GameFiles/Resources/Nav.cs index 95aa63b..cf5bd53 100644 --- a/CodeWalker.Core/GameFiles/Resources/Nav.cs +++ b/CodeWalker.Core/GameFiles/Resources/Nav.cs @@ -158,6 +158,39 @@ namespace CodeWalker.GameFiles + //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) + //{ } + + writer.Write((uint)ContentFlags); writer.Write(VersionUnk1); writer.Write(Unused_018h); @@ -192,6 +225,12 @@ 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() { @@ -221,6 +260,15 @@ namespace CodeWalker.GameFiles } + + public void SetDefaults(bool vehicle) + { + VersionUnk1 = 0x00010011; + VersionUnk2 = vehicle ? 0 : 0x85CB3561; + Transform = Matrix.Identity; + } + + public override string ToString() { return "(Size: " + FloatUtil.GetVector3String(AABBSize) + ")"; @@ -325,6 +373,50 @@ namespace CodeWalker.GameFiles } } + public uint Get(uint i) + { + switch (i) + { + default: + case 0: return v00; + case 1: return v01; + case 2: return v02; + case 3: return v03; + case 4: return v04; + case 5: return v05; + case 6: return v06; + case 7: return v07; + case 8: return v08; + case 9: return v09; + case 10: return v10; + case 11: return v11; + case 12: return v12; + case 13: return v13; + case 14: return v14; + case 15: return v15; + case 16: return v16; + case 17: return v17; + case 18: return v18; + case 19: return v19; + case 20: return v20; + case 21: return v21; + case 22: return v22; + case 23: return v23; + case 24: return v24; + case 25: return v25; + case 26: return v26; + case 27: return v27; + case 28: return v28; + case 29: return v29; + case 30: return v30; + case 31: return v31; + } + } + + public void Set(uint[] arr) + { + Values = arr; + } public override string ToString() { @@ -459,7 +551,7 @@ namespace CodeWalker.GameFiles } ListParts = parts; ListOffsets = offsets.ToArray(); - + ItemCount = (uint)items.Count; } @@ -594,13 +686,15 @@ namespace CodeWalker.GameFiles [TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshEdge { - public NavMeshEdgePart Unknown_0h { get; set; } - public NavMeshEdgePart Unknown_4h { get; set; } + public NavMeshEdgePart _Poly1; + public NavMeshEdgePart _Poly2; + public NavMeshEdgePart Poly1 { get { return _Poly1; } set { _Poly1 = value; } } + public NavMeshEdgePart Poly2 { get { return _Poly2; } set { _Poly2 = value; } } public override string ToString() { - return //Unknown_0h.Bin + " | " + Unknown_4h.Bin + " | " + - Unknown_0h.ToString() + " | " + Unknown_4h.ToString(); + return //Poly1.Bin + " | " + Poly2.Bin + " | " + + _Poly1.ToString() + " | " + _Poly2.ToString(); } } @@ -616,10 +710,10 @@ namespace CodeWalker.GameFiles } } - public uint AreaIDInd { get { return (Value >> 0) & 0x1F; } } - public uint PolyID { get { return (Value >> 5) & 0x3FFF; } } - public uint Unk2 { get { return (Value >> 19) & 0x3; } } - public uint Unk3 { get { return (Value >> 21) & 0x7FF; } } + public uint AreaIDInd { get { return (Value >> 0) & 0x1F; } set { Value = (Value & 0xFFFFFFE0) | (value & 0x1F); } } + public uint PolyID { get { return (Value >> 5) & 0x3FFF; } set { Value = (Value & 0xFFF8001F) | ((value & 0x3FFF) << 5); } } + public uint Unk2 { get { return (Value >> 19) & 0x3; } set { Value = (Value & 0xFFE7FFFF) | ((value & 0x3) << 19); } } + public uint Unk3 { get { return (Value >> 21) & 0x7FF; } set { Value = (Value & 0x001FFFFF) | ((value & 0x7FF) << 21); } } public override string ToString() { @@ -791,8 +885,8 @@ namespace CodeWalker.GameFiles public ushort[] PolyIDs { get; set; } public NavMeshPoint[] Points { get; set; } - private ResourceSystemStructBlock PolyIDsBlock = null; - private ResourceSystemStructBlock PointsBlock = null; + public ResourceSystemStructBlock PolyIDsBlock = null; + public ResourceSystemStructBlock PointsBlock = null; public override void Read(ResourceDataReader reader, params object[] parameters) { diff --git a/CodeWalker.Core/World/Space.cs b/CodeWalker.Core/World/Space.cs index 1505c0a..fbad2a2 100644 --- a/CodeWalker.Core/World/Space.cs +++ b/CodeWalker.Core/World/Space.cs @@ -2043,7 +2043,10 @@ namespace CodeWalker.World } - + public Vector3 GetCellRel(Vector3 p)//float value in cell coords + { + return (p - new Vector3(CornerX, CornerY, 0)) * CellSizeInv; + } public Vector2I GetCellPos(Vector3 p) { Vector3 ind = (p - new Vector3(CornerX, CornerY, 0)) * CellSizeInv; @@ -2069,7 +2072,15 @@ namespace CodeWalker.World } - + public Vector3 GetCellMin(SpaceNavGridCell cell) + { + Vector3 c = new Vector3(cell.X, cell.Y, 0); + return new Vector3(CornerX, CornerY, 0) + (c * CellSize); + } + public Vector3 GetCellMax(SpaceNavGridCell cell) + { + return GetCellMin(cell) + new Vector3(CellSize, CellSize, 0.0f); + } } public class SpaceNavGridCell