Nav mesh progress

This commit is contained in:
dexyfex 2018-05-05 01:58:23 +10:00
parent b18b1e7672
commit bab1bee460
6 changed files with 738 additions and 24 deletions

View File

@ -48,6 +48,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="GameFiles\FileTypes\AwcFile.cs" /> <Compile Include="GameFiles\FileTypes\AwcFile.cs" />
<Compile Include="GameFiles\FileTypes\Builders\YndBuilder.cs" />
<Compile Include="GameFiles\FileTypes\Builders\YnvBuilder.cs" />
<Compile Include="GameFiles\FileTypes\CacheDatFile.cs" /> <Compile Include="GameFiles\FileTypes\CacheDatFile.cs" />
<Compile Include="GameFiles\FileTypes\CutFile.cs" /> <Compile Include="GameFiles\FileTypes\CutFile.cs" />
<Compile Include="GameFiles\FileTypes\DlcContentFile.cs" /> <Compile Include="GameFiles\FileTypes\DlcContentFile.cs" />

View File

@ -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
{
}
}

View File

@ -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<YnvPoly> PolyList = new List<YnvPoly>();
private SpaceNavGrid NavGrid = null;
private List<YnvFile> 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<YnvFile> Build(bool forVehicle)
{
NavGrid = new SpaceNavGrid();
YnvFiles = new List<YnvFile>();
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<YnvPoly> SplitPolys(List<YnvPoly> polys, bool xaxis)
{
var newpolys = new List<YnvPoly>();
var verts1 = new List<Vector3>();
var verts2 = new List<Vector3>();
var edges1 = new List<YnvEdge>();
var edges2 = new List<YnvEdge>();
var polysplits = new Dictionary<YnvPoly, YnvPolySplit>();
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<YnvPoly, YnvPolySplit> polysplits, YnvPoly poly)
{
if (poly == null) return null;
YnvPolySplit r = null;
polysplits.TryGetValue(poly, out r);
return r;
}
private void AddPolysIntoGrid(List<YnvPoly> 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<YnvPoly>();
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<YnvFile> 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;
}
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace CodeWalker.GameFiles
public List<Vector3> Vertices { get; set; } public List<Vector3> Vertices { get; set; }
public List<ushort> Indices { get; set; } public List<ushort> Indices { get; set; }
public List<NavMeshEdge> Edges { get; set; } public List<YnvEdge> Edges { get; set; }
public List<YnvPoly> Polys { get; set; } public List<YnvPoly> Polys { get; set; }
public List<YnvPortal> Portals { get; set; } public List<YnvPortal> Portals { get; set; }
public List<YnvPoint> Points { get; set; } public List<YnvPoint> Points { get; set; }
@ -103,7 +103,14 @@ namespace CodeWalker.GameFiles
} }
if (Nav.Edges != null) if (Nav.Edges != null)
{ {
Edges = Nav.Edges.GetFullList(); var edges = Nav.Edges.GetFullList();
Edges = new List<YnvEdge>(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) if (Nav.Polys != null)
{ {
@ -207,9 +214,8 @@ namespace CodeWalker.GameFiles
var portallinks = new List<ushort>(); var portallinks = new List<ushort>();
var vertdict = new Dictionary<Vector3, ushort>(); var vertdict = new Dictionary<Vector3, ushort>();
var blankedgepart1 = new NavMeshEdgePart() { Value = 0x0FFFE1 };//1, -, 1, 0 var areadict = new Dictionary<uint, uint>();
var blankedgepart2 = new NavMeshEdgePart() { Value = 0x2FFFE1 };//1, -, 1, 1 var arealist = new List<uint>();
var blankedge = new NavMeshEdge() { Unknown_0h = blankedgepart1, Unknown_4h = blankedgepart2 };
if (Polys != null) //rebuild vertices, indices, edges and polys lists from poly data. 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 poly = Polys[i];
var vc = poly.Vertices?.Length ?? 0; var vc = poly.Vertices?.Length ?? 0;
//poly.AreaID = (ushort)Nav.AreaID;
poly._RawData.IndexID = (ushort)indslist.Count;
for (int n = 0; n < vc; n++) for (int n = 0; n < vc; n++)
{ {
Vector3 v = poly.Vertices[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; ushort ind;
if (!vertdict.TryGetValue(v, out ind)) if (!vertdict.TryGetValue(v, out ind))
{ {
@ -233,7 +241,40 @@ namespace CodeWalker.GameFiles
poly.Indices[n] = ind; poly.Indices[n] = ind;
} }
indslist.Add(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.IndexCount = vc;
poly._RawData.PortalLinkID = (uint)portallinks.Count;//these shouldn't be directly editable! poly._RawData.PortalLinkID = (uint)portallinks.Count;//these shouldn't be directly editable!
@ -243,6 +284,7 @@ namespace CodeWalker.GameFiles
portallinks.AddRange(poly.PortalLinks); portallinks.AddRange(poly.PortalLinks);
} }
poly.Index = i;//this should be redundant... poly.Index = i;//this should be redundant...
poly.CalculateAABB();//make sure this is up to date!
polylist.Add(poly.RawData); polylist.Add(poly.RawData);
} }
} }
@ -293,18 +335,25 @@ namespace CodeWalker.GameFiles
Nav.Vertices.RebuildList(vertlist); Nav.Vertices.RebuildList(vertlist);
Nav.VerticesCount = Nav.Vertices.ItemCount;
Nav.Indices.RebuildList(indslist); Nav.Indices.RebuildList(indslist);
Nav.Edges.RebuildList(edgelist); Nav.Edges.RebuildList(edgelist);
Nav.EdgesIndicesCount = Nav.Indices.ItemCount;
Nav.Polys.RebuildList(polylist); Nav.Polys.RebuildList(polylist);
Nav.PolysCount = Nav.Polys.ItemCount;
Nav.Portals = (portallist.Count > 0) ? portallist.ToArray() : null; Nav.Portals = (portallist.Count > 0) ? portallist.ToArray() : null;
Nav.PortalsCount = (uint)(Nav.Portals?.Length ?? 0); Nav.PortalsCount = (uint)(Nav.Portals?.Length ?? 0);
Nav.PortalLinks = (portallinks.Count > 0) ? portallinks.ToArray() : null; Nav.PortalLinks = (portallinks.Count > 0) ? portallinks.ToArray() : null;
Nav.PortalLinksCount = (uint)(Nav.PortalLinks?.Length ?? 0); 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... 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<uint, uint> areadict, List<uint> 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) private void BuildSectorTree(NavMeshSector node, int depth, ref uint pointindex)
{ {
Vector3 min = node.AABBMin.XYZ(); Vector3 min = node.AABBMin.XYZ();
Vector3 max = node.AABBMax.XYZ(); Vector3 max = node.AABBMax.XYZ();
Vector3 cen = (min + max) * 0.5f; Vector3 cen = (min + max) * 0.5f;
//totbytes += (uint)node.BlockLength;
if (depth <= 0) if (depth <= 0)
{ {
//go through polys and points and create new lists for this node //go through polys and points and create new lists for this node
@ -351,6 +415,8 @@ namespace CodeWalker.GameFiles
data.PointsStartID = pointindex; data.PointsStartID = pointindex;
//totbytes += (uint)data.BlockLength;
if (Polys != null) if (Polys != null)
{ {
List<ushort> polyids = new List<ushort>(); List<ushort> polyids = new List<ushort>();
@ -367,6 +433,7 @@ namespace CodeWalker.GameFiles
{ {
data.PolyIDs = polyids.ToArray(); data.PolyIDs = polyids.ToArray();
} }
//totbytes += (uint)(polyids.Count * 2);
} }
if (Points != null) if (Points != null)
@ -385,6 +452,7 @@ namespace CodeWalker.GameFiles
data.Points = points.ToArray(); data.Points = points.ToArray();
pointindex += (uint)points.Count; 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() public void UpdateAllNodePositions()
@ -566,9 +646,9 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoly [TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoly
{ {
public NavMeshPoly _RawData; public NavMeshPoly _RawData;
public NavMeshPoly RawData { get { return _RawData; } set { _RawData = value; } }
public YnvFile Ynv { get; set; } 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 AreaID { get { return _RawData.AreaID; } set { _RawData.AreaID = value; } }
public ushort PartID { get { return _RawData.PartID; } set { _RawData.PartID = 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 ushort[] Indices { get; set; }
public Vector3[] Vertices { get; set; } public Vector3[] Vertices { get; set; }
public NavMeshEdge[] Edges { get; set; } public YnvEdge[] Edges { get; set; }
public ushort[] PortalLinks { get; set; } public ushort[] PortalLinks { get; set; }
@ -656,7 +736,7 @@ namespace CodeWalker.GameFiles
Indices = new ushort[ic]; Indices = new ushort[ic];
Vertices = new Vector3[ic]; Vertices = new Vector3[ic];
Edges = new NavMeshEdge[ic]; Edges = new YnvEdge[ic];
int i = 0; int i = 0;
for (int id = startid; id < endid; id++) for (int id = startid; id < endid; id++)
@ -781,9 +861,28 @@ namespace CodeWalker.GameFiles
pcenter += Vertices[i]; 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() 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();
}
}
} }

View File

@ -158,6 +158,39 @@ namespace CodeWalker.GameFiles
//uint totbytes = 0;
//Stack<NavMeshSector> sectorstack = new Stack<NavMeshSector>();
//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((uint)ContentFlags);
writer.Write(VersionUnk1); writer.Write(VersionUnk1);
writer.Write(Unused_018h); writer.Write(Unused_018h);
@ -192,6 +225,12 @@ namespace CodeWalker.GameFiles
writer.Write(Unused_16Ch); 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() 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() public override string ToString()
{ {
return "(Size: " + FloatUtil.GetVector3String(AABBSize) + ")"; 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() public override string ToString()
{ {
@ -459,7 +551,7 @@ namespace CodeWalker.GameFiles
} }
ListParts = parts; ListParts = parts;
ListOffsets = offsets.ToArray(); ListOffsets = offsets.ToArray();
ItemCount = (uint)items.Count;
} }
@ -594,13 +686,15 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshEdge [TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshEdge
{ {
public NavMeshEdgePart Unknown_0h { get; set; } public NavMeshEdgePart _Poly1;
public NavMeshEdgePart Unknown_4h { get; set; } 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() public override string ToString()
{ {
return //Unknown_0h.Bin + " | " + Unknown_4h.Bin + " | " + return //Poly1.Bin + " | " + Poly2.Bin + " | " +
Unknown_0h.ToString() + " | " + Unknown_4h.ToString(); _Poly1.ToString() + " | " + _Poly2.ToString();
} }
} }
@ -616,10 +710,10 @@ namespace CodeWalker.GameFiles
} }
} }
public uint AreaIDInd { get { return (Value >> 0) & 0x1F; } } public uint AreaIDInd { get { return (Value >> 0) & 0x1F; } set { Value = (Value & 0xFFFFFFE0) | (value & 0x1F); } }
public uint PolyID { get { return (Value >> 5) & 0x3FFF; } } public uint PolyID { get { return (Value >> 5) & 0x3FFF; } set { Value = (Value & 0xFFF8001F) | ((value & 0x3FFF) << 5); } }
public uint Unk2 { get { return (Value >> 19) & 0x3; } } public uint Unk2 { get { return (Value >> 19) & 0x3; } set { Value = (Value & 0xFFE7FFFF) | ((value & 0x3) << 19); } }
public uint Unk3 { get { return (Value >> 21) & 0x7FF; } } public uint Unk3 { get { return (Value >> 21) & 0x7FF; } set { Value = (Value & 0x001FFFFF) | ((value & 0x7FF) << 21); } }
public override string ToString() public override string ToString()
{ {
@ -791,8 +885,8 @@ namespace CodeWalker.GameFiles
public ushort[] PolyIDs { get; set; } public ushort[] PolyIDs { get; set; }
public NavMeshPoint[] Points { get; set; } public NavMeshPoint[] Points { get; set; }
private ResourceSystemStructBlock<ushort> PolyIDsBlock = null; public ResourceSystemStructBlock<ushort> PolyIDsBlock = null;
private ResourceSystemStructBlock<NavMeshPoint> PointsBlock = null; public ResourceSystemStructBlock<NavMeshPoint> PointsBlock = null;
public override void Read(ResourceDataReader reader, params object[] parameters) public override void Read(ResourceDataReader reader, params object[] parameters)
{ {

View File

@ -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) public Vector2I GetCellPos(Vector3 p)
{ {
Vector3 ind = (p - new Vector3(CornerX, CornerY, 0)) * CellSizeInv; 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 public class SpaceNavGridCell