Semi-fast coarse PVS
This commit is contained in:
parent
6a7ee922b5
commit
b0e4c8dff3
|
@ -5,10 +5,18 @@ namespace KeepersCompound.Lightmapper;
|
||||||
|
|
||||||
public class PotentiallyVisibleSet
|
public class PotentiallyVisibleSet
|
||||||
{
|
{
|
||||||
private struct Edge
|
private struct Node(List<int> edgeIndices)
|
||||||
{
|
{
|
||||||
public int Destination;
|
public bool VisibilityComputed = false;
|
||||||
public Poly Poly;
|
public HashSet<int> VisibleNodes = [];
|
||||||
|
public readonly List<int> EdgeIndices = edgeIndices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct Edge(int destination, Poly poly)
|
||||||
|
{
|
||||||
|
public readonly HashSet<int> MightSee = [];
|
||||||
|
public readonly int Destination = destination;
|
||||||
|
public readonly Poly Poly = poly;
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
@ -44,9 +52,8 @@ public class PotentiallyVisibleSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly List<int>[] _portalGraph;
|
private readonly Node[] _graph;
|
||||||
private readonly List<Edge> _edges;
|
private readonly List<Edge> _edges;
|
||||||
private readonly Dictionary<int, HashSet<int>> _visibilitySet;
|
|
||||||
|
|
||||||
private const float Epsilon = 0.1f;
|
private const float Epsilon = 0.1f;
|
||||||
|
|
||||||
|
@ -68,19 +75,19 @@ public class PotentiallyVisibleSet
|
||||||
|
|
||||||
public PotentiallyVisibleSet(WorldRep.Cell[] cells)
|
public PotentiallyVisibleSet(WorldRep.Cell[] cells)
|
||||||
{
|
{
|
||||||
|
_graph = new Node[cells.Length];
|
||||||
_edges = [];
|
_edges = [];
|
||||||
_visibilitySet = new Dictionary<int, HashSet<int>>();
|
|
||||||
|
|
||||||
_portalGraph = new List<int>[cells.Length];
|
|
||||||
for (var i = 0; i < cells.Length; i++)
|
for (var i = 0; i < cells.Length; i++)
|
||||||
{
|
{
|
||||||
_portalGraph[i] = [];
|
|
||||||
var cell = cells[i];
|
var cell = cells[i];
|
||||||
|
var edgeIndices = new List<int>(cell.PortalPolyCount);
|
||||||
|
|
||||||
// If a cell is "blocks vision" flagged, we can never see out of it
|
// If a cell is "blocks vision" flagged, we can never see out of it
|
||||||
// We can see into it though, so we still want the edges coming in
|
// We can see into it though, so we still want the edges coming in
|
||||||
if ((cell.Flags & 8) != 0)
|
if ((cell.Flags & 8) != 0)
|
||||||
{
|
{
|
||||||
|
_graph[i] = new Node(edgeIndices);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +103,6 @@ public class PotentiallyVisibleSet
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var other = poly.Destination;
|
|
||||||
|
|
||||||
// Checking if there's already an edge is super slow. It's much faster to just add a new edge, even with
|
// Checking if there's already an edge is super slow. It's much faster to just add a new edge, even with
|
||||||
// the duplicated poly
|
// the duplicated poly
|
||||||
var vs = new List<Vector3>(poly.VertexCount);
|
var vs = new List<Vector3>(poly.VertexCount);
|
||||||
|
@ -106,33 +111,96 @@ public class PotentiallyVisibleSet
|
||||||
vs.Add(cell.Vertices[cell.Indices[indicesOffset + vIdx]]);
|
vs.Add(cell.Vertices[cell.Indices[indicesOffset + vIdx]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var edge = new Edge
|
var edge = new Edge(poly.Destination, new Poly(vs, cell.Planes[poly.PlaneId]));
|
||||||
{
|
edgeIndices.Add(_edges.Count);
|
||||||
Destination = other,
|
|
||||||
Poly = new Poly(vs, cell.Planes[poly.PlaneId]),
|
|
||||||
};
|
|
||||||
_edges.Add(edge);
|
_edges.Add(edge);
|
||||||
_portalGraph[i].Add(_edges.Count - 1);
|
|
||||||
indicesOffset += poly.VertexCount;
|
indicesOffset += poly.VertexCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_graph[i] = new Node(edgeIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Parallel.For(0, _edges.Count, ComputeEdgeMightSee);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] GetVisible(int cellIdx)
|
public int[] GetVisible(int cellIdx)
|
||||||
{
|
{
|
||||||
if (_visibilitySet.TryGetValue(cellIdx, out var value))
|
// TODO: Handle out of range indices
|
||||||
|
var node = _graph[cellIdx];
|
||||||
|
if (node.VisibilityComputed)
|
||||||
{
|
{
|
||||||
return [..value];
|
return [..node.VisibleNodes];
|
||||||
}
|
}
|
||||||
|
|
||||||
var visibleCells = ComputeVisibility(cellIdx);
|
var visibleCells = ComputeVisibility(cellIdx);
|
||||||
_visibilitySet.Add(cellIdx, visibleCells);
|
node.VisibilityComputed = true;
|
||||||
|
node.VisibleNodes = visibleCells;
|
||||||
|
_graph[cellIdx] = node;
|
||||||
return [..visibleCells];
|
return [..visibleCells];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ComputeEdgeMightSee(int edgeIdx)
|
||||||
|
{
|
||||||
|
var source = _edges[edgeIdx];
|
||||||
|
var sourcePlane = source.Poly.Plane;
|
||||||
|
Flood(source.Destination);
|
||||||
|
return;
|
||||||
|
|
||||||
|
void Flood(int cellIdx)
|
||||||
|
{
|
||||||
|
if (!source.MightSee.Add(cellIdx))
|
||||||
|
{
|
||||||
|
return; // target is already explored
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target must be partly behind source, source must be partly in front of target, and source and target cannot face each other
|
||||||
|
foreach (var targetEdgeIdx in _graph[cellIdx].EdgeIndices)
|
||||||
|
{
|
||||||
|
var target = _edges[targetEdgeIdx];
|
||||||
|
var targetPlane = target.Poly.Plane;
|
||||||
|
|
||||||
|
var validTarget = false;
|
||||||
|
foreach (var v in target.Poly.Vertices)
|
||||||
|
{
|
||||||
|
if (MathUtils.DistanceFromPlane(sourcePlane, v) < -MathUtils.Epsilon)
|
||||||
|
{
|
||||||
|
validTarget = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validTarget)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
validTarget = false;
|
||||||
|
foreach (var v in source.Poly.Vertices)
|
||||||
|
{
|
||||||
|
if (MathUtils.DistanceFromPlane(targetPlane, v) > MathUtils.Epsilon)
|
||||||
|
{
|
||||||
|
validTarget = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validTarget)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Vector3.Dot(sourcePlane.Normal, targetPlane.Normal) > MathUtils.Epsilon - 1)
|
||||||
|
{
|
||||||
|
Flood(target.Destination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private HashSet<int> ComputeVisibility(int cellIdx)
|
private HashSet<int> ComputeVisibility(int cellIdx)
|
||||||
{
|
{
|
||||||
if (cellIdx >= _portalGraph.Length)
|
if (cellIdx >= _graph.Length)
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -141,86 +209,102 @@ public class PotentiallyVisibleSet
|
||||||
var visible = new HashSet<int>();
|
var visible = new HashSet<int>();
|
||||||
visible.Add(cellIdx);
|
visible.Add(cellIdx);
|
||||||
|
|
||||||
// Additionally a cell can always see it's direct neighbours (obviously)
|
foreach (var edgeIdx in _graph[cellIdx].EdgeIndices)
|
||||||
foreach (var edgeIndex in _portalGraph[cellIdx])
|
|
||||||
{
|
{
|
||||||
var edge = _edges[edgeIndex];
|
var edge = _edges[edgeIdx];
|
||||||
var neighbourIdx = edge.Destination;
|
foreach (var mightSee in edge.MightSee)
|
||||||
visible.Add(neighbourIdx);
|
|
||||||
|
|
||||||
// Neighbours of our direct neighbour are always visible, unless they're coplanar
|
|
||||||
foreach (var innerEdgeIndex in _portalGraph[neighbourIdx])
|
|
||||||
{
|
{
|
||||||
var innerEdge = _edges[innerEdgeIndex];
|
visible.Add(mightSee);
|
||||||
if (innerEdge.Destination == cellIdx || edge.Poly.IsCoplanar(innerEdge.Poly))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExplorePortalRecursive(visible, edge.Poly, new Poly(innerEdge.Poly), neighbourIdx, innerEdge.Destination, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return visible;
|
return visible;
|
||||||
|
|
||||||
|
// if (cellIdx >= _portalGraph.Length)
|
||||||
|
// {
|
||||||
|
// return [];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Additionally a cell can always see it's direct neighbours (obviously)
|
||||||
|
// foreach (var edgeIndex in _portalGraph[cellIdx])
|
||||||
|
// {
|
||||||
|
// var edge = _edges[edgeIndex];
|
||||||
|
// var neighbourIdx = edge.Destination;
|
||||||
|
// visible.Add(neighbourIdx);
|
||||||
|
//
|
||||||
|
// // Neighbours of our direct neighbour are always visible, unless they're coplanar
|
||||||
|
// foreach (var innerEdgeIndex in _portalGraph[neighbourIdx])
|
||||||
|
// {
|
||||||
|
// var innerEdge = _edges[innerEdgeIndex];
|
||||||
|
// if (innerEdge.Destination == cellIdx || edge.Poly.IsCoplanar(innerEdge.Poly))
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// ExplorePortalRecursive(visible, edge.Poly, new Poly(innerEdge.Poly), neighbourIdx, innerEdge.Destination, 0);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExplorePortalRecursive(
|
// private void ExplorePortalRecursive(
|
||||||
HashSet<int> visible,
|
// HashSet<int> visible,
|
||||||
Poly sourcePoly,
|
// Poly sourcePoly,
|
||||||
Poly previousPoly,
|
// Poly previousPoly,
|
||||||
int previousCellIdx,
|
// int previousCellIdx,
|
||||||
int currentCellIdx,
|
// int currentCellIdx,
|
||||||
int depth)
|
// int depth)
|
||||||
{
|
// {
|
||||||
// TODO: Might need to lose this
|
// // TODO: Might need to lose this
|
||||||
if (depth > 1024)
|
// if (depth > 1024)
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
visible.Add(currentCellIdx);
|
// visible.Add(currentCellIdx);
|
||||||
|
//
|
||||||
// Only one edge out of the cell means we'd be going back on ourselves
|
// // Only one edge out of the cell means we'd be going back on ourselves
|
||||||
if (_portalGraph[currentCellIdx].Count <= 1)
|
// if (_portalGraph[currentCellIdx].Count <= 1)
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// TODO: If all neighbours are already in `visible` skip exploring?
|
// // TODO: If all neighbours are already in `visible` skip exploring?
|
||||||
|
//
|
||||||
var separators = new List<Plane>();
|
// var separators = new List<Plane>();
|
||||||
GetSeparatingPlanes(separators, sourcePoly, previousPoly, false);
|
// GetSeparatingPlanes(separators, sourcePoly, previousPoly, false);
|
||||||
GetSeparatingPlanes(separators, previousPoly, sourcePoly, true);
|
// GetSeparatingPlanes(separators, previousPoly, sourcePoly, true);
|
||||||
|
//
|
||||||
// The case for this occuring is... interesting ( idk )
|
// // The case for this occuring is... interesting ( idk )
|
||||||
if (separators.Count == 0)
|
// if (separators.Count == 0)
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Clip all new polys and recurse
|
// // Clip all new polys and recurse
|
||||||
foreach (var edgeIndex in _portalGraph[currentCellIdx])
|
// foreach (var edgeIndex in _portalGraph[currentCellIdx])
|
||||||
{
|
// {
|
||||||
var edge = _edges[edgeIndex];
|
// var edge = _edges[edgeIndex];
|
||||||
if (edge.Destination == previousCellIdx || previousPoly.IsCoplanar(edge.Poly) || sourcePoly.IsCoplanar(edge.Poly))
|
// if (edge.Destination == previousCellIdx || previousPoly.IsCoplanar(edge.Poly) || sourcePoly.IsCoplanar(edge.Poly))
|
||||||
{
|
// {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
var poly = new Poly(edge.Poly);
|
// var poly = new Poly(edge.Poly);
|
||||||
foreach (var separator in separators)
|
// foreach (var separator in separators)
|
||||||
{
|
// {
|
||||||
ClipPolygonByPlane(ref poly, separator);
|
// ClipPolygonByPlane(ref poly, separator);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (poly.Vertices.Count == 0)
|
// if (poly.Vertices.Count == 0)
|
||||||
{
|
// {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
ExplorePortalRecursive(visible, sourcePoly, poly, currentCellIdx, edge.Destination, depth + 1);
|
// ExplorePortalRecursive(visible, sourcePoly, poly, currentCellIdx, edge.Destination, depth + 1);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// TODO: We're getting multiple separating planes that are the same, let's not somehow?
|
// TODO: We're getting multiple separating planes that are the same, let's not somehow?
|
||||||
private static void GetSeparatingPlanes(List<Plane> separators, Poly p0, Poly p1, bool flip)
|
private static void GetSeparatingPlanes(List<Plane> separators, Poly p0, Poly p1, bool flip)
|
||||||
|
|
Loading…
Reference in New Issue