mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-26 00:43:00 +08:00
Nav mesh progress
This commit is contained in:
parent
b18b1e7672
commit
bab1bee460
@ -48,6 +48,8 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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\CutFile.cs" />
|
||||
<Compile Include="GameFiles\FileTypes\DlcContentFile.cs" />
|
||||
|
12
CodeWalker.Core/GameFiles/FileTypes/Builders/YndBuilder.cs
Normal file
12
CodeWalker.Core/GameFiles/FileTypes/Builders/YndBuilder.cs
Normal 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
|
||||
{
|
||||
}
|
||||
}
|
443
CodeWalker.Core/GameFiles/FileTypes/Builders/YnvBuilder.cs
Normal file
443
CodeWalker.Core/GameFiles/FileTypes/Builders/YnvBuilder.cs
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace CodeWalker.GameFiles
|
||||
|
||||
public List<Vector3> Vertices { 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<YnvPortal> Portals { get; set; }
|
||||
public List<YnvPoint> 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<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)
|
||||
{
|
||||
@ -207,9 +214,8 @@ namespace CodeWalker.GameFiles
|
||||
var portallinks = new List<ushort>();
|
||||
|
||||
var vertdict = new Dictionary<Vector3, ushort>();
|
||||
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<uint, uint>();
|
||||
var arealist = new List<uint>();
|
||||
|
||||
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<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)
|
||||
{
|
||||
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<ushort> polyids = new List<ushort>();
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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(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<ushort> PolyIDsBlock = null;
|
||||
private ResourceSystemStructBlock<NavMeshPoint> PointsBlock = null;
|
||||
public ResourceSystemStructBlock<ushort> PolyIDsBlock = null;
|
||||
public ResourceSystemStructBlock<NavMeshPoint> PointsBlock = null;
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user