This commit is contained in:
dexy
2018-12-05 11:47:28 +11:00
Unverified
70 changed files with 9912 additions and 20852 deletions
@@ -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
{
}
}
@@ -0,0 +1,473 @@
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
{
/*
*
* YnvBuilder by dexyfex
*
* This class allows for conversion of navmesh data in a generic format into .ynv files.
* The usage is to call AddPoly() with an array of vertex positions for each polygon.
* Polygons should be wound in an anticlockwise direction.
* The returned YnvPoly object needs to have its Edges array set by the importer.
* YnvPoly.Edges is an array of YnvEdge, with one edge for each vertex in the poly.
* The first edge should join the first and second vertices, and the last edge should
* join the last and first vertices.
* The YnvEdge Poly1 and Poly2 both need to be set to the same value, which is the
* corresponding YnvPoly object that was returned by AddPoly.
* Flags values on the polygons and edges also need to be set by the importer.
*
* Once the polygons and edges have all been added, the Build() method should be called,
* which will return a list of YnvFile objects. Call the Save() method on each of those
* to get the byte array for the .ynv file. The correct filename is given by the
* YnvFile.Name property.
* Note that the .ynv building process will split polygons that cross .ynv area borders,
* and assign all the new polygons into the correct .ynv's.
*
*/
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>();
ynv.HasChanged = true;//mark it for the project window
ynv.RpfFileEntry = new RpfResourceFileEntry();
ynv.RpfFileEntry.Name = ynv.Name + ".ynv";
ynv.RpfFileEntry.Path = string.Empty;
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._Poly1.Unk2 = 0;//crash without this
edge._RawData._Poly2.Unk2 = 0;//crash without this
edge._RawData._Poly2.Unk3 = 4;////// edge._RawData._Poly2.Unk3 | 4;
border = true;
////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;
}
}
}
poly.B19_IsCellEdge = border;
}
}
}
}
}
+618 -53
View File
@@ -7,6 +7,8 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using CodeWalker.Core.Utils;
using CodeWalker.World;
namespace CodeWalker.GameFiles
{
@@ -925,6 +927,7 @@ namespace CodeWalker.GameFiles
GrassInstanceBatches = batches.ToArray();
HasChanged = true;
UpdateGrassPhysDict(true);
}
public bool RemoveGrassBatch(YmapGrassInstanceBatch batch)
@@ -949,6 +952,10 @@ namespace CodeWalker.GameFiles
}
}
if (batches.Count <= 0)
{
UpdateGrassPhysDict(false);
}
GrassInstanceBatches = batches.ToArray();
@@ -1137,6 +1144,62 @@ namespace CodeWalker.GameFiles
}
private void UpdateGrassPhysDict(bool add)
{
var physDict = physicsDictionaries?.ToList() ?? new List<MetaHash>();
var vproc1 = JenkHash.GenHash("v_proc1");
var vproc2 = JenkHash.GenHash("v_proc2"); // I think you need vproc2 as well.
var change = false;
if (!physDict.Contains(vproc1))
{
change = true;
if (add) physDict.Add(vproc1);
else physDict.Remove(vproc1);
}
if (!physDict.Contains(vproc2))
{
change = true;
if (add) physDict.Add(vproc2);
else physDict.Remove(vproc2);
}
if (change) physicsDictionaries = physDict.ToArray();
}
public void InitYmapEntityArchetypes(GameFileCache gfc)
{
if (AllEntities != null)
{
for (int i = 0; i < AllEntities.Length; i++)
{
var ent = AllEntities[i];
var arch = gfc.GetArchetype(ent.CEntityDef.archetypeName);
ent.SetArchetype(arch);
if (ent.IsMlo) ent.MloInstance.InitYmapEntityArchetypes(gfc);
}
}
if (GrassInstanceBatches != null)
{
for (int i = 0; i < GrassInstanceBatches.Length; i++)
{
var batch = GrassInstanceBatches[i];
batch.Archetype = gfc.GetArchetype(batch.Batch.archetypeName);
}
}
if (TimeCycleModifiers != null)
{
for (int i = 0; i < TimeCycleModifiers.Length; i++)
{
var tcm = TimeCycleModifiers[i];
World.TimecycleMod wtcm;
if (gfc.TimeCycleModsDict.TryGetValue(tcm.CTimeCycleModifier.name.Hash, out wtcm))
{
tcm.TimeCycleModData = wtcm;
}
}
}
}
private static uint SetBit(uint value, int bit)
{
@@ -1166,7 +1229,7 @@ namespace CodeWalker.GameFiles
public bool IsMlo { get; set; }
public MloInstanceData MloInstance { get; set; }
public YmapEntityDef MloParent { get; set; }
public MCMloEntitySet MloEntitySet { get; set; }
public MloInstanceEntitySet MloEntitySet { get; set; }
public Vector3 MloRefPosition { get; set; }
public Quaternion MloRefOrientation { get; set; }
public MetaWrapper[] Extensions { get; set; }
@@ -1269,56 +1332,57 @@ namespace CodeWalker.GameFiles
{
MloInstance = new MloInstanceData();
}
MloInstance.CreateYmapEntities(this, mloa);
if (mloa != null)
{
if (!IsMlo)
{
IsMlo = true;
MloInstance._Instance = new CMloInstanceDef { CEntityDef = _CEntityDef };
List<YmapEntityDef> mloEntities = Ymap.MloEntities?.ToList() ?? new List<YmapEntityDef>();
mloEntities.Add(this);
Ymap.MloEntities = mloEntities.ToArray();
}
MloInstance.CreateYmapEntities(this, mloa);
}
if (BSRadius == 0.0f)
{
BSRadius = CEntityDef.lodDist;//need something so it doesn't get culled...
}
}
else if (IsMlo) // archetype is no longer an mlo
{
IsMlo = false;
MloInstance = null;
if (Ymap.MloEntities != null)
{
List<YmapEntityDef> mloEntities = Ymap.MloEntities.ToList();
if (mloEntities.Remove(this))
{
Ymap.MloEntities = mloEntities.ToArray();
}
}
}
}
}
public void SetPosition(Vector3 pos)
{
Position = pos;
if (MloParent != null)
{
//TODO: SetPosition for interior entities!
Position = pos;
var inst = MloParent.MloInstance;
if (inst != null)
{
//transform world position into mlo space
//MloRefPosition = ...
//MloRefOrientation = ...
}
_CEntityDef.position = Quaternion.Normalize(Quaternion.Invert(MloParent.Orientation)).Multiply(pos - MloParent.Position);
MloRefPosition = _CEntityDef.position;
UpdateBB();
UpdateMloArchetype();
}
else
{
Position = pos;
_CEntityDef.position = pos;
if (Archetype != null)
{
BSCenter = Orientation.Multiply(Archetype.BSCenter) * Scale;
}
if ((Archetype != null) && (Orientation == Quaternion.Identity))
{
BBMin = (Archetype.BBMin * Scale) + Position;
BBMax = (Archetype.BBMax * Scale) + Position;
}
else
{
BBMin = Position - (BSRadius);
BBMax = Position + (BSRadius);
////not ideal: should transform all 8 corners!
}
UpdateWidgetPosition();
UpdateBB();
}
@@ -1328,40 +1392,51 @@ namespace CodeWalker.GameFiles
MloInstance.UpdateEntities();
}
UpdateWidgetPosition();
}
public void SetOrientation(Quaternion ori)
private void UpdateBB()
{
Quaternion inv = Quaternion.Normalize(Quaternion.Invert(ori));
Orientation = ori;
_CEntityDef.rotation = new Vector4(inv.X, inv.Y, inv.Z, inv.W);
if (MloInstance != null)
{
MloInstance.SetOrientation(ori);
}
if (Archetype != null)
{
BSCenter = Orientation.Multiply(Archetype.BSCenter) * Scale;
}
UpdateWidgetPosition();
UpdateWidgetOrientation();
if ((Archetype != null) && (Orientation == Quaternion.Identity))
{
BBMin = (Archetype.BBMin * Scale) + Position;
BBMax = (Archetype.BBMax * Scale) + Position;
}
else
{
BBMin = Position - (BSRadius);
BBMax = Position + (BSRadius);
////not ideal: should transform all 8 corners!
}
}
public void SetOrientationInv(Quaternion inv)
public void SetOrientation(Quaternion ori, bool inverse = false)
{
Quaternion ori = Quaternion.Normalize(Quaternion.Invert(inv));
Orientation = ori;
_CEntityDef.rotation = new Vector4(inv.X, inv.Y, inv.Z, inv.W);
if (MloParent != null)
{
var mloInv = Quaternion.Normalize(Quaternion.Invert(MloParent.Orientation));
Quaternion rel = Quaternion.Normalize(Quaternion.Multiply(mloInv, ori));
Quaternion inv = Quaternion.Normalize(Quaternion.Invert(rel));
Orientation = ori;
_CEntityDef.rotation = inv.ToVector4();
}
else
{
Quaternion inv = inverse ? ori : Quaternion.Normalize(Quaternion.Invert(ori));
ori = inverse ? Quaternion.Normalize(Quaternion.Invert(ori)) : ori;
Orientation = ori;
_CEntityDef.rotation = inv.ToVector4();
}
if (MloInstance != null)
{
MloInstance.SetOrientation(ori);
}
if (Archetype != null)
{
BSCenter = Orientation.Multiply(Archetype.BSCenter) * Scale;
@@ -1376,14 +1451,35 @@ namespace CodeWalker.GameFiles
Scale = new Vector3(s.X, s.X, s.Z);
_CEntityDef.scaleXY = s.X;
_CEntityDef.scaleZ = s.Z;
MloInstanceData mloInstance = MloParent?.MloInstance;
if (mloInstance != null)
{
var mcEntity = mloInstance.TryGetArchetypeEntity(this);
if (mcEntity != null)
{
mcEntity._Data.scaleXY = s.X;
mcEntity._Data.scaleZ = s.Z;
}
}
if (Archetype != null)
{
float smax = Math.Max(Scale.X, Scale.Z);
BSRadius = Archetype.BSRadius * smax;
}
SetPosition(Position);//update the BB
}
private void UpdateMloArchetype()
{
if (!(MloParent.Archetype is MloArchetype mloArchetype)) return;
if (Index >= mloArchetype.entities.Length) return;
MCEntityDef entity = mloArchetype.entities[Index];
entity._Data.position = _CEntityDef.position;
entity._Data.rotation = _CEntityDef.rotation;
}
public void SetPivotPosition(Vector3 pos)
@@ -1493,6 +1589,8 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))]
public class YmapGrassInstanceBatch
{
private const float BatchVertMultiplier = 0.00001525878f;
public Archetype Archetype { get; set; } //cached by GameFileCache on loading...
public rage__fwGrassInstanceListDef Batch { get; set; }
public rage__fwGrassInstanceListDef__InstanceData[] Instances { get; set; }
@@ -1504,10 +1602,478 @@ namespace CodeWalker.GameFiles
public float Distance; //used for rendering
public YmapFile Ymap { get; set; }
private List<BoundingBox> grassBounds; // for brush
public bool BrushEnabled; // for brush
public float BrushRadius = 5f; // for brush
public bool HasChanged; // for brush and renderer
// TODO: Make configurable.
const float BoundingSize = 0.3F;
static readonly Vector3 GrassMinMax = Vector3.One * BoundingSize;
public override string ToString()
{
return Batch.ToString();
}
public void UpdateInstanceCount()
{
var b = Batch;
var ins = b.InstanceList;
ins.Count1 = (ushort)Instances.Length;
b.InstanceList = ins;
Batch = b;
}
public bool IsPointBlockedByInstance(Vector3 point)
{
return grassBounds.Any(bb => bb.Contains(point) == ContainmentType.Contains);
}
private void ReInitializeBoundingCache()
{
// cache is already initialized correctly.
if (grassBounds != null && (grassBounds.Count == Instances.Length))
return;
// Clear the current bounding cache.
if (grassBounds == null)
grassBounds = new List<BoundingBox>();
else grassBounds?.Clear();
foreach (var inst in Instances)
{
// create bounding box for this instance.
var worldPos = GetGrassWorldPos(inst.Position, new BoundingBox(AABBMin, AABBMax));
var bb = new BoundingBox(worldPos - GrassMinMax, worldPos + GrassMinMax);
grassBounds.Add(bb);
}
}
public bool EraseInstancesAtMouse(
YmapGrassInstanceBatch batch,
SpaceRayIntersectResult mouseRay,
float radius)
{
rage__spdAABB batchAABB = batch.Batch.BatchAABB;
var oldInstanceBounds = new BoundingBox
(
batchAABB.min.XYZ(),
batchAABB.max.XYZ()
);
var deleteSphere = new BoundingSphere(mouseRay.Position, radius);
// check each instance to see if it's in the delete sphere
// thankfully we've just avoided an O(n^2) op using this bounds stuff (doesn't mean it's super fast though,
// but it's not super slow either, even at like 50,000 instances)
var insList = new List<rage__fwGrassInstanceListDef__InstanceData>();
foreach (var instance in batch.Instances)
{
// get the world pos
var worldPos = GetGrassWorldPos(instance.Position, oldInstanceBounds);
// create a boundary around the instance.
var instanceBounds = new BoundingBox(worldPos - GrassMinMax, worldPos + GrassMinMax);
// check if the sphere contains the boundary.
var bb = new BoundingBox(instanceBounds.Minimum, instanceBounds.Maximum);
var ct = deleteSphere.Contains(ref bb);
if (ct == ContainmentType.Contains || ct == ContainmentType.Intersects)
{
//delInstances.Add(instance); // Add a copy of this instance
continue;
}
insList.Add(instance);
}
if (insList.Count == Instances.Length)
return false;
var newBounds = GetNewGrassBounds(insList, oldInstanceBounds);
// recalc instances
var b = RecalcBatch(newBounds, batch);
batch.Batch = b;
insList = RecalculateInstances(insList, oldInstanceBounds, newBounds);
batch.Instances = insList.ToArray();
return true;
}
public void CreateInstancesAtMouse(
YmapGrassInstanceBatch batch,
SpaceRayIntersectResult mouseRay,
float radius,
int amount,
Func<Vector3, SpaceRayIntersectResult> spawnRayFunc,
Color color,
int ao,
int scale,
Vector3 pad,
bool randomScale)
{
ReInitializeBoundingCache();
var spawnPosition = mouseRay.Position;
var positions = new List<Vector3>();
var normals = new List<Vector3>();
// Get rand positions.
GetSpawns(spawnPosition, spawnRayFunc, positions, normals, radius, amount);
if (positions.Count <= 0) return;
// get the instance list
var instances =
batch.Instances?.ToList() ?? new List<rage__fwGrassInstanceListDef__InstanceData>();
var batchAABB = batch.Batch.BatchAABB;
// make sure to store the old instance bounds for the original
// grass instances
var oldInstanceBounds = new BoundingBox(batchAABB.min.XYZ(), batchAABB.max.XYZ());
if (positions.Count <= 0)
return;
// Begin the spawn bounds.
var grassBound = new BoundingBox(positions[0] - GrassMinMax, positions[0] + GrassMinMax);
grassBound = EncapsulatePositions(positions, grassBound);
// Calculate the new spawn bounds.
var newInstanceBounds = new BoundingBox(oldInstanceBounds.Minimum, oldInstanceBounds.Maximum);
newInstanceBounds = instances.Count > 0
? newInstanceBounds.Encapsulate(grassBound)
: new BoundingBox(grassBound.Minimum, grassBound.Maximum);
// now we need to recalculate the position of each instance
instances = RecalculateInstances(instances, oldInstanceBounds, newInstanceBounds);
// Add new instances at each spawn position with
// the parameters in the brush.
SpawnInstances(positions, normals, instances, newInstanceBounds, color, ao, scale, pad, randomScale);
// then recalc the bounds of the grass batch
var b = RecalcBatch(newInstanceBounds, batch);
// plug our values back in and refresh the ymap.
batch.Batch = b;
// Give back the new intsances
batch.Instances = instances.ToArray();
grassBounds.Clear();
}
// bhv approach recommended by dexy.
public YmapGrassInstanceBatch[] OptimizeInstances(YmapGrassInstanceBatch batch, float minRadius)
{
// this function will return an array of grass instance batches
// that are split up into sectors (groups) with a specific size.
// say for instance we have 30,000 instances spread across a large
// distance. We will split those instances into a grid-like group
// and return the groups as an array of batches.
var oldInstanceBounds = new BoundingBox(batch.Batch.BatchAABB.min.XYZ(), batch.Batch.BatchAABB.max.XYZ());
if (oldInstanceBounds.Radius() < minRadius)
{
return new [] { batch };
}
// Get our optimized grassInstances
var split = SplitGrassRecursive(batch.Instances.ToList(), oldInstanceBounds, minRadius);
// Initiate a new batch list.
var newBatches = new List<YmapGrassInstanceBatch>();
foreach (var grassList in split)
{
// Create a new batch
var newBatch = new YmapGrassInstanceBatch
{
Archetype = batch.Archetype,
Ymap = batch.Ymap
};
// Get the boundary of the grassInstances
var newInstanceBounds = GetNewGrassBounds(grassList, oldInstanceBounds);
// Recalculate the batch boundaries.
var b = RecalcBatch(newInstanceBounds, newBatch);
newBatch.Batch = b;
var ins = RecalculateInstances(grassList, oldInstanceBounds, newInstanceBounds);
newBatch.Instances = ins.ToArray();
newBatches.Add(newBatch);
}
return newBatches.ToArray();
}
private List<List<rage__fwGrassInstanceListDef__InstanceData>> SplitGrassRecursive(
IReadOnlyList<rage__fwGrassInstanceListDef__InstanceData> grassInstances,
BoundingBox batchAABB,
float minRadius = 15F
)
{
var ret = new List<List<rage__fwGrassInstanceListDef__InstanceData>>();
var oldPoints = SplitGrass(grassInstances, batchAABB);
while (true)
{
var stop = true;
var newPoints = new List<List<rage__fwGrassInstanceListDef__InstanceData>>();
foreach (var mb in oldPoints)
{
// for some reason we got a null group?
if (mb == null)
continue;
// Get the bounds of the grassInstances list
var radius = GetNewGrassBounds(mb, batchAABB).Radius();
// check if the radius of the grassInstances
if (radius <= minRadius)
{
// this point list is within the minimum
// radius.
ret.Add(mb);
continue; // we don't need to continue.
}
// since we're here let's keep going
stop = false;
// split the grassInstances again
var s = SplitGrass(mb, batchAABB);
// add it into the new grassInstances list.
newPoints.AddRange(s);
}
// set the old grassInstances to the new grassInstances.
oldPoints = newPoints.ToArray();
// if we're done, and all grassInstances are within the desired size
// then end the loop.
if (stop) break;
}
return ret;
}
private List<rage__fwGrassInstanceListDef__InstanceData>[] SplitGrass(
IReadOnlyList<rage__fwGrassInstanceListDef__InstanceData> points,
BoundingBox batchAABB)
{
var pointGroup = new List<rage__fwGrassInstanceListDef__InstanceData>[2];
// Calculate the bounds of these grassInstances.
var m = GetNewGrassBounds(points, batchAABB);
// Get the center and size
var mm = new Vector3
{
X = Math.Abs(m.Minimum.X - m.Maximum.X),
Y = Math.Abs(m.Minimum.Y - m.Maximum.Y),
Z = Math.Abs(m.Minimum.Z - m.Maximum.Z)
};
// x is the greatest axis...
if (mm.X > mm.Y && mm.X > mm.Z)
{
// Calculate both boundaries.
var lhs = new BoundingBox(m.Minimum, m.Maximum - new Vector3(mm.X * 0.5F, 0, 0));
var rhs = new BoundingBox(m.Minimum + new Vector3(mm.X * 0.5F, 0, 0), m.Maximum);
// Set the grassInstances accordingly.
pointGroup[0] = points
.Where(p => lhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
pointGroup[1] = points
.Where(p => rhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
}
// y is the greatest axis...
else if (mm.Y > mm.X && mm.Y > mm.Z)
{
// Calculate both boundaries.
var lhs = new BoundingBox(m.Minimum, m.Maximum - new Vector3(0, mm.Y * 0.5F, 0));
var rhs = new BoundingBox(m.Minimum + new Vector3(0, mm.Y * 0.5F, 0), m.Maximum);
// Set the grassInstances accordingly.
pointGroup[0] = points
.Where(p => lhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
pointGroup[1] = points
.Where(p => rhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
}
// z is the greatest axis...
else if (mm.Z > mm.X && mm.Z > mm.Y)
{
// Calculate both boundaries.
var lhs = new BoundingBox(m.Minimum, m.Maximum - new Vector3(0, 0, mm.Z * 0.5F));
var rhs = new BoundingBox(m.Minimum + new Vector3(0, 0, mm.Z * 0.5F), m.Maximum);
// Set the grassInstances accordingly.
pointGroup[0] = points
.Where(p => lhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
pointGroup[1] = points
.Where(p => rhs.Contains(GetGrassWorldPos(p.Position, batchAABB)) == ContainmentType.Contains).ToList();
}
return pointGroup;
}
private static BoundingBox GetNewGrassBounds(IReadOnlyList<rage__fwGrassInstanceListDef__InstanceData> newGrass, BoundingBox oldAABB)
{
var grassPositions = newGrass.Select(x => GetGrassWorldPos(x.Position, oldAABB)).ToArray();
return BoundingBox.FromPoints(grassPositions).Expand(1f);
}
private void SpawnInstances(
IReadOnlyList<Vector3> positions,
IReadOnlyList<Vector3> normals,
ICollection<rage__fwGrassInstanceListDef__InstanceData> instanceList,
BoundingBox instanceBounds,
Color color,
int ao,
int scale,
Vector3 pad,
bool randomScale)
{
for (var i = 0; i < positions.Count; i++)
{
var pos = positions[i];
// create the new instance.
var newInstance = CreateNewInstance(normals[i], color, ao, scale, pad, randomScale);
// get the grass position of the new instance and add it to the
// instance list
var grassPosition = GetGrassPos(pos, instanceBounds);
newInstance.Position = grassPosition;
instanceList.Add(newInstance);
}
}
private rage__fwGrassInstanceListDef__InstanceData CreateNewInstance(Vector3 normal, Color color, int ao, int scale, Vector3 pad,
bool randomScale = false)
{
//Vector3 pad = FloatUtil.ParseVector3String(PadTextBox.Text);
//int scale = (int)ScaleNumericUpDown.Value;
var rand = new Random();
if (randomScale)
scale = rand.Next(scale / 2, scale);
var newInstance = new rage__fwGrassInstanceListDef__InstanceData
{
Ao = (byte)ao,
Scale = (byte)scale,
Color = new ArrayOfBytes3 { b0 = color.R, b1 = color.G, b2 = color.B },
Pad = new ArrayOfBytes3 { b0 = (byte)pad.X, b1 = (byte)pad.Y, b2 = (byte)pad.Z },
NormalX = (byte)((normal.X + 1) * 0.5F * 255F),
NormalY = (byte)((normal.Y + 1) * 0.5F * 255F)
};
return newInstance;
}
private rage__fwGrassInstanceListDef RecalcBatch(BoundingBox newInstanceBounds, YmapGrassInstanceBatch batch)
{
batch.AABBMax = newInstanceBounds.Maximum;
batch.AABBMin = newInstanceBounds.Minimum;
batch.Position = newInstanceBounds.Center();
batch.Radius = newInstanceBounds.Radius();
var b = batch.Batch;
b.BatchAABB = new rage__spdAABB
{
min =
new Vector4(newInstanceBounds.Minimum,
0), // Let's pass the new stuff into the batchabb as well just because.
max = new Vector4(newInstanceBounds.Maximum, 0)
};
return b;
}
private void GetSpawns(
Vector3 origin, Func<Vector3,
SpaceRayIntersectResult> spawnRayFunc,
ICollection<Vector3> positions,
ICollection<Vector3> normals,
float radius,
int resolution = 28)
{
var rand = new Random();
for (var i = 0; i < resolution; i++)
{
var randX = (float)rand.NextDouble(-radius, radius);
var randY = (float)rand.NextDouble(-radius, radius);
if (Math.Abs(randX) > 0 && Math.Abs(randY) > 0)
{
randX *= .7071f;
randY *= .7071f;
}
var posOffset = origin + new Vector3(randX, randY, 2f);
var spaceRay = spawnRayFunc.Invoke(posOffset);
if (!spaceRay.Hit) continue;
// not truly O(n^2) but may be slow...
// actually just did some testing, not slow at all.
if (IsPointBlockedByInstance(spaceRay.Position)) continue;
normals.Add(spaceRay.Normal);
positions.Add(spaceRay.Position);
}
}
private static List<rage__fwGrassInstanceListDef__InstanceData> RecalculateInstances(
List<rage__fwGrassInstanceListDef__InstanceData> instances,
BoundingBox oldInstanceBounds,
BoundingBox newInstanceBounds)
{
var refreshList = new List<rage__fwGrassInstanceListDef__InstanceData>();
foreach (var inst in instances)
{
// Copy instance
var copy =
new rage__fwGrassInstanceListDef__InstanceData
{
Position = inst.Position,
Ao = inst.Ao,
Color = inst.Color,
NormalX = inst.NormalX,
NormalY = inst.NormalY,
Pad = inst.Pad,
Scale = inst.Scale
};
// get the position from where we would be in the old bounds, and move it to
// the position it needs to be in the new bounds.
var oldPos = GetGrassWorldPos(copy.Position, oldInstanceBounds);
//var oldPos = oldInstanceBounds.min + oldInstanceBounds.Size * (grassPos * BatchVertMultiplier);
copy.Position = GetGrassPos(oldPos, newInstanceBounds);
refreshList.Add(copy);
}
instances = refreshList.ToList();
return instances;
}
private static BoundingBox EncapsulatePositions(IEnumerable<Vector3> positions, BoundingBox bounds)
{
foreach (var pos in positions)
{
var posBounds = new BoundingBox(pos - (GrassMinMax + 0.1f), pos + (GrassMinMax + 0.1f));
bounds = bounds.Encapsulate(posBounds);
}
return bounds;
}
private static ArrayOfUshorts3 GetGrassPos(Vector3 worldPos, BoundingBox batchAABB)
{
var offset = worldPos - batchAABB.Minimum;
var size = batchAABB.Size();
var percentage =
new Vector3(
offset.X / size.X,
offset.Y / size.Y,
offset.Z / size.Z
);
var instancePos = percentage / BatchVertMultiplier;
return new ArrayOfUshorts3
{
u0 = (ushort)instancePos.X,
u1 = (ushort)instancePos.Y,
u2 = (ushort)instancePos.Z
};
}
private static Vector3 GetGrassWorldPos(ArrayOfUshorts3 grassPos, BoundingBox batchAABB)
{
return batchAABB.Minimum + batchAABB.Size() * (grassPos.XYZ() * BatchVertMultiplier);
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
@@ -1548,7 +2114,6 @@ namespace CodeWalker.GameFiles
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class YmapTimeCycleModifier
{
+313 -93
View File
@@ -14,7 +14,7 @@ namespace CodeWalker.GameFiles
public List<Vector3> Vertices { get; set; }
public List<ushort> Indices { get; set; }
public List<NavMeshAdjPoly> AdjPolys { 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; }
@@ -48,6 +48,16 @@ namespace CodeWalker.GameFiles
//getters for property grids viewing of the lists
public Vector3[] AllVertices { get { return Vertices?.ToArray(); } }
public ushort[] AllIndices { get { return Indices?.ToArray(); } }
public YnvEdge[] AllEdges { get { return Edges?.ToArray(); } }
public YnvPoly[] AllPolys { get { return Polys?.ToArray(); } }
public YnvPortal[] AllPortals { get { return Portals?.ToArray(); } }
public YnvPoint[] AllPoints { get { return Points?.ToArray(); } }
public YnvFile() : base(null, GameFileType.Ynv)
{
@@ -101,9 +111,16 @@ namespace CodeWalker.GameFiles
{
Indices = Nav.Indices.GetFullList();
}
if (Nav.AdjPolys != null)
if (Nav.Edges != null)
{
AdjPolys = Nav.AdjPolys.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)
{
@@ -114,16 +131,7 @@ namespace CodeWalker.GameFiles
YnvPoly poly = new YnvPoly();
poly.Init(this, polys[i]);
poly.Index = i;
poly.CalculatePosition(); //calc poly center for display purposes..
Polys.Add(poly);
if (poly.PortalType > 0)
{
if (poly.PortalType != 2) //seems to be what portal links need to understand..
{ }
}
}
}
if (Nav.Portals != null)
@@ -209,23 +217,97 @@ namespace CodeWalker.GameFiles
Vector3 aabbsizeinv = 1.0f / aabbsize;
var vertlist = new List<NavMeshVertex>();
if (Vertices != null)
{
for (int i = 0; i < Vertices.Count; i++)
{
vertlist.Add(NavMeshVertex.Create((Vertices[i] - posoffset) * aabbsizeinv));
}
}
var indslist = new List<ushort>();
var edgelist = new List<NavMeshEdge>();
var polylist = new List<NavMeshPoly>();
if (Polys != null)
var portallist = new List<NavMeshPortal>();
var portallinks = new List<ushort>();
var vertdict = new Dictionary<Vector3, ushort>();
var areadict = new Dictionary<uint, uint>();
var arealist = new List<uint>();
var areaid = Nav.AreaID;
EnsureEdgeAreaID(areaid, areadict, arealist);
EnsureEdgeAreaID(0x3FFF, areadict, arealist);
EnsureEdgeAreaID(areaid - 100, areadict, arealist);
EnsureEdgeAreaID(areaid - 1, areadict, arealist);
EnsureEdgeAreaID(areaid + 1, areadict, arealist);
EnsureEdgeAreaID(areaid + 100, areadict, arealist);
if (Polys != null) //rebuild vertices, indices, edges and polys lists from poly data.
{
for (int i = 0; i < Polys.Count; i++)
{
Polys[i].Index = i;
polylist.Add(Polys[i].RawData);
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];
YnvEdge e = ((poly.Edges != null) && (n < poly.Edges.Length)) ? poly.Edges[n] : null;
ushort ind;
if (!vertdict.TryGetValue(v, out ind))
{
ind = (ushort)vertlist.Count;
vertdict[v] = ind;
vertlist.Add(NavMeshVertex.Create(Vector3.Clamp((v - posoffset) * aabbsizeinv, Vector3.Zero, Vector3.One)));
}
if ((poly.Indices != null) && (n < poly.Indices.Length))
{
poly.Indices[n] = ind;
}
indslist.Add(ind);
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!
poly._RawData.PortalLinkCount = (byte)(poly.PortalLinks?.Length ?? 0);
if (poly.PortalLinks != null)
{
portallinks.AddRange(poly.PortalLinks);
}
poly.Index = i;//this should be redundant...
poly.CalculateAABB();//make sure this is up to date!
polylist.Add(poly.RawData);
}
}
var portallist = new List<NavMeshPortal>();
if (Portals != null)
{
for (int i = 0; i < Portals.Count; i++)
@@ -238,7 +320,7 @@ namespace CodeWalker.GameFiles
}
}
if (Points != null)
if (Points != null) //points will be built into the sector tree
{
for (int i = 0; i < Points.Count; i++)
{
@@ -259,10 +341,10 @@ namespace CodeWalker.GameFiles
Nav.Indices = new NavMeshList<ushort>();
Nav.Indices.VFT = 1080158424;
}
if (Nav.AdjPolys == null)
if (Nav.Edges == null)
{
Nav.AdjPolys = new NavMeshList<NavMeshAdjPoly>();
Nav.AdjPolys.VFT = 1080158440;
Nav.Edges = new NavMeshList<NavMeshEdge>();
Nav.Edges.VFT = 1080158440;
}
if (Nav.Polys == null)
{
@@ -272,16 +354,24 @@ namespace CodeWalker.GameFiles
Nav.Vertices.RebuildList(vertlist);
Nav.VerticesCount = Nav.Vertices.ItemCount;
Nav.Indices.RebuildList(Indices);
Nav.Indices.RebuildList(indslist);
Nav.AdjPolys.RebuildList(AdjPolys);
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);
//TODO: update portal links data.....
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...
@@ -315,12 +405,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
@@ -329,6 +434,8 @@ namespace CodeWalker.GameFiles
data.PointsStartID = pointindex;
//totbytes += (uint)data.BlockLength;
if (Polys != null)
{
List<ushort> polyids = new List<ushort>();
@@ -345,6 +452,7 @@ namespace CodeWalker.GameFiles
{
data.PolyIDs = polyids.ToArray();
}
//totbytes += (uint)(polyids.Count * 2);
}
if (Points != null)
@@ -363,6 +471,7 @@ namespace CodeWalker.GameFiles
data.Points = points.ToArray();
pointindex += (uint)points.Count;
}
//totbytes += (uint)(points.Count * 8);
}
}
@@ -419,6 +528,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()
@@ -474,67 +595,39 @@ namespace CodeWalker.GameFiles
//go through the nav mesh polys and generate verts to render...
if ((Vertices == null) || (Vertices.Count == 0)) return;
if ((Indices == null) || (Indices.Count == 0)) return;
if ((Polys == null) || (Polys.Count == 0)) return;
int vc = Vertices.Count;
List<EditorVertex> rverts = new List<EditorVertex>();
EditorVertex p0 = new EditorVertex();
EditorVertex p1 = new EditorVertex();
EditorVertex p2 = new EditorVertex();
foreach (var ypoly in Polys)
{
var poly = ypoly.RawData;
if ((ypoly.Vertices == null) || (ypoly.Vertices.Length < 3))
{ continue; }
var colour = ypoly.GetColour();
var colourval = (uint)colour.ToRgba();
var ic = poly.IndexCount;
var startid = poly.IndexID;
var endid = startid + ic;
if (startid >= Indices.Count)
{ continue; }
if (endid > Indices.Count)
{ continue; }
if(ic<3)
{ continue; }//not enough verts to make a triangle...
if (ic > 15)
{ }
EditorVertex p0 = new EditorVertex();
EditorVertex p1 = new EditorVertex();
EditorVertex p2 = new EditorVertex();
p0.Colour = colourval;
p1.Colour = colourval;
p2.Colour = colourval;
var startind = Indices[startid];
if (startind >= vc)
{ continue; }
p0.Position = Vertices[startind];
p0.Position = ypoly.Vertices[0];
//build triangles for the poly.
int tricount = ic - 2;
int tricount = ypoly.Vertices.Length - 2;
for (int t = 0; t < tricount; t++)
{
int tid = startid + t;
int ind1 = Indices[tid + 1];
int ind2 = Indices[tid + 2];
if ((ind1 >= vc) || (ind2 >= vc))
{ continue; }
p1.Position = Vertices[ind1];
p2.Position = Vertices[ind2];
p1.Position = ypoly.Vertices[t + 1];
p2.Position = ypoly.Vertices[t + 2];
rverts.Add(p0);
rverts.Add(p1);
rverts.Add(p2);
}
}
TriangleVerts = rverts.ToArray();
@@ -572,14 +665,14 @@ 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; } }
public ushort PortalLinkID { get { return _RawData.PortalLinkID; } set { _RawData.PortalLinkID = value; } }
public byte PortalType { get { return _RawData.PortalType; } set { _RawData.PortalType = 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)); } }
@@ -624,14 +717,84 @@ namespace CodeWalker.GameFiles
public Vector3 Position { get; set; }
public int Index { get; set; }
public ushort[] Indices { get; set; }
public Vector3[] Vertices { get; set; }
public YnvEdge[] Edges { get; set; }
public ushort[] PortalLinks { get; set; }
public void Init(YnvFile ynv, NavMeshPoly poly)
{
Ynv = ynv;
RawData = poly;
LoadIndices();
LoadPortalLinks();
CalculatePosition(); //calc poly center for display purposes..
}
public void LoadIndices()
{
//load indices, vertices and edges
var indices = Ynv.Indices;
var vertices = Ynv.Vertices;
var edges = Ynv.Edges;
if ((indices == null) || (vertices == null) || (edges == null))
{ return; }
var vc = vertices.Count;
var ic = _RawData.IndexCount;
var startid = _RawData.IndexID;
var endid = startid + ic;
if (startid >= indices.Count)
{ return; }
if (endid > indices.Count)
{ return; }
if (endid > edges.Count)
{ return; }
Indices = new ushort[ic];
Vertices = new Vector3[ic];
Edges = new YnvEdge[ic];
int i = 0;
for (int id = startid; id < endid; id++)
{
var ind = indices[id];
Indices[i] = ind;
Vertices[i] = (ind < vc) ? vertices[ind] : Vector3.Zero;
Edges[i] = edges[id];
i++;
}
}
public void LoadPortalLinks()
{
if (PortalLinkCount == 0)
{ return; }
var links = Ynv.Nav?.PortalLinks;
if (links == null)
{ return; }
var ll = links.Length;
PortalLinks = new ushort[PortalLinkCount];
int offset = (int)PortalLinkID;
for (int i = 0; i < PortalLinkCount; i++)
{
int idx = offset + i;
PortalLinks[i] = (idx < ll) ? links[idx] : (ushort)0;
}
if (PortalLinkCount != 2)
{ }//debug
}
public void SetPosition(Vector3 pos)
{
Vector3 delta = pos - Position;
@@ -687,7 +850,7 @@ namespace CodeWalker.GameFiles
//if ((u5 & 8388608) > 0) colour.Red += 1.0f; //slope facing -X,-Y (southwest)
//if (u5 >= 16777216) { } //other bits unused
var u1 = _RawData.PortalType;
var u1 = _RawData.PortalLinkCount;
//if ((u1 & 1) > 0) colour.Red += 1.0f; //portal - don't interact?
//if ((u1 & 2) > 0) colour.Green += 1.0f; //portal - ladder/fence interaction?
//if ((u1 & 4) > 0) colour.Blue += 1.0f; //portal - fence interaction / go away from?
@@ -709,32 +872,36 @@ namespace CodeWalker.GameFiles
public void CalculatePosition()
{
//calc poly center for display purposes.
var indices = Ynv.Indices;
var vertices = Ynv.Vertices;
if ((indices == null) || (vertices == null))
{ return; }
var vc = vertices.Count;
var ic = _RawData.IndexCount;
var startid = _RawData.IndexID;
var endid = startid + ic;
if (startid >= indices.Count)
{ return; }
if (endid > indices.Count)
{ return; }
Vector3 pcenter = Vector3.Zero;
float pcount = 0.0f;
for (int id = startid; id < endid; id++)
if (Vertices != null)
{
var ind = indices[id];
if (ind >= vc)
{ continue; }
pcenter += vertices[ind];
pcount += 1.0f;
for (int i = 0; i < Vertices.Length; i++)
{
pcenter += Vertices[i];
}
}
Position = pcenter * (1.0f / pcount);
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()
@@ -870,4 +1037,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();
}
}
}
+144 -7
View File
@@ -21,8 +21,8 @@ namespace CodeWalker.GameFiles
public uint NameHash { get; set; }
public string[] Strings { get; set; }
public CMapTypes CMapTypes { get; set; }
public CMapTypes _CMapTypes;
public CMapTypes CMapTypes { get { return _CMapTypes; } set { _CMapTypes = value; } }
public Archetype[] AllArchetypes { get; set; }
@@ -50,6 +50,111 @@ namespace CodeWalker.GameFiles
return (RpfFileEntry != null) ? RpfFileEntry.Name : string.Empty;
}
public byte[] Save()
{
MetaBuilder mb = new MetaBuilder();
var mdb = mb.EnsureBlock(MetaName.CMapTypes);
CMapTypes mapTypes = _CMapTypes;
if (Extensions == null || Extensions.Length <= 0)
mapTypes.extensions = new Array_StructurePointer();
else
mapTypes.extensions = mb.AddWrapperArrayPtr(Extensions);
if ((AllArchetypes != null) && (AllArchetypes.Length > 0))
{
for (int i = 0; i < AllArchetypes.Length; i++)
{
var arch = AllArchetypes[i]; //save the extensions first..
if (arch._BaseArchetypeDef.extensions.Count1 > 0)
{
arch._BaseArchetypeDef.extensions = mb.AddWrapperArrayPtr(arch.Extensions);
}
}
MetaPOINTER[] ptrs = new MetaPOINTER[AllArchetypes.Length];
for (int i = 0; i < AllArchetypes.Length; i++)
{
var arch = AllArchetypes[i];
switch (arch)
{
case TimeArchetype t:
ptrs[i] = mb.AddItemPtr(MetaName.CTimeArchetypeDef, t._TimeArchetypeDef);
break;
case MloArchetype m:
try
{
m._MloArchetypeDef._MloArchetypeDef.entities = mb.AddWrapperArrayPtr(m.entities);
m._MloArchetypeDef._MloArchetypeDef.rooms = mb.AddWrapperArray(m.rooms);
m._MloArchetypeDef._MloArchetypeDef.portals = mb.AddWrapperArray(m.portals);
m._MloArchetypeDef._MloArchetypeDef.entitySets = mb.AddWrapperArray(m.entitySets);
m._MloArchetypeDef._MloArchetypeDef.timeCycleModifiers = mb.AddItemArrayPtr(MetaName.CTimeCycleModifier, m.timeCycleModifiers);
}
catch/* (Exception e)*/
{
//todo: log save error.
}
ptrs[i] = mb.AddItemPtr(MetaName.CMloArchetypeDef, m._MloArchetypeDef);
break;
case Archetype a:
ptrs[i] = mb.AddItemPtr(MetaName.CBaseArchetypeDef, a._BaseArchetypeDef);
break;
}
}
mapTypes.archetypes = mb.AddPointerArray(ptrs);
}
else
{
mapTypes.archetypes = new Array_StructurePointer();
}
if (CompositeEntityTypes != null && CompositeEntityTypes.Length > 0)
{
var cptrs = new MetaPOINTER[CompositeEntityTypes.Length];
for (int i = 0; i < cptrs.Length; i++)
{
var cet = CompositeEntityTypes[i];
cptrs[i] = mb.AddItemPtr(MetaName.CCompositeEntityType, cet);
}
mapTypes.compositeEntityTypes = mb.AddItemArrayPtr(MetaName.CCompositeEntityType, cptrs);
}
mapTypes.name = NameHash;
mapTypes.dependencies = new Array_uint(); // is this never used? possibly a todo?
mb.AddStructureInfo(MetaName.CMapTypes);
mb.AddStructureInfo(MetaName.CBaseArchetypeDef);
mb.AddStructureInfo(MetaName.CMloArchetypeDef);
mb.AddStructureInfo(MetaName.CTimeArchetypeDef);
mb.AddStructureInfo(MetaName.CCompositeEntityType);
mb.AddStructureInfo(MetaName.CMloRoomDef);
mb.AddStructureInfo(MetaName.CMloPortalDef);
mb.AddStructureInfo(MetaName.CMloEntitySet);
if ((AllArchetypes != null && AllArchetypes.Length > 0))
{
mb.AddEnumInfo((MetaName)1991964615); // ASSET_TYPE_
}
if ((AllArchetypes != null) && (AllArchetypes.Any(x => x is MloArchetype m && m.entities.Length > 0)))
{
mb.AddStructureInfo(MetaName.CEntityDef);
mb.AddEnumInfo((MetaName)1264241711); //LODTYPES_
mb.AddEnumInfo((MetaName)648413703); //PRI_
}
mb.AddItem(MetaName.CMapTypes, mapTypes);
Meta meta = mb.GetMeta();
byte[] data = ResourceBuilder.Build(meta, 2);
HasChanged = false;
return data;
}
public void Load(byte[] data)
{
@@ -94,12 +199,12 @@ namespace CodeWalker.GameFiles
Meta = rd.ReadBlock<Meta>();
CMapTypes = MetaTypes.GetTypedData<CMapTypes>(Meta, MetaName.CMapTypes);
_CMapTypes = MetaTypes.GetTypedData<CMapTypes>(Meta, MetaName.CMapTypes);
List<Archetype> allarchs = new List<Archetype>();
var ptrs = MetaTypes.GetPointerArray(Meta, CMapTypes.archetypes);
var ptrs = MetaTypes.GetPointerArray(Meta, _CMapTypes.archetypes);
if (ptrs != null)
{
for (int i = 0; i < ptrs.Length; i++)
@@ -151,7 +256,8 @@ namespace CodeWalker.GameFiles
AllArchetypes = allarchs.ToArray();
Extensions = MetaTypes.GetExtensions(Meta, CMapTypes.extensions);
Extensions = MetaTypes.GetExtensions(Meta, _CMapTypes.extensions);
if (Extensions != null)
{ }
@@ -163,11 +269,11 @@ namespace CodeWalker.GameFiles
//CEntityDefs = MetaTypes.GetTypedDataArray<CEntityDef>(Meta, MetaName.CEntityDef);
CompositeEntityTypes = MetaTypes.ConvertDataArray<CCompositeEntityType>(Meta, MetaName.CCompositeEntityType, CMapTypes.compositeEntityTypes);
CompositeEntityTypes = MetaTypes.ConvertDataArray<CCompositeEntityType>(Meta, MetaName.CCompositeEntityType, _CMapTypes.compositeEntityTypes);
if (CompositeEntityTypes != null)
{ }
NameHash = CMapTypes.name;
NameHash = _CMapTypes.name;
if (NameHash == 0)
{
int ind = entry.NameLower.LastIndexOf('.');
@@ -242,7 +348,38 @@ namespace CodeWalker.GameFiles
}
public void AddArchetype(Archetype archetype)
{
if (AllArchetypes == null)
AllArchetypes = new Archetype[0];
List<Archetype> archetypes = AllArchetypes.ToList();
archetype.Ytyp = this;
archetypes.Add(archetype);
AllArchetypes = archetypes.ToArray();
}
public Archetype AddArchetype()
{
var a = new Archetype();
a._BaseArchetypeDef.assetType = Unk_1991964615.ASSET_TYPE_DRAWABLE;
a._BaseArchetypeDef.lodDist = 60;
a._BaseArchetypeDef.hdTextureDist = 15;
a.Ytyp = this;
AddArchetype(a);
return a;
}
public bool RemoveArchetype(Archetype archetype)
{
List<Archetype> archetypes = AllArchetypes.ToList();
if (archetypes.Remove(archetype))
{
AllArchetypes = archetypes.ToArray();
return true;
}
return false;
}
}
+3 -112
View File
@@ -1805,116 +1805,6 @@ namespace CodeWalker.GameFiles
}
public void InitYmapEntityArchetypes(YmapFile file)
{
if (file == null) return;
if (file.AllEntities != null)
{
for (int i = 0; i < file.AllEntities.Length; i++)
{
var ent = file.AllEntities[i];
var arch = GetArchetype(ent.CEntityDef.archetypeName);
ent.SetArchetype(arch);
if (ent.MloInstance != null)
{
var entities = ent.MloInstance.Entities;
if (entities != null)
{
for (int j = 0; j < entities.Length; j++)
{
var ient = entities[j];
var iarch = GetArchetype(ient.CEntityDef.archetypeName);
ient.SetArchetype(iarch);
if (iarch == null)
{ } //can't find archetype - des stuff eg {des_prologue_door}
}
//update archetype room AABB's.. bad to have this here? where else to put it?
var mloa = arch as MloArchetype;
if (mloa != null)
{
Vector3 mlobbmin = Vector3.Zero;
Vector3 mlobbmax = Vector3.Zero;
Vector3[] c = new Vector3[8];
var rooms = mloa.rooms;
if (rooms != null)
{
for (int j = 0; j < rooms.Length; j++)
{
var room = rooms[j];
if ((room.AttachedObjects == null) || (room.AttachedObjects.Length == 0)) continue;
Vector3 min = new Vector3(float.MaxValue);
Vector3 max = new Vector3(float.MinValue);
for (int k = 0; k < room.AttachedObjects.Length; k++)
{
var objid = room.AttachedObjects[k];
if (objid < entities.Length)
{
var rooment = entities[objid];
if ((rooment != null) && (rooment.Archetype != null))
{
var earch = rooment.Archetype;
var pos = rooment._CEntityDef.position;
var ori = rooment.Orientation;
Vector3 abmin = earch.BBMin * rooment.Scale; //entity box
Vector3 abmax = earch.BBMax * rooment.Scale;
c[0] = abmin;
c[1] = new Vector3(abmin.X, abmin.Y, abmax.Z);
c[2] = new Vector3(abmin.X, abmax.Y, abmin.Z);
c[3] = new Vector3(abmin.X, abmax.Y, abmax.Z);
c[4] = new Vector3(abmax.X, abmin.Y, abmin.Z);
c[5] = new Vector3(abmax.X, abmin.Y, abmax.Z);
c[6] = new Vector3(abmax.X, abmax.Y, abmin.Z);
c[7] = abmax;
for (int n = 0; n < 8; n++)
{
Vector3 corn = ori.Multiply(c[n]) + pos;
min = Vector3.Min(min, corn);
max = Vector3.Max(max, corn);
}
}
}
}
room.BBMin_CW = min;
room.BBMax_CW = max;
mlobbmin = Vector3.Min(mlobbmin, min);
mlobbmax = Vector3.Max(mlobbmax, max);
}
}
mloa.BBMin = mlobbmin;
mloa.BBMax = mlobbmax;
}
}
}
}
}
if (file.GrassInstanceBatches != null)
{
for (int i = 0; i < file.GrassInstanceBatches.Length; i++)
{
var batch = file.GrassInstanceBatches[i];
batch.Archetype = GetArchetype(batch.Batch.archetypeName);
}
}
if (file.TimeCycleModifiers != null)
{
for (int i = 0; i < file.TimeCycleModifiers.Length; i++)
{
var tcm = file.TimeCycleModifiers[i];
World.TimecycleMod wtcm;
if (TimeCycleModsDict.TryGetValue(tcm.CTimeCycleModifier.name.Hash, out wtcm))
{
tcm.TimeCycleModData = wtcm;
}
}
}
}
@@ -1956,8 +1846,9 @@ namespace CodeWalker.GameFiles
if (req.Loaded) AddTextureLookups(req as YtdFile);
break;
case GameFileType.Ymap:
req.Loaded = LoadFile(req as YmapFile);
if (req.Loaded) InitYmapEntityArchetypes(req as YmapFile);
YmapFile y = req as YmapFile;
req.Loaded = LoadFile(y);
if (req.Loaded) y.InitYmapEntityArchetypes(this);
break;
case GameFileType.Yft:
req.Loaded = LoadFile(req as YftFile);
+391 -33
View File
@@ -14,7 +14,7 @@ namespace CodeWalker.GameFiles
public virtual MetaName Type => MetaName.CBaseArchetypeDef;
public CBaseArchetypeDef _BaseArchetypeDef;
public CBaseArchetypeDef BaseArchetypeDef { get { return _BaseArchetypeDef; } set { _BaseArchetypeDef = value; } }
public CBaseArchetypeDef BaseArchetypeDef => _BaseArchetypeDef; // for browsing.
public MetaHash Hash { get; set; }
public YtypFile Ytyp { get; set; }
@@ -31,16 +31,16 @@ namespace CodeWalker.GameFiles
public string Name
public string Name
{
get
get
{
return _BaseArchetypeDef.name.ToString();
}
}
public string AssetName
public string AssetName
{
get
get
{
return _BaseArchetypeDef.assetName.ToString();
}
@@ -49,7 +49,7 @@ namespace CodeWalker.GameFiles
protected void InitVars(ref CBaseArchetypeDef arch)
{
BaseArchetypeDef = arch;
_BaseArchetypeDef = arch;
Hash = arch.assetName;
if (Hash.Hash == 0) Hash = arch.name;
DrawableDict = arch.drawableDictionary;
@@ -83,10 +83,8 @@ namespace CodeWalker.GameFiles
public class TimeArchetype : Archetype
{
public override MetaName Type => MetaName.CTimeArchetypeDef;
public CTimeArchetypeDefData _TimeArchetypeDef;
public CTimeArchetypeDefData TimeArchetypeDef { get { return _TimeArchetypeDef; } set { _TimeArchetypeDef = value; } }
public CTimeArchetypeDef _TimeArchetypeDef;
public CTimeArchetypeDef TimeArchetypeDef => _TimeArchetypeDef; // for browsing.
public uint TimeFlags { get; set; }
public bool[] ActiveHours { get; set; }
@@ -98,9 +96,9 @@ namespace CodeWalker.GameFiles
{
Ytyp = ytyp;
InitVars(ref arch._BaseArchetypeDef);
TimeArchetypeDef = arch.TimeArchetypeDef;
_TimeArchetypeDef = arch;
TimeFlags = _TimeArchetypeDef.timeFlags;
TimeFlags = arch.TimeArchetypeDef.timeFlags;
ActiveHours = new bool[24];
ActiveHoursText = new string[24];
for (int i = 0; i < 24; i++)
@@ -128,8 +126,10 @@ namespace CodeWalker.GameFiles
{
public override MetaName Type => MetaName.CMloArchetypeDef;
public CMloArchetypeDefData _MloArchetypeDef;
public CMloArchetypeDefData MloArchetypeDef { get { return _MloArchetypeDef; } set { _MloArchetypeDef = value; } }
public CMloArchetypeDef MloArchetypeDef => _MloArchetypeDef; // for browsing.
public CMloArchetypeDef _MloArchetypeDef;
public CMloArchetypeDefData _MloArchetypeDefData;
public MCEntityDef[] entities { get; set; }
public MCMloRoomDef[] rooms { get; set; }
@@ -137,36 +137,147 @@ namespace CodeWalker.GameFiles
public MCMloEntitySet[] entitySets { get; set; }
public CMloTimeCycleModifier[] timeCycleModifiers { get; set; }
public void Init(YtypFile ytyp, ref CMloArchetypeDef arch)
{
Ytyp = ytyp;
InitVars(ref arch._BaseArchetypeDef);
MloArchetypeDef = arch.MloArchetypeDef;
_MloArchetypeDef = arch;
_MloArchetypeDefData = arch.MloArchetypeDef;
}
public bool AddEntity(YmapEntityDef ent, int roomIndex)
{
if (ent == null) return false;
// entity already exists in our array. so we'll just add
// it to the instanced entities list and continue.
MloInstanceData mloInstance = ent.MloParent?.MloInstance;
MCEntityDef ymcent = mloInstance?.TryGetArchetypeEntity(ent);
if (ymcent != null)
{
return true;
}
if (roomIndex > rooms.Length)
{
throw new ArgumentOutOfRangeException($"Room index {roomIndex} exceeds the amount of rooms in {Name}.");
}
var mcEntityDef = new MCEntityDef(ref ent._CEntityDef, this);
// Add the new entity def to the entities list.
AddEntity(ent, mcEntityDef);
// Update the attached objects in the room index specified.
AttachEntityToRoom(ent, roomIndex);
return true;
}
// attaches the specified ymap entity index to the room at roomIndex.
private void AttachEntityToRoom(YmapEntityDef ent, int roomIndex)
{
if (roomIndex > rooms.Length)
{
return; // the room index is bigger than the rooms we have...
}
var attachedObjs = rooms[roomIndex].AttachedObjects?.ToList() ?? new List<uint>();
attachedObjs.Add((uint)ent.Index);
rooms[roomIndex].AttachedObjects = attachedObjs.ToArray();
}
// Adds an entity to the entities array and then set's the index of the
// ymap entity to the index of the new MCEntityDef.
private void AddEntity(YmapEntityDef ent, MCEntityDef mcEntityDef)
{
if (ent == null || mcEntityDef == null) return; // no entity?...
// initialize the array.
if (entities == null) entities = new MCEntityDef[0];
List<MCEntityDef> entList = entities.ToList();
entList.Add(mcEntityDef);
ent.Index = entList.IndexOf(mcEntityDef);
entities = entList.ToArray();
}
public bool RemoveEntity(YmapEntityDef ent)
{
if (ent.Index >= entities.Length) return false;
MCEntityDef delent = entities[ent.Index];
MloInstanceData inst = ent.MloParent?.MloInstance;
if (inst == null) return false;
if (delent != null)
{
MCEntityDef[] newentities = new MCEntityDef[entities.Length - 1];
bool didDel = false;
int index = 0;
int delIndex = 0;
for (int i = 0; i < entities.Length; i++)
{
if (entities[i] == delent)
{
delIndex = i;
didDel = true;
continue;
}
newentities[index] = entities[i];
YmapEntityDef ymapEntityDef = inst.TryGetYmapEntity(newentities[index]);
if (ymapEntityDef != null) ymapEntityDef.Index = index;
index++;
}
entities = newentities;
if (didDel) FixRoomIndexes(delIndex);
return didDel;
}
return false;
}
private void FixRoomIndexes(int deletedIndex)
{
foreach (var room in rooms)
{
List<uint> newAttachedObjects = new List<uint>();
if (room.AttachedObjects == null)
continue;
foreach (var objIndex in room.AttachedObjects)
{
if (objIndex == deletedIndex) continue;
if (objIndex > deletedIndex)
newAttachedObjects.Add(objIndex - 1); // move the index back so it matches the index in the entitiy array.
else newAttachedObjects.Add(objIndex); // else just add the index to the attached objects.
}
room.AttachedObjects = newAttachedObjects.ToArray();
}
}
public void LoadChildren(Meta meta)
{
var centities = MetaTypes.ConvertDataArray<CEntityDef>(meta, MetaName.CEntityDef, _MloArchetypeDef.entities);
var centities = MetaTypes.ConvertDataArray<CEntityDef>(meta, MetaName.CEntityDef, _MloArchetypeDefData.entities);
if (centities != null)
{
entities = new MCEntityDef[centities.Length];
for (int i = 0; i < centities.Length; i++)
{
entities[i] = new MCEntityDef(meta, centities[i]);
entities[i] = new MCEntityDef(meta, ref centities[i]) { Archetype = this };
}
}
var crooms = MetaTypes.ConvertDataArray<CMloRoomDef>(meta, MetaName.CMloRoomDef, _MloArchetypeDef.rooms);
var crooms = MetaTypes.ConvertDataArray<CMloRoomDef>(meta, MetaName.CMloRoomDef, _MloArchetypeDefData.rooms);
if (crooms != null)
{
rooms = new MCMloRoomDef[crooms.Length];
for (int i = 0; i < crooms.Length; i++)
{
rooms[i] = new MCMloRoomDef(meta, crooms[i]);
rooms[i] = new MCMloRoomDef(meta, crooms[i]) { Archetype = this, Index = i };
}
}
var cportals = MetaTypes.ConvertDataArray<CMloPortalDef>(meta, MetaName.CMloPortalDef, _MloArchetypeDef.portals);
var cportals = MetaTypes.ConvertDataArray<CMloPortalDef>(meta, MetaName.CMloPortalDef, _MloArchetypeDefData.portals);
if (cportals != null)
{
portals = new MCMloPortalDef[cportals.Length];
@@ -176,7 +287,7 @@ namespace CodeWalker.GameFiles
}
}
var centitySets = MetaTypes.ConvertDataArray<CMloEntitySet>(meta, MetaName.CMloEntitySet, _MloArchetypeDef.entitySets);
var centitySets = MetaTypes.ConvertDataArray<CMloEntitySet>(meta, MetaName.CMloEntitySet, _MloArchetypeDefData.entitySets);
if (centitySets != null)
{
entitySets = new MCMloEntitySet[centitySets.Length];
@@ -187,14 +298,44 @@ namespace CodeWalker.GameFiles
}
timeCycleModifiers = MetaTypes.ConvertDataArray<CMloTimeCycleModifier>(meta, MetaName.CMloTimeCycleModifier, _MloArchetypeDef.timeCycleModifiers);
timeCycleModifiers = MetaTypes.ConvertDataArray<CMloTimeCycleModifier>(meta, MetaName.CMloTimeCycleModifier, _MloArchetypeDefData.timeCycleModifiers);
}
public MCMloRoomDef GetEntityRoom(MCEntityDef ent)
{
int objectIndex = -1;
for (int i = 0; i < entities.Length; i++)
{
MCEntityDef e = entities[i];
if (e == ent)
{
objectIndex = i;
break;
}
}
if (objectIndex == -1) return null;
MCMloRoomDef room = null;
for (int i = 0; i < rooms.Length; i++)
{
MCMloRoomDef r = rooms[i];
for (int j = 0; j < r.AttachedObjects.Length; j++)
{
uint ind = r.AttachedObjects[j];
if (ind == objectIndex)
{
room = r;
break;
}
}
if (room != null) break;
}
return room;
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class MloInstanceData
{
@@ -204,7 +345,12 @@ namespace CodeWalker.GameFiles
public uint[] defaultEntitySets { get; set; }
public YmapEntityDef[] Entities { get; set; }
public Dictionary<MetaHash, MloInstanceEntitySet> EntitySets { get; set; }
public MloInstanceData()
{
EntitySets = new Dictionary<MetaHash, MloInstanceEntitySet>();
}
public void CreateYmapEntities(YmapEntityDef owner, MloArchetype mloa)
{
@@ -230,27 +376,182 @@ namespace CodeWalker.GameFiles
var entitySet = entitySets[i];
if (entitySet.Entities != null)
{
EntitySets[entitySet._Data.name] = new MloInstanceEntitySet(entitySet, this);
MloInstanceEntitySet instset = EntitySets[entitySet._Data.name];
for (int j = 0; j < entitySet.Entities.Length; j++)
{
YmapEntityDef e = CreateYmapEntity(owner, entitySet.Entities[j], lasti);
e.MloEntitySet = entitySet;
entlist.Add(e);
EntitySets[entitySet._Data.name].Entities.Add(e);
e.MloEntitySet = instset;
lasti++;
}
}
}
}
if (defaultEntitySets != null)
if ((defaultEntitySets != null) && (entitySets != null))
{
for (var i = 0; i < defaultEntitySets.Length; i++)
{
uint index = defaultEntitySets[i];
if (index >= entitySets.Length) continue;
MCMloEntitySet set = entitySets[index];
MloInstanceEntitySet instset = EntitySets[set._Data.name];
instset.Visible = true;
}
}
Entities = entlist.ToArray();
}
private YmapEntityDef CreateYmapEntity(YmapEntityDef owner, MCEntityDef ment, int i)
public void InitYmapEntityArchetypes(GameFileCache gfc)
{
YmapEntityDef e = new YmapEntityDef(null, i, ref ment._Data);
if (Owner == null) return;
var arch = Owner.Archetype;
if (Entities != null)
{
for (int j = 0; j < Entities.Length; j++)
{
var ient = Entities[j];
var iarch = gfc.GetArchetype(ient.CEntityDef.archetypeName);
ient.SetArchetype(iarch);
if (iarch == null)
{ } //can't find archetype - des stuff eg {des_prologue_door}
}
UpdateBBs(arch);
}
if (EntitySets != null)
{
foreach (var entitySet in EntitySets)
{
var entities = entitySet.Value.Entities;
if (entities == null) continue;
for (int i = 0; i < entities.Count; i++)
{
var ient = entities[i];
var iarch = gfc.GetArchetype(ient.CEntityDef.archetypeName);
ient.SetArchetype(iarch);
if (iarch == null)
{ } //can't find archetype - des stuff eg {des_prologue_door}
}
}
}
}
public void UpdateBBs(Archetype arch)
{
//update archetype room AABB's.. bad to have this here? where else to put it?
var mloa = arch as MloArchetype;
if (mloa != null)
{
Vector3 mlobbmin = Vector3.Zero;
Vector3 mlobbmax = Vector3.Zero;
Vector3[] c = new Vector3[8];
var rooms = mloa.rooms;
if (rooms != null)
{
for (int j = 0; j < rooms.Length; j++)
{
var room = rooms[j];
if ((room.AttachedObjects == null) || (room.AttachedObjects.Length == 0)) continue;
Vector3 min = new Vector3(float.MaxValue);
Vector3 max = new Vector3(float.MinValue);
for (int k = 0; k < room.AttachedObjects.Length; k++)
{
var objid = room.AttachedObjects[k];
if (objid < Entities.Length)
{
var rooment = Entities[objid];
if ((rooment != null) && (rooment.Archetype != null))
{
var earch = rooment.Archetype;
var pos = rooment._CEntityDef.position;
var ori = rooment.Orientation;
Vector3 abmin = earch.BBMin * rooment.Scale; //entity box
Vector3 abmax = earch.BBMax * rooment.Scale;
c[0] = abmin;
c[1] = new Vector3(abmin.X, abmin.Y, abmax.Z);
c[2] = new Vector3(abmin.X, abmax.Y, abmin.Z);
c[3] = new Vector3(abmin.X, abmax.Y, abmax.Z);
c[4] = new Vector3(abmax.X, abmin.Y, abmin.Z);
c[5] = new Vector3(abmax.X, abmin.Y, abmax.Z);
c[6] = new Vector3(abmax.X, abmax.Y, abmin.Z);
c[7] = abmax;
for (int n = 0; n < 8; n++)
{
Vector3 corn = ori.Multiply(c[n]) + pos;
min = Vector3.Min(min, corn);
max = Vector3.Max(max, corn);
}
}
}
}
room.BBMin_CW = min;
room.BBMax_CW = max;
mlobbmin = Vector3.Min(mlobbmin, min);
mlobbmax = Vector3.Max(mlobbmax, max);
}
}
mloa.BBMin = mlobbmin;
mloa.BBMax = mlobbmax;
}
}
public bool DeleteEntity(YmapEntityDef ent)
{
if (Entities == null)
{
throw new NullReferenceException("The Entities list returned null in our MloInstanceData. This could be an issue with initialization. The MloInstance probably doesn't exist.");
}
if (ent.Index >= Entities.Length)
{
throw new ArgumentOutOfRangeException("The index of the entity was greater than the amount of entities that exist in this MloInstance. Likely an issue with initializion.");
}
int index = 0;
YmapEntityDef[] newentities = new YmapEntityDef[Entities.Length - 1];
YmapEntityDef delentity = Entities[ent.Index];
bool del = false;
for (int i = 0; i < Entities.Length; i++)
{
if (Entities[i] == delentity)
{
del = true;
continue;
}
newentities[index] = Entities[i];
newentities[index].Index = index;
index++;
}
if (!del)
throw new ArgumentException("The entity specified was not found in this MloInstance. It cannot be deleted.");
if (Owner.Archetype is MloArchetype arch)
{
if (arch.RemoveEntity(ent))
{
if (ent.MloEntitySet != null)
if (!ent.MloEntitySet.Entities.Remove(ent))
return false;
// Delete was successful...
Entities = newentities;
return true;
}
}
throw new InvalidCastException("The owner of this archetype's archetype definition is not an MloArchetype.");
}
public YmapEntityDef CreateYmapEntity(YmapEntityDef owner, MCEntityDef ment, int index)
{
YmapEntityDef e = new YmapEntityDef(null, index, ref ment._Data);
e.Extensions = ment.Extensions;
e.MloRefPosition = e.Position;
e.MloRefOrientation = e.Orientation;
@@ -259,9 +560,31 @@ namespace CodeWalker.GameFiles
e.Orientation = Quaternion.Multiply(owner.Orientation, e.MloRefOrientation);
e.UpdateWidgetPosition();
e.UpdateWidgetOrientation();
return e;
}
public MCEntityDef TryGetArchetypeEntity(YmapEntityDef ymapEntity)
{
if (ymapEntity == null) return null;
if (Owner?.Archetype == null) return null;
if (!(Owner.Archetype is MloArchetype mloa)) return null;
if (ymapEntity.Index >= mloa.entities.Length) return null;
var entity = mloa.entities[ymapEntity.Index];
return entity;
}
public YmapEntityDef TryGetYmapEntity(MCEntityDef mcEntity)
{
if (mcEntity == null) return null;
if (Owner?.Archetype == null) return null;
if (!(Owner.Archetype is MloArchetype mloa)) return null;
var index = Array.FindIndex(mloa.entities, x => x == mcEntity);
if (index == -1 || index >= Entities.Length) return null;
return Entities[index];
}
public void SetPosition(Vector3 pos)
{
@@ -285,17 +608,52 @@ namespace CodeWalker.GameFiles
for (int i = 0; i < Entities.Length; i++)
{
YmapEntityDef e = Entities[i];
e.Position = Owner.Position + Owner.Orientation.Multiply(e.MloRefPosition);
e.Orientation = Quaternion.Multiply(Owner.Orientation, e.MloRefOrientation);
e.UpdateWidgetPosition();
e.UpdateWidgetOrientation();
UpdateEntity(e);
}
}
public void UpdateEntity(YmapEntityDef e)
{
e.Position = Owner.Position + Owner.Orientation.Multiply(e.MloRefPosition);
e.Orientation = Quaternion.Multiply(Owner.Orientation, e.MloRefOrientation);
e.UpdateWidgetPosition();
e.UpdateWidgetOrientation();
}
public void AddEntity(YmapEntityDef e)
{
if (e == null) return;
if (Entities == null) Entities = new YmapEntityDef[0];
var entities = Entities.ToList();
entities.Add(e);
Entities = entities.ToArray();
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public class MloInstanceEntitySet
{
public MloInstanceEntitySet(MCMloEntitySet entSet, MloInstanceData instance)
{
EntitySet = entSet;
Entities = new List<YmapEntityDef>();
Instance = instance;
}
public MCMloEntitySet EntitySet { get; set; }
public List<YmapEntityDef> Entities { get; set; }
public MloInstanceData Instance { get; set; }
public uint[] Locations
{
get { return EntitySet?.Locations; }
set { if (EntitySet != null) EntitySet.Locations = value; }
}
public bool Visible { get; set; }
}
}
@@ -935,6 +935,10 @@ namespace CodeWalker.GameFiles
[TC(typeof(EXP))] public struct ArrayOfUshorts3 //array of 3 ushorts
{
public ushort u0, u1, u2;
public Vector3 XYZ()
{
return new Vector3(u0, u1, u2);
}
public override string ToString()
{
return u0.ToString() + ", " + u1.ToString() + ", " + u2.ToString();
@@ -538,6 +538,7 @@ namespace CodeWalker.GameFiles
CExtensionDefSpawnPointOverride = 2716862120,
CExtensionDefWindDisturbance = 569228403,
CFiringPatternInfo = 4139644871,
CFiringPatternInfoManager = 3698419088,
CGrabHelper__Tunables = 1898505781,
Chances = 3434267272,
changeSetName = 3618800523,
@@ -1605,6 +1606,7 @@ namespace CodeWalker.GameFiles
VEHICLE_LAYOUTS_FILE = 2004032454,
VEHICLE_METADATA_FILE = 4125139733,
VEHICLE_POPULATION_FILE = 4010054647,
VEHICLE_RESPONSE_DEFAULT = 3986150789,
VEHICLE_RESPONSE_ARMY_BASE = 317362887,
VEHICLE_RESPONSE_COUNTRYSIDE = 2467847847,
VEHICLE_SHOP_DLC_FILE = 3203173146,
@@ -1621,6 +1623,13 @@ namespace CodeWalker.GameFiles
VFXINTERIORINFO_FILE = 354822867,
VFXPEDINFO_FILE = 962370952,
VFXREGIONINFO_FILE = 3633596549,
vfxregioninfo_default = 526963733,
vfxregioninfo_desert = 1202232026,
vfxregioninfo_beach = 4239901007,
vfxregioninfo_slum = 4267832995,
vfxregioninfo_woodland = 1397181648,
vfxregioninfo_mountain = 3282595980,
vfxregioninfo_countryside = 2691334223,
vfxTagHashName = 1944993828,
VFXVEHICLEINFO_FILE = 1918258814,
vfxVehicleInfos = 1829968483,
@@ -3528,9 +3537,15 @@ namespace CodeWalker.GameFiles
spName = 4254542050,
//from rubidium
//from rubidium / dav90 PSO XML / zonebind
mpName = 2031203854,
zoneName = 257000,
vfxRegion = 3384955624,
vehDirtMin = 1831590592,
vehDirtMax = 625824556,
vehDirtGrowScale = 2919812941,
pedDirtMin = 1861946207,
pedDirtMax = 3150688023,
@@ -2428,7 +2428,9 @@ namespace CodeWalker.GameFiles
public Vector3 BBMax { get { return (_Data.bbMax); } }
public Vector3 BBMin_CW { get; set; }
public Vector3 BBMax_CW { get; set; }
public MloArchetype Archetype { get; set; }
public int Index { get; set; }
public MCMloRoomDef() { }
public MCMloRoomDef(Meta meta, CMloRoomDef data)
@@ -2610,7 +2612,7 @@ namespace CodeWalker.GameFiles
Entities = new MCEntityDef[ents.Length];
for (int i = 0; i < ents.Length; i++)
{
Entities[i] = new MCEntityDef(meta, ents[i]);
Entities[i] = new MCEntityDef(meta, ref ents[i]);
}
}
}
@@ -2750,23 +2752,26 @@ namespace CodeWalker.GameFiles
[TC(typeof(EXP))] public class MCEntityDef : MetaWrapper
{
public CEntityDef _Data;
public CEntityDef Data { get { return _Data; } }
public CEntityDef Data { get { return _Data; } set { _Data = value; } }
public MetaWrapper[] Extensions { get; set; }
public MCEntityDef() { }
public MloArchetype Archetype { get; set; } // for browsing/reference purposes
public MCEntityDef(MCEntityDef copy)
{
if (copy != null)
{
_Data = copy.Data;
}
if (copy != null) _Data = copy.Data;
}
public MCEntityDef(Meta meta, CEntityDef d)
public MCEntityDef(Meta meta, ref CEntityDef d)
{
_Data = d;
Extensions = MetaTypes.GetExtensions(meta, _Data.extensions);
}
public MCEntityDef(ref CEntityDef d, MloArchetype arch)
{
_Data = d;
Archetype = arch;
}
public override void Load(Meta meta, MetaPOINTER ptr)
{
@@ -1646,7 +1646,12 @@ namespace CodeWalker.GameFiles
public static string FormatHash(MetaHash h) //for use with WriteItemArray
{
return h.ToString();
var str = JenkIndex.TryGetString(h);
if (!string.IsNullOrEmpty(str)) return str;
str = GlobalText.TryGetString(h);
if (!string.IsNullOrEmpty(str)) return str;
return HashString(h);// "hash_" + h.Hex;
//return h.ToString();
}
public static string FormatVector2(Vector2 v) //for use with WriteItemArray
{
@@ -23,6 +23,95 @@
//shamelessly stolen and mangled
/*
Regarding saving PSO files:
[for checksum - use whole file]
Brick - Today
uint32_t joaat_checksum(const void* memory, const size_t length)
{
uint32_t hash = 0x3FAC7125;
for (size_t i = 0; i < length; ++i)
{
hash += static_cast<const int8_t*>(memory)[i];
hash += hash << 10;
hash ^= hash >> 6;
}
hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;
return hash;
}
[before doing checksum for file:]
v12->Checksum = 0;
v12->FileSize = 0;
v12->Magic = 'SKHC';
v12->Size = 0x14000000;
v22 = v12;
LOBYTE(v12->Platform) = platformChar[0];
Brick - Today
This is a table i made a while ago for the pso types btw
| Index | Type | Size | Align | Name | Serialization
| 0 | Simple | 1 | 1 | bool |
| 1 | Simple | 1 | 1 | s8 |
| 2 | Simple | 1 | 1 | u8 |
| 3 | Simple | 2 | 2 | s16 |
| 4 | Simple | 2 | 2 | u16 |
| 5 | Simple | 4 | 4 | s32 |
| 6 | Simple | 4 | 4 | u32 |
| 7 | Simple | 4 | 4 | f32 |
| 8 | Vector | 8 | 4 | vec2 |
| 9 | Vector | 16 | 16 | vec3 |
| 10 | Vector | 16 | 16 | vec4 |
| 11 | String | 0 | 0 | string |
| 12 | Struct | 0 | 0 | struct |
| 13 | Array | 0 | 0 | array |
| 14 | Enum | 0 | 0 | enum |
| 15 | Bitset | 0 | 0 | bitset |
| 16 | Map | 0 | 0 | map |
| 17 | Matrix | 64 | 16 | matrix43 | shuffled
| 18 | Matrix | 64 | 16 | matrix44 | shuffled
| 19 | Vector | 16 | 16 | vec4 | x, y, x, x
| 20 | Vector | 16 | 16 | vec4 | x, y, z, x
| 21 | Vector | 16 | 16 | vec4 | x, y, z, w
| 22 | Matrix | 48 | 16 | matrix34 |
| 23 | Matrix | 64 | 16 | matrix43 |
| 24 | Matrix | 64 | 16 | matrix44 |
| 25 | Simple | 16 | 16 | f32_vec4 | fill all with f32
| 26 | Simple | 16 | 16 | bool_int4 | fill all with 0x00000000 or 0xFFFFFFFF depending on bool
| 27 | Vector | 16 | 16 | bool4_int4 | fill each with 0x00000000 or 0xFFFFFFFF depending on bools
| 28 | Simple | 8 | 8 | s32_i64 | sign extended s32
| 29 | Simple | 8 | 8 | s32_u64 | sign extended s32
| 30 | Simple | 2 | 2 | f16 | f64 converted to f16
| 31 | Simple | 8 | 8 | s64 |
| 32 | Simple | 8 | 8 | u64 |
| 33 | Simple | 8 | 8 | f64 |
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
+100 -46
View File
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Xml;
using SharpDX;
@@ -182,7 +183,7 @@ namespace CodeWalker.GameFiles
mb.AddEnumInfo(_infos.EnumNameHash);
int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText);
int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText, entry.DataType);
Write(val, data, entry.DataOffset);
break;
}
@@ -193,7 +194,7 @@ namespace CodeWalker.GameFiles
mb.AddEnumInfo(_infos.EnumNameHash);
int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText);
int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText, entry.DataType);
Write((short)val, data, entry.DataOffset);
break;
}
@@ -296,11 +297,16 @@ namespace CodeWalker.GameFiles
private static void GetParsedArrayOfBytes(XmlNode node, byte[] data, MetaStructureEntryInfo_s entry, MetaStructureEntryInfo_s arrEntry)
{
int offset = entry.DataOffset;
string[] split;
var ns = NumberStyles.Any;
var ic = CultureInfo.InvariantCulture;
var sa = new[] { ' ' };
var so = StringSplitOptions.RemoveEmptyEntries;
var split = node.InnerText.Trim().Split(sa, so); //split = Split(node.InnerText, 2); to read as unsplitted HEX
switch (arrEntry.DataType)
{
default:
default: //expecting hex string.
split = Split(node.InnerText, 2);
for (int j = 0; j < split.Length; j++)
{
@@ -309,67 +315,81 @@ namespace CodeWalker.GameFiles
offset += sizeof(byte);
}
break;
case MetaStructureEntryDataType.SignedByte:
split = node.InnerText.Split(); //split = Split(node.InnerText, 2); to read as unsplitted HEX
case MetaStructureEntryDataType.SignedByte: //expecting space-separated array.
for (int j = 0; j < split.Length; j++)
{
sbyte val = Convert.ToSByte(split[j], 10);
data[offset] = (byte)val;
offset += sizeof(sbyte);
sbyte val;// = Convert.ToSByte(split[j], 10);
if (sbyte.TryParse(split[j].Trim(), ns, ic, out val))
{
data[offset] = (byte)val;
offset += sizeof(sbyte);
}
}
break;
case MetaStructureEntryDataType.UnsignedByte:
split = node.InnerText.Split();
case MetaStructureEntryDataType.UnsignedByte: //expecting space-separated array.
for (int j = 0; j < split.Length; j++)
{
byte val = Convert.ToByte(split[j], 10);
data[offset] = val;
offset += sizeof(byte);
byte val;// = Convert.ToByte(split[j], 10);
if (byte.TryParse(split[j].Trim(), ns, ic, out val))
{
data[offset] = val;
offset += sizeof(byte);
}
}
break;
case MetaStructureEntryDataType.SignedShort:
split = node.InnerText.Split();
case MetaStructureEntryDataType.SignedShort: //expecting space-separated array.
for (int j = 0; j < split.Length; j++)
{
short val = Convert.ToInt16(split[j], 10);
Write(val, data, offset);
offset += sizeof(short);
short val;// = Convert.ToInt16(split[j], 10);
if (short.TryParse(split[j].Trim(), ns, ic, out val))
{
Write(val, data, offset);
offset += sizeof(short);
}
}
break;
case MetaStructureEntryDataType.UnsignedShort:
split = node.InnerText.Split();
case MetaStructureEntryDataType.UnsignedShort: //expecting space-separated array.
for (int j = 0; j < split.Length; j++)
{
ushort val = Convert.ToUInt16(split[j], 10);
Write(val, data, offset);
offset += sizeof(ushort);
ushort val;// = Convert.ToUInt16(split[j], 10);
if (ushort.TryParse(split[j].Trim(), ns, ic, out val))
{
Write(val, data, offset);
offset += sizeof(ushort);
}
}
break;
case MetaStructureEntryDataType.SignedInt:
split = node.InnerText.Split();
case MetaStructureEntryDataType.SignedInt: //expecting space-separated array.
for (int j = 0; j < split.Length; j++)
{
int val = Convert.ToInt32(split[j], 10);
Write(val, data, offset);
offset += sizeof(int);
int val;// = Convert.ToInt32(split[j], 10);
if (int.TryParse(split[j].Trim(), ns, ic, out val))
{
Write(val, data, offset);
offset += sizeof(int);
}
}
break;
case MetaStructureEntryDataType.UnsignedInt:
split = node.InnerText.Split();
case MetaStructureEntryDataType.UnsignedInt: //expecting space-separated array.
for (int j = 0; j < split.Length; j++)
{
uint val = Convert.ToUInt32(split[j], 10);
Write(val, data, offset);
offset += sizeof(uint);
uint val;// = Convert.ToUInt32(split[j], 10);
if (uint.TryParse(split[j].Trim(), ns, ic, out val))
{
Write(val, data, offset);
offset += sizeof(uint);
}
}
break;
case MetaStructureEntryDataType.Float:
split = node.InnerText.Split();
case MetaStructureEntryDataType.Float: //expecting space-separated array.
for (int j = 0; j < split.Length; j++)
{
float val = FloatUtil.Parse(split[j]);
Write(val, data, offset);
offset += sizeof(float);
float val;// = FloatUtil.Parse(split[j]);
if (FloatUtil.TryParse(split[j].Trim(), out val))
{
Write(val, data, offset);
offset += sizeof(float);
}
}
break;
}
@@ -685,9 +705,8 @@ namespace CodeWalker.GameFiles
return chunks.ToArray();
}
private static int GetEnumInt(MetaName type, string enumString)
private static int GetEnumInt(MetaName type, string enumString, MetaStructureEntryDataType dataType)
{
var enumName = (MetaName)(uint)GetHash(enumString);
var infos = MetaTypes.GetEnumInfo(type);
if (infos == null)
@@ -695,13 +714,48 @@ namespace CodeWalker.GameFiles
return 0;
}
for (int j = 0; j < infos.Entries.Length; j++)
{
var entry = infos.Entries[j];
if (entry.EntryNameHash == enumName)
bool isFlags = (dataType == MetaStructureEntryDataType.IntFlags1) ||
(dataType == MetaStructureEntryDataType.IntFlags2);// ||
//(dataType == MetaStructureEntryDataType.ShortFlags);
if (isFlags)
{
//flags enum. (multiple values, comma-separated)
var split = enumString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
int enumVal = 0;
for (int i = 0; i < split.Length; i++)
{
return entry.EntryValue;
var enumName = (MetaName)(uint)GetHash(split[i].Trim());
for (int j = 0; j < infos.Entries.Length; j++)
{
var entry = infos.Entries[j];
if (entry.EntryNameHash == enumName)
{
enumVal += (1 << entry.EntryValue);
break;
}
}
}
return enumVal;
}
else
{
//single value enum.
var enumName = (MetaName)(uint)GetHash(enumString);
for (int j = 0; j < infos.Entries.Length; j++)
{
var entry = infos.Entries[j];
if (entry.EntryNameHash == enumName)
{
return entry.EntryValue;
}
}
}
+55 -21
View File
@@ -1283,7 +1283,7 @@ namespace CodeWalker.GameFiles
public string HeatsTyre { get; set; }
public string Material { get; set; }
public Color4 Colour { get; set; }
public Color Colour { get; set; }
public override string ToString()
{
@@ -1293,14 +1293,19 @@ namespace CodeWalker.GameFiles
public static class BoundsMaterialTypes
{
private static Dictionary<string, Color4> ColourDict;
private static Dictionary<string, Color> ColourDict;
private static List<BoundsMaterialData> Materials;
public static void Init(GameFileCache gameFileCache)
{
var rpfman = gameFileCache.RpfMan;
InitColours();
var dic = new Dictionary<string,Color>();
string filename2 = "common.rpf\\data\\effects\\materialfx.dat";
string txt2 = rpfman.GetFileUTF8Text(filename2);
AddMaterialfxDat(txt2, dic);
ColourDict = dic;
var list = new List<BoundsMaterialData>();
string filename = "common.rpf\\data\\materials\\materials.dat";
@@ -1314,25 +1319,47 @@ namespace CodeWalker.GameFiles
Materials = list;
}
private static void InitColours()
//Only gets the colors
private static void AddMaterialfxDat(string txt, Dictionary<string, Color> dic)
{
var dict = new Dictionary<string, Color4>();
string txt = File.ReadAllText("Materials.txt");
string[] lines = txt.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
dic.Clear();
if (txt == null) return;
string[] lines = txt.Split('\n');
string startLine = "MTLFX_TABLE_START";
string endLine = "MTLFX_TABLE_END";
for (int i = 1; i < lines.Length; i++)
{
var line = lines[i];
if (line.Length < 10) continue;
if (line[0] == '#') continue;
if (line.StartsWith(startLine)) continue;
if (line.StartsWith(endLine)) break;
string[] parts = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2) continue;
string name = parts[0].Trim();
string cstr = parts[1].Trim();
uint cval = Convert.ToUInt32(cstr, 16);
Color4 c = new Color4(cval);
dict[name] = c;
if (parts.Length < 5) continue; // FXGroup R G B ...
int cp = 0;
Color c = new Color();
c.A = 0xFF;
string fxgroup = string.Empty;
for (int p = 0; p < parts.Length; p++)
{
string part = parts[p].Trim();
if (string.IsNullOrWhiteSpace(part)) continue;
switch (cp)
{
case 0: fxgroup = part; break;
case 1: c.R = byte.Parse(part); break;
case 2: c.G = byte.Parse(part); break;
case 3: c.B = byte.Parse(part); break;
}
cp++;
}
dic.Add(fxgroup, c);
}
ColourDict = dict;
}
private static void AddMaterialsDat(string txt, List<BoundsMaterialData> list)
@@ -1384,14 +1411,14 @@ namespace CodeWalker.GameFiles
if (cp != 23)
{ }
Color4 c;
if ((ColourDict != null) && (ColourDict.TryGetValue(d.Name, out c)))
Color c;
if ((ColourDict != null) && (ColourDict.TryGetValue(d.FXGroup, out c)))
{
d.Colour = c;
}
else
{
d.Colour = new Color4(0xFFCCCCCC);
d.Colour = new Color(0xFFCCCCCC);
}
@@ -1416,6 +1443,13 @@ namespace CodeWalker.GameFiles
return Materials[type.Index];
}
public static BoundsMaterialData GetMaterial(byte index)
{
if (Materials == null) return null;
if ((int)index >= Materials.Count) return null;
return Materials[index];
}
public static string GetMaterialName(BoundsMaterialType type)
{
var m = GetMaterial(type);
@@ -1423,10 +1457,10 @@ namespace CodeWalker.GameFiles
return m.Name;
}
public static Color4 GetMaterialColour(BoundsMaterialType type)
public static Color GetMaterialColour(BoundsMaterialType type)
{
var m = GetMaterial(type);
if (m == null) return new Color4(0xFFCCCCCC);
if (m == null) return new Color(0xFFCCCCCC);
return m.Colour;
}
}
+19 -23
View File
@@ -718,16 +718,16 @@ namespace CodeWalker.GameFiles
//public float TranslationX { get; set; }
//public float TranslationY { get; set; }
//public float TranslationZ { get; set; }
public uint Unknown_1Ch { get; set; } // 0x00000000
public float Unknown_20h { get; set; } // 1.0
public float Unknown_24h { get; set; } // 1.0
public float Unknown_28h { get; set; } // 1.0
public float Unknown_2Ch { get; set; } // 1.0
public ushort Unknown_30h { get; set; } //limb end index? IK chain?
public uint Unknown_1Ch { get; set; } // 0x00000000 RHW?
public float ScaleX { get; set; } // 1.0
public float ScaleY { get; set; } // 1.0
public float ScaleZ { get; set; } // 1.0
public float Unknown_2Ch { get; set; } // 1.0 RHW?
public ushort NextSiblingIndex { get; set; } //limb end index? IK chain?
public short ParentIndex { get; set; }
public uint Unknown_34h { get; set; } // 0x00000000
public ulong NamePointer { get; set; }
public ushort Unknown_40h { get; set; }
public ushort Flags { get; set; }
public ushort Unknown_42h { get; set; }
public ushort Id { get; set; }
public ushort Unknown_46h { get; set; }
@@ -756,15 +756,15 @@ namespace CodeWalker.GameFiles
//this.TranslationY = reader.ReadSingle();
//this.TranslationZ = reader.ReadSingle();
this.Unknown_1Ch = reader.ReadUInt32();
this.Unknown_20h = reader.ReadSingle();
this.Unknown_24h = reader.ReadSingle();
this.Unknown_28h = reader.ReadSingle();
this.ScaleX = reader.ReadSingle();
this.ScaleY = reader.ReadSingle();
this.ScaleZ = reader.ReadSingle();
this.Unknown_2Ch = reader.ReadSingle();
this.Unknown_30h = reader.ReadUInt16();
this.NextSiblingIndex = reader.ReadUInt16();
this.ParentIndex = reader.ReadInt16();
this.Unknown_34h = reader.ReadUInt32();
this.NamePointer = reader.ReadUInt64();
this.Unknown_40h = reader.ReadUInt16();
this.Flags = reader.ReadUInt16();
this.Unknown_42h = reader.ReadUInt16();
this.Id = reader.ReadUInt16();
this.Unknown_46h = reader.ReadUInt16();
@@ -796,15 +796,15 @@ namespace CodeWalker.GameFiles
//writer.Write(this.TranslationY);
//writer.Write(this.TranslationZ);
writer.Write(this.Unknown_1Ch);
writer.Write(this.Unknown_20h);
writer.Write(this.Unknown_24h);
writer.Write(this.Unknown_28h);
writer.Write(this.ScaleX);
writer.Write(this.ScaleY);
writer.Write(this.ScaleZ);
writer.Write(this.Unknown_2Ch);
writer.Write(this.Unknown_30h);
writer.Write(this.NextSiblingIndex);
writer.Write(this.ParentIndex);
writer.Write(this.Unknown_34h);
writer.Write(this.NamePointer);
writer.Write(this.Unknown_40h);
writer.Write(this.Flags);
writer.Write(this.Unknown_42h);
writer.Write(this.Id);
writer.Write(this.Unknown_46h);
@@ -968,12 +968,8 @@ namespace CodeWalker.GameFiles
public float Unknown_50h { get; set; } // -pi
public float Unknown_54h { get; set; } // pi
public float Unknown_58h { get; set; } // 1.0
public float Unknown_5Ch { get; set; }
public float Unknown_60h { get; set; }
public float Unknown_64h { get; set; }
public float Unknown_68h { get; set; }
public float Unknown_6Ch { get; set; }
public float Unknown_70h { get; set; }
public Vector3 Min { get; set; }
public Vector3 Max { get; set; }
public float Unknown_74h { get; set; } // pi
public float Unknown_78h { get; set; } // -pi
public float Unknown_7Ch { get; set; } // pi
+125 -30
View File
@@ -54,8 +54,8 @@ namespace CodeWalker.GameFiles
public uint Unused_078h { get; set; } // 0x00000000
public uint Unused_07Ch { get; set; } // 0x00000000
public ulong IndicesPointer { get; set; }
public ulong AdjPolysPointer { get; set; }
public uint AdjPolysIndicesCount { get; set; }
public ulong EdgesPointer { get; set; }
public uint EdgesIndicesCount { get; set; }
public NavMeshUintArray AdjAreaIDs { get; set; }
public ulong PolysPointer { get; set; }
public ulong SectorTreePointer { get; set; }
@@ -79,7 +79,7 @@ namespace CodeWalker.GameFiles
public NavMeshList<NavMeshVertex> Vertices { get; set; }
public NavMeshList<ushort> Indices { get; set; }
public NavMeshList<NavMeshAdjPoly> AdjPolys { get; set; }
public NavMeshList<NavMeshEdge> Edges { get; set; }
public NavMeshList<NavMeshPoly> Polys { get; set; }
public NavMeshSector SectorTree { get; set; }
public NavMeshPortal[] Portals { get; set; }
@@ -109,8 +109,8 @@ namespace CodeWalker.GameFiles
Unused_078h = reader.ReadUInt32();
Unused_07Ch = reader.ReadUInt32();
IndicesPointer = reader.ReadUInt64();
AdjPolysPointer = reader.ReadUInt64();
AdjPolysIndicesCount = reader.ReadUInt32();
EdgesPointer = reader.ReadUInt64();
EdgesIndicesCount = reader.ReadUInt32();
AdjAreaIDs = reader.ReadStruct<NavMeshUintArray>();
PolysPointer = reader.ReadUInt64();
SectorTreePointer = reader.ReadUInt64();
@@ -135,7 +135,7 @@ namespace CodeWalker.GameFiles
Vertices = reader.ReadBlockAt<NavMeshList<NavMeshVertex>>(VerticesPointer);
Indices = reader.ReadBlockAt<NavMeshList<ushort>>(IndicesPointer);
AdjPolys = reader.ReadBlockAt<NavMeshList<NavMeshAdjPoly>>(AdjPolysPointer);
Edges = reader.ReadBlockAt<NavMeshList<NavMeshEdge>>(EdgesPointer);
Polys = reader.ReadBlockAt<NavMeshList<NavMeshPoly>>(PolysPointer);
SectorTree = reader.ReadBlockAt<NavMeshSector>(SectorTreePointer);
Portals = reader.ReadStructsAt<NavMeshPortal>(PortalsPointer, PortalsCount);
@@ -150,7 +150,7 @@ namespace CodeWalker.GameFiles
VerticesPointer = (ulong)(Vertices != null ? Vertices.FilePosition : 0);
IndicesPointer = (ulong)(Indices != null ? Indices.FilePosition : 0);
AdjPolysPointer = (ulong)(AdjPolys != null ? AdjPolys.FilePosition : 0);
EdgesPointer = (ulong)(Edges != null ? Edges.FilePosition : 0);
PolysPointer = (ulong)(Polys != null ? Polys.FilePosition : 0);
SectorTreePointer = (ulong)(SectorTree != null ? SectorTree.FilePosition : 0);
PortalsPointer = (ulong)(PortalsBlock?.FilePosition ?? 0);
@@ -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);
@@ -169,8 +202,8 @@ namespace CodeWalker.GameFiles
writer.Write(Unused_078h);
writer.Write(Unused_07Ch);
writer.Write(IndicesPointer);
writer.Write(AdjPolysPointer);
writer.Write(AdjPolysIndicesCount);
writer.Write(EdgesPointer);
writer.Write(EdgesIndicesCount);
writer.WriteStruct(AdjAreaIDs);
writer.Write(PolysPointer);
writer.Write(SectorTreePointer);
@@ -192,13 +225,19 @@ 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());
if (Vertices != null) list.Add(Vertices);
if (Indices != null) list.Add(Indices);
if (AdjPolys != null) list.Add(AdjPolys);
if (Edges != null) list.Add(Edges);
if (Polys != null) list.Add(Polys);
if (SectorTree != null) list.Add(SectorTree);
@@ -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;
}
@@ -592,19 +684,21 @@ namespace CodeWalker.GameFiles
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshAdjPoly
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshEdge
{
public NavMeshAdjPolyPart Unknown_0h { get; set; }
public NavMeshAdjPolyPart 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();
}
}
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshAdjPolyPart
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshEdgePart
{
public uint Value { get; set; }
@@ -616,14 +710,15 @@ 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()
{
return AreaIDInd.ToString() + ", " + PolyID.ToString() + ", " + Unk2.ToString() + ", " + Unk3.ToString();
string pid = (PolyID == 0x3FFF) ? "-" : PolyID.ToString();
return AreaIDInd.ToString() + ", " + pid + ", " + Unk2.ToString() + ", " + Unk3.ToString();
}
}
@@ -642,16 +737,16 @@ namespace CodeWalker.GameFiles
public NavMeshAABB CellAABB { get; set; }
public FlagsUint Unknown_24h { get; set; }
public FlagsUint Unknown_28h { get; set; }
public ushort PartFlags { get; set; }
public ushort PortalLinkID { get; set; }
public uint PartFlags { get; set; }
//public int IndexUnk { get { return (IndexFlags >> 0) & 31; } } //always 0
public int IndexCount { get { return (IndexFlags >> 5); } }
public int IndexCount { get { return (IndexFlags >> 5); } set { IndexFlags = (ushort)((IndexFlags & 31) | ((value & 0x7FF) << 5)); } }
//public int PartUnk1 { get { return (PartFlags >> 0) & 0xF; } } //always 0
public ushort PartID { get { return (ushort)((PartFlags >> 4) & 0xFF); } set { PartFlags = (ushort)((PartFlags & 0xF00F) | ((value & 0xFF) << 4)); } }
public byte PortalType { get { return (byte)((PartFlags >> 12) & 0xF); } set { PartFlags = (ushort)((PartFlags & 0x0FFF) | ((value & 0xF) << 12)); } }
public ushort PartID { get { return (ushort)((PartFlags >> 4) & 0xFF); } set { PartFlags = ((PartFlags & 0xFFFFF00F) | (((uint)value & 0xFF) << 4)); } }
public byte PortalLinkCount { get { return (byte)((PartFlags >> 12) & 0x7); } set { PartFlags = ((PartFlags & 0xFFFF8FFF) | (((uint)value & 0x7) << 12)); } }
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); } }
@@ -672,7 +767,7 @@ namespace CodeWalker.GameFiles
Unknown_28h.Hex + ", " +
//PartFlags.ToString() + ", " + //PartUnk1.ToString() + ", " +
PartID.ToString() + ", " +
PortalType.ToString() + ", " +
PortalLinkCount.ToString() + ", " +
PortalLinkID.ToString();
}
}
@@ -790,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)
{
+1
View File
@@ -626,6 +626,7 @@ namespace CodeWalker.GameFiles
RpfFileEntry entry = CreateResourceFileEntry(ref data, 0);
if ((data != null) && (entry != null))
{
data = ResourceBuilder.Decompress(data);
file = new T();
file.Load(data, entry);
}