Replaced world grid with quadtrees, improved loading speed of cutscenes

This commit is contained in:
dexy 2019-12-05 21:05:31 +11:00
parent d59663d87e
commit a5259c17fe
10 changed files with 887 additions and 926 deletions

View File

@ -625,6 +625,22 @@ namespace CodeWalker.GameFiles
}
}
public void GetVisibleChildren(ref Vector3 p, List<MapDataStoreNode> items)
{
if (Children == null) return;
for (int i = 0; i < Children.Length; i++)
{
var c = Children[i];
if (c == null) continue;
var cmin = c.streamingExtentsMin;
var cmax = c.streamingExtentsMax;
if ((p.X >= cmin.X) && (p.X <= cmax.X) && (p.Y >= cmin.Y) && (p.Y <= cmax.Y))
{
items.Add(c);
c.GetVisibleChildren(ref p, items);
}
}
}
public override string ToString()
{

View File

@ -101,7 +101,7 @@ namespace CodeWalker.World
Yld = gfc.GetFileUncached<YldFile>(clothFile);
while ((Yld != null) && (!Yld.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(Yld);
}
}
@ -110,22 +110,22 @@ namespace CodeWalker.World
while ((Ydd != null) && (!Ydd.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
Ydd = gfc.GetYdd(pedhash);
}
while ((Ytd != null) && (!Ytd.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
Ytd = gfc.GetYtd(pedhash);
}
while ((Ycd != null) && (!Ycd.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
Ycd = gfc.GetYcd(ycdhash);
}
while ((Yft != null) && (!Yft.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
Yft = gfc.GetYft(pedhash);
}
@ -169,7 +169,7 @@ namespace CodeWalker.World
var ydd = gfc.GetFileUncached<YddFile>(file);
while ((ydd != null) && (!ydd.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(ydd);
}
if (ydd?.Drawables?.Length > 0)
@ -193,7 +193,7 @@ namespace CodeWalker.World
var ytd = gfc.GetFileUncached<YtdFile>(file);
while ((ytd != null) && (!ytd.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(ytd);
}
if (ytd?.TextureDict?.Textures?.data_items.Length > 0)
@ -216,7 +216,7 @@ namespace CodeWalker.World
var yld = gfc.GetFileUncached<YldFile>(file);
while ((yld != null) && (!yld.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
gfc.TryLoadEnqueue(yld);
}
if (yld?.ClothDictionary?.Clothes?.data_items?.Length > 0)

View File

@ -18,8 +18,12 @@ namespace CodeWalker.World
private GameFileCache GameFileCache = null;
public SpaceGrid Grid;
private Dictionary<SpaceBoundsKey, BoundsStoreItem> visibleBoundsDict = new Dictionary<SpaceBoundsKey, BoundsStoreItem>();
public SpaceMapDataStore MapDataStore;
public SpaceBoundsStore BoundsStore;
private Dictionary<MetaHash, MetaHash> interiorLookup = new Dictionary<MetaHash, MetaHash>();
private Dictionary<MetaHash, YmfInterior> interiorManifest = new Dictionary<MetaHash, YmfInterior>();
@ -58,9 +62,14 @@ namespace CodeWalker.World
InitCacheData();
updateStatus("Building world grid...");
updateStatus("Building map data store...");
InitMapGrid();
InitMapDataStore();
updateStatus("Building bounds store...");
InitBoundsStore();
updateStatus("Loading paths...");
@ -313,101 +322,46 @@ namespace CodeWalker.World
}
private void InitMapGrid()
private void InitMapDataStore()
{
Grid = new SpaceGrid();
MapDataStore = new SpaceMapDataStore();
//List<MapDataStoreNode> containers = new List<MapDataStoreNode>();
//List<MapDataStoreNode> critnodes = new List<MapDataStoreNode>();
//List<MapDataStoreNode> hdnodes = new List<MapDataStoreNode>();
//List<MapDataStoreNode> lodnodes = new List<MapDataStoreNode>();
//List<MapDataStoreNode> strmnodes = new List<MapDataStoreNode>();
//List<MapDataStoreNode> intnodes = new List<MapDataStoreNode>();
//List<MapDataStoreNode> occlnodes = new List<MapDataStoreNode>();
//List<MapDataStoreNode> grassnodes = new List<MapDataStoreNode>();
//List<MapDataStoreNode> lodlightsnodes = new List<MapDataStoreNode>();
List<MapDataStoreNode> rootnodes = new List<MapDataStoreNode>();
List<MapDataStoreNode> lodnodes = new List<MapDataStoreNode>();
List<MapDataStoreNode> orphnodes = new List<MapDataStoreNode>();
foreach (var node in nodedict.Values)
{
bool addtogrid = false;
byte t = (byte)(node.ContentFlags & 0xFF);
switch (node.ContentFlags)// t)
if (node.ParentName == 0)
{
case 0: //for mods/unused stuff? could be interesting.
addtogrid = true;
break;
case 16://"container" node?
//containers.Add(node);
break;
case 18:
case 82: //HD nodes
//hdnodes.Add(node);
addtogrid = true;
break;
case 1:
case 65: //Stream nodes
//strmnodes.Add(node);
addtogrid = true;
break;
case 513:
case 577: //critical nodes
//critnodes.Add(node);
addtogrid = true;
break;
case 9:
case 73: //interior nodes
//intnodes.Add(node);
addtogrid = true;
break;
case 2:
case 4:
case 20:
case 66:
case 514: //LOD nodes
//lodnodes.Add(node);
addtogrid = true;
break;
case 128:
case 256: //LOD lights nodes
//lodlightsnodes.Add(node);
addtogrid = true;
break;
case 32: //occlusion nodes
//occlnodes.Add(node);
addtogrid = true;
break;
case 1088: //grass nodes
//grassnodes.Add(node);
addtogrid = true;
break;
default:
addtogrid = true;
break;
}
if (addtogrid)
rootnodes.Add(node);
if (((node.ContentFlags & 2) > 0) || ((node.ContentFlags & 4) > 0) || ((node.ContentFlags & 16) > 0))
{
Grid.AddNode(node);
lodnodes.Add(node);
}
}
foreach (var item in boundsdict.Values)
else
{
Grid.AddBounds(item);
orphnodes.Add(node);
}
}
}
foreach (var intprx in interiorProxies.Values)
MapDataStore.Init(rootnodes);
}
private void InitBoundsStore()
{
Grid.AddInterior(intprx);
}
BoundsStore = new SpaceBoundsStore();
BoundsStore.Init(boundsdict.Values.ToList());
}
private void InitNodeGrid()
{
@ -786,7 +740,7 @@ namespace CodeWalker.World
public void Update(float elapsed)
{
if (!Inited) return;
if (Grid == null) return;
if (BoundsStore == null) return;
if (elapsed > 0.1f) elapsed = 0.1f;
@ -1044,19 +998,12 @@ namespace CodeWalker.World
public void GetVisibleYmaps(Camera cam, int hour, MetaHash weather, Dictionary<MetaHash, YmapFile> ymaps)
{
if (!Inited) return;
if (Grid == null) return;
ymaps.Clear();
//var pos = Grid.GetCellPos(cam.Position);
var cell = Grid.GetCell(cam.Position);
if (cell.NodesList != null)
if (MapDataStore == null) return;
var items = MapDataStore.GetItems(ref cam.Position);
for (int i = 0; i < items.Count; i++)
{
for (int n = 0; n < cell.NodesList.Count; n++)
{
var node = cell.NodesList[n];
var hash = node.Name;
var item = items[i];
var hash = item.Name;
if (!ymaps.ContainsKey(hash))
{
var ymap = (hash > 0) ? GameFileCache.GetYmap(hash) : null;
@ -1073,86 +1020,17 @@ namespace CodeWalker.World
}
//int gridrange = 0;
//var pos = Grid.GetCellPos(cam.Position);
//int minx = Math.Min(Math.Max(pos.X - gridrange, 0), SpaceGrid.LastCell);
//int maxx = Math.Min(Math.Max(pos.X + gridrange, 0), SpaceGrid.LastCell);
//int miny = Math.Min(Math.Max(pos.Y - gridrange, 0), SpaceGrid.LastCell);
//int maxy = Math.Min(Math.Max(pos.Y + gridrange, 0), SpaceGrid.LastCell);
//for (int x = minx; x <= maxx; x++)
//{
// for (int y = miny; y <= maxy; y++)
// {
// var cell = Grid.GetCell(new Vector2I(x, y));
// if (cell.NodesList != null)
// {
// for (int n = 0; n < cell.NodesList.Count; n++)
// {
// var node = cell.NodesList[n];
// var hash = node.Name;
// if (!ymaps.ContainsKey(hash))
// {
// var ymap = (hash > 0) ? GameFileCache.GetYmap(hash) : null;
// if ((ymap != null) && (ymap.Loaded))
// {
// ymaps[hash] = ymap;
// }
// }
// }
// }
// }
//}
}
public void GetVisibleBounds(Camera cam, int gridrange, bool[] layers, List<BoundsStoreItem> boundslist)
{
if (!Inited) return;
if (Grid == null) return;
visibleBoundsDict.Clear();
var pos = Grid.GetCellPos(cam.Position);
int minx = Math.Min(Math.Max(pos.X - gridrange, 0), SpaceGrid.LastCell);
int maxx = Math.Min(Math.Max(pos.X + gridrange, 0), SpaceGrid.LastCell);
int miny = Math.Min(Math.Max(pos.Y - gridrange, 0), SpaceGrid.LastCell);
int maxy = Math.Min(Math.Max(pos.Y + gridrange, 0), SpaceGrid.LastCell);
for (int x = minx; x <= maxx; x++)
{
for (int y = miny; y <= maxy; y++)
{
var cell = Grid.GetCell(new Vector2I(x, y));
if (cell.BoundsList != null)
{
foreach (var item in cell.BoundsList)
{
uint l = item.Layer;
if (l < 3)
{
if (!layers[l]) continue;
}
else
{ }
visibleBoundsDict[new SpaceBoundsKey(item.Name, item.Min)] = item;
}
}
}
}
//var cell = grid.GetCell(cam.Position);
//if (cell.BoundsList != null)
//{
// boundslist.AddRange(cell.BoundsList);
//}
boundslist.AddRange(visibleBoundsDict.Values);
if (BoundsStore == null) return;
float dist = 50.0f * gridrange;
var pos = cam.Position;
var min = pos - dist;
var max = pos + dist;
var items = BoundsStore.GetItems(ref min, ref max);
boundslist.AddRange(items);
}
@ -1174,7 +1052,7 @@ namespace CodeWalker.World
public void GetVisibleYnvs(Camera cam, int gridrange, List<YnvFile> ynvs)
{
if (!Inited) return;
if (Grid == null) return;
if (NavGrid == null) return;
ynvs.Clear();
@ -1213,18 +1091,11 @@ namespace CodeWalker.World
int polytestcount = 0;
int nodetestcount = 0;
bool testcomplete = true;
var cellpos = Grid.GetCellPos(ray.Position);
var cell = Grid.GetCell(cellpos);
var startz = ray.Position.Z;
var maxcells = 5;
var cellcount = 0;
var box = new BoundingBox();
var tsph = new BoundingSphere();
var rayt = new Ray();
var rp = ray.Position;
var rd = ray.Direction;
float dirx = (rd.X < 0) ? -1 : (rd.X > 0) ? 1 : 0;
float diry = (rd.Y < 0) ? -1 : (rd.Y > 0) ? 1 : 0;
var boxhitdist = float.MaxValue;
var itemhitdist = float.MaxValue;
Vector3 p1, p2, p3, p4, a1, a2, a3;
@ -1235,11 +1106,11 @@ namespace CodeWalker.World
BoundMaterial_s hitmat = new BoundMaterial_s();
Vector3 hitnorm = Vector3.Zero;
Vector3 hitpos = Vector3.Zero;
while (cell != null)
{
if (cell.BoundsList != null)
{
foreach (var bound in cell.BoundsList)
if (BoundsStore == null) return res;
var boundslist = BoundsStore.GetItems(ref ray);
foreach (var bound in boundslist)
{
uint l = bound.Layer;
if ((layers != null) && (l < 3))
@ -1447,39 +1318,6 @@ namespace CodeWalker.World
}
}
}
//walk the line to the next cell...
cellcount++;
if (cellcount >= maxcells) break;
if ((dirx == 0) && (diry == 0)) break; //vertical ray
Vector3 cellwp = Grid.GetWorldPos(cellpos);
float compx = (dirx < 0) ? cellwp.X : cellwp.X + SpaceGrid.CellSize;
float compy = (diry < 0) ? cellwp.Y : cellwp.Y + SpaceGrid.CellSize;
float deltx = Math.Abs(compx - rp.X);
float delty = Math.Abs(compy - rp.Y);
float nextd = 0;
if (deltx < delty)
{
cellpos.X += (int)dirx;
nextd = (rd.X != 0) ? (deltx / rd.X) : 0;
}
else
{
cellpos.Y += (int)diry;
nextd = (rd.Y != 0) ? (delty / rd.Y) : 0;
}
cell = Grid.GetCell(cellpos);
if (nextd > itemhitdist)
{ break; } //next cell is further away than current hit.. no need to continue
if (nextd > maxdist)
{ break; } //next cell is out of the testing range.. stop now
}
if (hit)
{
@ -1508,9 +1346,6 @@ namespace CodeWalker.World
bool testcomplete = true;
Vector3 sphmin = sph.Center - sph.Radius;
Vector3 sphmax = sph.Center + sph.Radius;
var cellmin = Grid.GetCellPos(sphmin);
var cellmax = Grid.GetCellPos(sphmax);
var cellcount = 0;
var box = new BoundingBox();
var tsph = new BoundingSphere();
var spht = new BoundingSphere();
@ -1525,15 +1360,13 @@ namespace CodeWalker.World
BoundPolygon hitpoly = null;
Vector3 hitnorm = Vector3.Zero;
Vector3 hitpos = Vector3.Zero;
for (int x = cellmin.X; x <= cellmax.X; x++)
{
for (int y = cellmin.Y; y <= cellmax.Y; y++)
{
var cell = Grid.GetCell(new Vector2I(x, y));
if (cell == null) continue;
if (cell.BoundsList == null) continue;
foreach (var bound in cell.BoundsList)
if (BoundsStore == null) return res;
var boundslist = BoundsStore.GetItems(ref sphmin, ref sphmax);
foreach (var bound in boundslist)
{
box.Minimum = bound.Min;
box.Maximum = bound.Max;
@ -1722,9 +1555,6 @@ namespace CodeWalker.World
}
}
cellcount++;
}
}
//if (hit)
//{
@ -1745,205 +1575,8 @@ namespace CodeWalker.World
}
public class SpaceGrid
{
public const int CellCount = 500; //cells along a side, total cell count is this squared
public const int LastCell = CellCount - 1; //the last cell index in the array
public const float WorldSize = 10000.0f; //max world grid size +/- 10000 units
public const float CellSize = 2.0f * WorldSize / (float)CellCount;//40.0f; //size of a cell
public const float CellSizeInv = 1.0f / CellSize; //inverse of the cell size.
public const float CellSizeHalf = CellSize * 0.5f; //half the cell size
public int TotalBoundsCount = 0; //total number of bounds in this grid
public int TotalBoundsRefCount = 0; //total number of bounds placements in cells
private int MaxBoundsInCell = 0; //biggest number of bounds added to a single cell
private SpaceGridCell DensestBoundsCell = null;
public int TotalNodeCount = 0; //total map nodes in grid
public int TotalNodeRefCount = 0; //total number of map node placements in cells
public int MaxNodesInCell = 0; //biggest number of nodes added to a single cell
private SpaceGridCell DensestNodeCell = null;
public int TotalInteriorCount = 0;
public int TotalInteriorRefCount = 0;
public int MaxInteriorsInCell = 0;
private SpaceGridCell DensestInteriorCell = null;
public SpaceGridCell[,] Cells { get; set; } = new SpaceGridCell[CellCount, CellCount];
public Vector3 GetWorldPos(Vector2I p)
{
Vector3 ind = new Vector3(p.X, p.Y, 0.0f);
return (ind * CellSize) - new Vector3(WorldSize, WorldSize, 0);
}
public Vector2I GetCellPos(Vector3 p)
{
Vector3 ind = (p + WorldSize) * CellSizeInv;
int x = (int)ind.X;
int y = (int)ind.Y;
x = (x < 0) ? 0 : (x > LastCell) ? LastCell : x;
y = (y < 0) ? 0 : (y > LastCell) ? LastCell : y;
return new Vector2I(x, y);
}
public SpaceGridCell GetCell(Vector2I g)
{
if ((g.X < 0) || (g.Y < 0) || (g.X >= CellCount) || (g.Y >= CellCount))
{
return null;
}
var cell = Cells[g.X, g.Y];
if (cell == null)
{
cell = new SpaceGridCell();
Cells[g.X, g.Y] = cell;
}
return cell;
}
public SpaceGridCell GetCell(Vector3 p)
{
return GetCell(GetCellPos(p));
}
public void AddBounds(BoundsStoreItem item)
{
Vector2I min = GetCellPos(item.Min);
Vector2I max = GetCellPos(item.Max);
int cellcount = 0;
for (int x = min.X; x <= max.X; x++)
{
for (int y = min.Y; y <= max.Y; y++)
{
var cell = GetCell(new Vector2I(x, y));
cell.AddBounds(item);
TotalBoundsRefCount++;
if (cell.BoundsList.Count > MaxBoundsInCell)
{
MaxBoundsInCell = cell.BoundsList.Count;
DensestBoundsCell = cell;
}
cellcount++;
}
}
if (cellcount == 0)
{ }
TotalBoundsCount++;
}
public void AddNode(MapDataStoreNode node)
{
bool useouter = true;// false;
//switch (node.Unk01)
//{
// case 2:
// case 4:
// case 20:
// case 66:
// case 514://lods
// useouter = true;
// break;
// case 128:
// case 256://lodlights
// useouter = true;
// break;
// case 18:
// case 82://HD nodes
// useouter = true;
// break;
//}
//Vector2I min = GetCellPos(node.OuterBBMin);
//Vector2I max = GetCellPos(node.OuterBBMax);
Vector2I min = GetCellPos(useouter ? node.streamingExtentsMin : node.entitiesExtentsMin);
Vector2I max = GetCellPos(useouter ? node.streamingExtentsMax : node.entitiesExtentsMax);
for (int x = min.X; x <= max.X; x++)
{
for (int y = min.Y; y <= max.Y; y++)
{
var cell = GetCell(new Vector2I(x, y));
cell.AddNode(node);
TotalNodeRefCount++;
if (cell.NodesList.Count > MaxNodesInCell)
{
MaxNodesInCell = cell.NodesList.Count;
DensestNodeCell = cell;
}
}
}
TotalNodeCount++;
}
public void AddInterior(CInteriorProxy intprx)
{
Vector2I min = GetCellPos(intprx.BBMin);
Vector2I max = GetCellPos(intprx.BBMax);
int cellcount = 0;
for (int x = min.X; x <= max.X; x++)
{
for (int y = min.Y; y <= max.Y; y++)
{
var cell = GetCell(new Vector2I(x, y));
cell.AddInterior(intprx);
TotalInteriorRefCount++;
if (cell.InteriorList.Count > MaxInteriorsInCell)
{
MaxInteriorsInCell = cell.InteriorList.Count;
DensestInteriorCell = cell;
}
cellcount++;
}
}
if (cellcount == 0)
{ }
TotalInteriorCount++;
}
}
public class SpaceGridCell
{
public List<MapDataStoreNode> NodesList;
public List<BoundsStoreItem> BoundsList;
public List<CInteriorProxy> InteriorList;
public void AddNode(MapDataStoreNode node)
{
if (NodesList == null)
{
NodesList = new List<MapDataStoreNode>();
}
NodesList.Add(node);
}
public void AddBounds(BoundsStoreItem item)
{
if (BoundsList == null)
{
BoundsList = new List<BoundsStoreItem>(5);
}
BoundsList.Add(item);
}
public void RemoveBounds(BoundsStoreItem item)
{
if (BoundsList != null)
{
BoundsList.Remove(item);
}
}
public void AddInterior(CInteriorProxy intprx)
{
if (InteriorList == null)
{
InteriorList = new List<CInteriorProxy>(5);
}
InteriorList.Add(intprx);
}
}
public struct SpaceBoundsKey
{
public MetaHash Name { get; set; }
@ -1956,6 +1589,317 @@ namespace CodeWalker.World
}
public class SpaceMapDataStore
{
public SpaceMapDataStoreNode RootNode;
public int SplitThreshold = 10;
public List<MapDataStoreNode> VisibleItems = new List<MapDataStoreNode>();
public void Init(List<MapDataStoreNode> rootnodes)
{
RootNode = new SpaceMapDataStoreNode();
RootNode.Owner = this;
foreach (var item in rootnodes)
{
RootNode.Add(item);
}
RootNode.TrySplit(SplitThreshold);
}
public List<MapDataStoreNode> GetItems(ref Vector3 p)
{
VisibleItems.Clear();
if (RootNode != null)
{
RootNode.GetItems(ref p, VisibleItems);
}
return VisibleItems;
}
}
public class SpaceMapDataStoreNode
{
public SpaceMapDataStore Owner = null;
public SpaceMapDataStoreNode[] Children = null;
public List<MapDataStoreNode> Items = null;
public Vector3 BBMin = new Vector3(float.MaxValue);
public Vector3 BBMax = new Vector3(float.MinValue);
public int Depth = 0;
public void Add(MapDataStoreNode item)
{
if (Items == null)
{
Items = new List<MapDataStoreNode>();
}
BBMin = Vector3.Min(BBMin, item.streamingExtentsMin);
BBMax = Vector3.Max(BBMax, item.streamingExtentsMax);
Items.Add(item);
}
public void TrySplit(int threshold)
{
if ((Items == null) || (Items.Count <= threshold))
{ return; }
Children = new SpaceMapDataStoreNode[4];
var newItems = new List<MapDataStoreNode>();
var ncen = (BBMax + BBMin) * 0.5f;
var next = (BBMax - BBMin) * 0.5f;
var nsiz = Math.Max(next.X, next.Y);
var nsizh = nsiz * 0.5f;
foreach (var item in Items)
{
var imin = item.streamingExtentsMin;
var imax = item.streamingExtentsMax;
var icen = (imax + imin) * 0.5f;
var iext = (imax - imin) * 0.5f;
var isiz = Math.Max(iext.X, iext.Y);
if (isiz >= nsizh)
{
newItems.Add(item);
}
else
{
var cind = ((icen.X > ncen.X) ? 1 : 0) + ((icen.Y > ncen.Y) ? 2 : 0);
var c = Children[cind];
if (c == null)
{
c = new SpaceMapDataStoreNode();
c.Owner = Owner;
c.Depth = Depth + 1;
Children[cind] = c;
}
c.Add(item);
}
}
for (int i = 0; i < 4; i++)
{
var c = Children[i];
if (c != null)
{
c.TrySplit(threshold);
}
}
Items = newItems;
}
public void GetItems(ref Vector3 p, List<MapDataStoreNode> items)
{
if ((p.X >= BBMin.X) && (p.X <= BBMax.X) && (p.Y >= BBMin.Y) && (p.Y <= BBMax.Y))
{
if (Items != null)
{
for (int i = 0; i < Items.Count; i++)
{
var item = Items[i];
var imin = item.streamingExtentsMin;
var imax = item.streamingExtentsMax;
if ((p.X >= imin.X) && (p.X <= imax.X) && (p.Y >= imin.Y) && (p.Y <= imax.Y))
{
items.Add(item);
item.GetVisibleChildren(ref p, items);
}
}
}
if (Children != null)
{
for (int i = 0; i < 4; i++)
{
var c = Children[i];
if (c != null)
{
c.GetItems(ref p, items);
}
}
}
}
}
}
public class SpaceBoundsStore
{
public SpaceBoundsStoreNode RootNode;
public int SplitThreshold = 10;
public List<BoundsStoreItem> VisibleItems = new List<BoundsStoreItem>();
public void Init(List<BoundsStoreItem> items)
{
RootNode = new SpaceBoundsStoreNode();
RootNode.Owner = this;
foreach (var item in items)
{
RootNode.Add(item);
}
RootNode.TrySplit(SplitThreshold);
}
public List<BoundsStoreItem> GetItems(ref Vector3 min, ref Vector3 max)
{
VisibleItems.Clear();
if (RootNode != null)
{
RootNode.GetItems(ref min, ref max, VisibleItems);
}
return VisibleItems;
}
public List<BoundsStoreItem> GetItems(ref Ray ray)
{
VisibleItems.Clear();
if (RootNode != null)
{
RootNode.GetItems(ref ray, VisibleItems);
}
return VisibleItems;
}
}
public class SpaceBoundsStoreNode
{
public SpaceBoundsStore Owner = null;
public SpaceBoundsStoreNode[] Children = null;
public List<BoundsStoreItem> Items = null;
public Vector3 BBMin = new Vector3(float.MaxValue);
public Vector3 BBMax = new Vector3(float.MinValue);
public int Depth = 0;
public void Add(BoundsStoreItem item)
{
if (Items == null)
{
Items = new List<BoundsStoreItem>();
}
BBMin = Vector3.Min(BBMin, item.Min);
BBMax = Vector3.Max(BBMax, item.Max);
Items.Add(item);
}
public void TrySplit(int threshold)
{
if ((Items == null) || (Items.Count <= threshold))
{ return; }
Children = new SpaceBoundsStoreNode[4];
var newItems = new List<BoundsStoreItem>();
var ncen = (BBMax + BBMin) * 0.5f;
var next = (BBMax - BBMin) * 0.5f;
var nsiz = Math.Max(next.X, next.Y);
var nsizh = nsiz * 0.5f;
foreach (var item in Items)
{
var imin = item.Min;
var imax = item.Max;
var icen = (imax + imin) * 0.5f;
var iext = (imax - imin) * 0.5f;
var isiz = Math.Max(iext.X, iext.Y);
if (isiz >= nsizh)
{
newItems.Add(item);
}
else
{
var cind = ((icen.X > ncen.X) ? 1 : 0) + ((icen.Y > ncen.Y) ? 2 : 0);
var c = Children[cind];
if (c == null)
{
c = new SpaceBoundsStoreNode();
c.Owner = Owner;
c.Depth = Depth + 1;
Children[cind] = c;
}
c.Add(item);
}
}
for (int i = 0; i < 4; i++)
{
var c = Children[i];
if (c != null)
{
c.TrySplit(threshold);
}
}
Items = newItems;
}
public void GetItems(ref Vector3 min, ref Vector3 max, List<BoundsStoreItem> items)
{
if ((max.X >= BBMin.X) && (min.X <= BBMax.X) && (max.Y >= BBMin.Y) && (min.Y <= BBMax.Y))
{
if (Items != null)
{
for (int i = 0; i < Items.Count; i++)
{
var item = Items[i];
if ((max.X >= item.Min.X) && (min.X <= item.Max.X) && (max.Y >= item.Min.Y) && (min.Y <= item.Max.Y))
{
items.Add(item);
}
}
}
if (Children != null)
{
for (int i = 0; i < 4; i++)
{
var c = Children[i];
if (c != null)
{
c.GetItems(ref min, ref max, items);
}
}
}
}
}
public void GetItems(ref Ray ray, List<BoundsStoreItem> items)
{
var box = new BoundingBox(BBMin, BBMax);
if (ray.Intersects(ref box))
{
if (Items != null)
{
for (int i = 0; i < Items.Count; i++)
{
var item = Items[i];
box = new BoundingBox(item.Min, item.Max);
if (ray.Intersects(box))
{
items.Add(item);
}
}
}
if (Children != null)
{
for (int i = 0; i < 4; i++)
{
var c = Children[i];
if (c != null)
{
c.GetItems(ref ray, items);
}
}
}
}
}
}
public class SpaceNodeGrid
{

View File

@ -51,7 +51,7 @@ namespace CodeWalker.World
Yft = gfc.GetYft(ModelHash);
while ((Yft != null) && (!Yft.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
Yft = gfc.GetYft(ModelHash);
}
@ -65,7 +65,7 @@ namespace CodeWalker.World
ConvRoofDict = gfc.GetYcd(ycdhash);
while ((ConvRoofDict != null) && (!ConvRoofDict.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
ConvRoofDict = gfc.GetYcd(ycdhash);
}
ClipMapEntry cme = null;

View File

@ -46,7 +46,7 @@ namespace CodeWalker.World
while ((Ydr != null) && (!Ydr.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
Ydr = gfc.GetYdr(useHash);
}

View File

@ -800,7 +800,7 @@ namespace CodeWalker.Forms
var ycd = gameFileCache.GetYcd(ycdhash);
while ((ycd != null) && (!ycd.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
ycd = gameFileCache.GetYcd(ycdhash);
}

View File

@ -826,7 +826,7 @@ namespace CodeWalker.Peds
var ycd = GameFileCache.GetYcd(ycdhash);
while ((ycd != null) && (!ycd.Loaded))
{
Thread.Sleep(20);//kinda hacky
Thread.Sleep(1);//kinda hacky
ycd = GameFileCache.GetYcd(ycdhash);
}

View File

@ -82,8 +82,6 @@ namespace CodeWalker.Project.Panels
float density = 0.5f; //distance between vertices for the initial grid
//float clipdz = 0.5f; //any polygons with greater steepness should be removed
Vector2I imin = space.Grid.GetCellPos(new Vector3(min, 0));
Vector2I imax = space.Grid.GetCellPos(new Vector3(max, 0));
//Vector2 vertexCounts = (max - min) / density;
//int vertexCountX = (int)vertexCounts.X;
@ -113,30 +111,22 @@ namespace CodeWalker.Project.Panels
var polys = new List<GenPoly>();
for (int x = imin.X; x <= imax.X; x++) //generate verts for each world cell
{
for (int y = imin.Y; y <= imax.Y; y++)
{
Vector2I gi = new Vector2I(x, y);
var cell = space.Grid.GetCell(gi);
var cellstr = gi.ToString();
var cellmin = space.Grid.GetWorldPos(gi);
var cellmax = cellmin + SpaceGrid.CellSize;
var vertexCountXY = (cellmax - cellmin) / density;
var vertexCountXY = (max - min) / density;
int vertexCountX = (int)vertexCountXY.X+1;
int vertexCountY = (int)vertexCountXY.Y+1;
//int vertexCountTot = vertexCountX * vertexCountY;
vgrid.BeginGrid(vertexCountX, vertexCountY);
cellmin.Z = 0.0f;//min probably not needed here
cellmax.Z = 0.0f;
Ray ray = new Ray(Vector3.Zero, new Vector3(0, 0, -1));//for casting with
UpdateStatus("Loading cell " + cellstr + " ...");
UpdateStatus("Loading YBNs...");
//pre-warm the bounds cache for this cell, and find the min/max Z
if (cell.BoundsList != null)
{
foreach (var boundsitem in cell.BoundsList)
var bmin = new Vector3(min, 0);
var bmax = new Vector3(max, 0);
var boundslist = space.BoundsStore.GetItems(ref bmin, ref bmax);
//pre-warm the bounds cache for this area, and find the min/max Z
foreach (var boundsitem in boundslist)
{
YbnFile ybn = gameFileCache.GetYbn(boundsitem.Name);
if (ybn == null)
@ -160,9 +150,8 @@ namespace CodeWalker.Project.Panels
}
if (ybn.Loaded && (ybn.Bounds != null))
{
cellmin.Z = Math.Min(cellmin.Z, ybn.Bounds.BoundingBoxMin.Z);
cellmax.Z = Math.Max(cellmax.Z, ybn.Bounds.BoundingBoxMax.Z);
}
bmin.Z = Math.Min(bmin.Z, ybn.Bounds.BoundingBoxMin.Z);
bmax.Z = Math.Max(bmax.Z, ybn.Bounds.BoundingBoxMax.Z);
}
}
@ -171,7 +160,7 @@ namespace CodeWalker.Project.Panels
//ray-cast each XY vertex position, and find the height and surface from ybn's
//continue casting down to find more surfaces...
UpdateStatus("Processing cell " + cellstr + " ...");
UpdateStatus("Processing...");
for (int vx = 0; vx < vertexCountX; vx++)
{
@ -179,8 +168,8 @@ namespace CodeWalker.Project.Panels
{
vgrid.BeginCell(vx, vy);
var vcoffset = new Vector3(vx, vy, 0) * density;
ray.Position = cellmin + vcoffset;
ray.Position.Z = cellmax.Z + 1.0f;//start the ray at the top of the cell
ray.Position = bmin + vcoffset;
ray.Position.Z = bmax.Z + 1.0f;//start the ray at the top of the cell
var intres = space.RayIntersect(ray, float.MaxValue, layers);
hitTestCount++;
while (intres.Hit)// && (intres.HitDist > 0))
@ -229,8 +218,20 @@ namespace CodeWalker.Project.Panels
newCount += genPolys.Count;
}
}
//try merge generated polys into bigger ones, while keeping convex!

View File

@ -381,7 +381,7 @@ namespace CodeWalker.World
var ycd = GameFileCache.GetYcd(ycdhash);
while ((ycd != null) && (!ycd.Loaded))
{
Thread.Sleep(20);//bite me
Thread.Sleep(1);//bite me
ycd = GameFileCache.GetYcd(ycdhash);
}
if (ycd != null)

View File

@ -2213,7 +2213,7 @@ namespace CodeWalker
public SpaceRayIntersectResult GetSpaceMouseRay()
{
SpaceRayIntersectResult ret = new SpaceRayIntersectResult();
if (space.Inited && space.Grid != null)
if (space.Inited && space.BoundsStore != null)
{
Ray mray = new Ray();
mray.Position = camera.MouseRay.Position + camera.Position;