YNV/XML conversion

This commit is contained in:
dexy 2021-04-17 06:33:23 +10:00
parent 6268298207
commit 4751818703
7 changed files with 768 additions and 276 deletions

View File

@ -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<string> SaveWarnings = null;
public bool BuildStructsOnSave { get; set; } = true;
public PathBVH BVH { get; set; }
@ -92,102 +96,7 @@ namespace CodeWalker.GameFiles
Nav = rd.ReadBlock<NavMesh>();
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<Vector3>(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<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)
{
var polys = Nav.Polys.GetFullList();
Polys = new List<YnvPoly>(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<YnvPortal>(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<NavMeshSector>();
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<YnvPoint>();
}
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<Vector3>(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<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)
{
var polys = Nav.Polys.GetFullList();
Polys = new List<YnvPoly>(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<YnvPortal>(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<NavMeshSector>();
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<YnvPoint>();
}
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<NavMeshFlags>(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<YnvPoly>(node, "Polygons");
Portals = XmlYnv.ReadItemList<YnvPortal>(node, "Portals");
Points = XmlYnv.ReadItemList<YnvPoint>(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<BasePathNode>();
@ -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<YnvEdge>();
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<T> ReadItemList<T>(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<T>();
foreach (XmlNode inode in inodes)
{
var v = new T();
v.ReadXml(inode);
vlist.Add(v);
}
return vlist;
}
}
return null;
}
}
}

View File

@ -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<RpfEntry>();
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<YnvFile>(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)

View File

@ -62,6 +62,11 @@ namespace CodeWalker.GameFiles
YndFile ynd = RpfFile.GetFile<YndFile>(e, data);
return GetXml(ynd, out filename);
}
else if (fnl.EndsWith(".ynv"))
{
YnvFile ynv = RpfFile.GetFile<YnvFile>(e, data);
return GetXml(ynv, out filename);
}
else if (fnl.EndsWith(".ycd"))
{
YcdFile ycd = RpfFile.GetFile<YcdFile>(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,
}
}

View File

@ -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<ushort> 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<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)
//{ }
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<IResourceBlock>(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<NavMeshSector>();
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<uint> ListOffsetsBlock = null;
public int ItemSize { get { return System.Runtime.InteropServices.Marshal.SizeOf<T>(); } }
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,

View File

@ -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);

View File

@ -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;

View File

@ -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;