Semi-fast coarse PVS
This commit is contained in:
		
							parent
							
								
									6a7ee922b5
								
							
						
					
					
						commit
						b0e4c8dff3
					
				| 
						 | 
				
			
			@ -5,10 +5,18 @@ namespace KeepersCompound.Lightmapper;
 | 
			
		|||
 | 
			
		||||
public class PotentiallyVisibleSet
 | 
			
		||||
{
 | 
			
		||||
    private struct Edge
 | 
			
		||||
    private struct Node(List<int> edgeIndices)
 | 
			
		||||
    {
 | 
			
		||||
        public int Destination;
 | 
			
		||||
        public Poly Poly;
 | 
			
		||||
        public bool VisibilityComputed = false;
 | 
			
		||||
        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()
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -44,9 +52,8 @@ public class PotentiallyVisibleSet
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private readonly List<int>[] _portalGraph;
 | 
			
		||||
    private readonly Node[] _graph;
 | 
			
		||||
    private readonly List<Edge> _edges;
 | 
			
		||||
    private readonly Dictionary<int, HashSet<int>> _visibilitySet;
 | 
			
		||||
 | 
			
		||||
    private const float Epsilon = 0.1f;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -68,19 +75,19 @@ public class PotentiallyVisibleSet
 | 
			
		|||
 | 
			
		||||
    public PotentiallyVisibleSet(WorldRep.Cell[] cells)
 | 
			
		||||
    {
 | 
			
		||||
        _graph = new Node[cells.Length];
 | 
			
		||||
        _edges = [];
 | 
			
		||||
        _visibilitySet = new Dictionary<int, HashSet<int>>();
 | 
			
		||||
        
 | 
			
		||||
        _portalGraph = new List<int>[cells.Length];
 | 
			
		||||
        for (var i = 0; i < cells.Length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            _portalGraph[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
 | 
			
		||||
            // We can see into it though, so we still want the edges coming in
 | 
			
		||||
            if ((cell.Flags & 8) != 0)
 | 
			
		||||
            {
 | 
			
		||||
                _graph[i] = new Node(edgeIndices);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +103,6 @@ public class PotentiallyVisibleSet
 | 
			
		|||
                    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
 | 
			
		||||
                // the duplicated poly
 | 
			
		||||
                var vs = new List<Vector3>(poly.VertexCount);
 | 
			
		||||
| 
						 | 
				
			
			@ -106,33 +111,96 @@ public class PotentiallyVisibleSet
 | 
			
		|||
                    vs.Add(cell.Vertices[cell.Indices[indicesOffset + vIdx]]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var edge = new Edge
 | 
			
		||||
                {
 | 
			
		||||
                    Destination = other,
 | 
			
		||||
                    Poly = new Poly(vs, cell.Planes[poly.PlaneId]),
 | 
			
		||||
                };
 | 
			
		||||
                var edge = new Edge(poly.Destination, new Poly(vs, cell.Planes[poly.PlaneId]));
 | 
			
		||||
                edgeIndices.Add(_edges.Count);
 | 
			
		||||
                _edges.Add(edge);
 | 
			
		||||
                _portalGraph[i].Add(_edges.Count - 1);
 | 
			
		||||
                indicesOffset += poly.VertexCount;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _graph[i] = new Node(edgeIndices);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Parallel.For(0, _edges.Count, ComputeEdgeMightSee);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
        _visibilitySet.Add(cellIdx, visibleCells);
 | 
			
		||||
        node.VisibilityComputed = true;
 | 
			
		||||
        node.VisibleNodes = visibleCells;
 | 
			
		||||
        _graph[cellIdx] = node;
 | 
			
		||||
        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)
 | 
			
		||||
    {
 | 
			
		||||
        if (cellIdx >= _portalGraph.Length)
 | 
			
		||||
        if (cellIdx >= _graph.Length)
 | 
			
		||||
        {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -141,86 +209,102 @@ public class PotentiallyVisibleSet
 | 
			
		|||
        var visible = new HashSet<int>();
 | 
			
		||||
        visible.Add(cellIdx);
 | 
			
		||||
 | 
			
		||||
        // Additionally a cell can always see it's direct neighbours (obviously)
 | 
			
		||||
        foreach (var edgeIndex in _portalGraph[cellIdx])
 | 
			
		||||
        foreach (var edgeIdx in _graph[cellIdx].EdgeIndices)
 | 
			
		||||
        {
 | 
			
		||||
            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 edge = _edges[edgeIdx];
 | 
			
		||||
            foreach (var mightSee in edge.MightSee)
 | 
			
		||||
            {
 | 
			
		||||
                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);
 | 
			
		||||
                visible.Add(mightSee);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        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(
 | 
			
		||||
        HashSet<int> visible,
 | 
			
		||||
        Poly sourcePoly,
 | 
			
		||||
        Poly previousPoly,
 | 
			
		||||
        int previousCellIdx,
 | 
			
		||||
        int currentCellIdx,
 | 
			
		||||
        int depth)
 | 
			
		||||
    {
 | 
			
		||||
        // TODO: Might need to lose this
 | 
			
		||||
        if (depth > 1024)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        visible.Add(currentCellIdx);
 | 
			
		||||
        
 | 
			
		||||
        // Only one edge out of the cell means we'd be going back on ourselves
 | 
			
		||||
        if (_portalGraph[currentCellIdx].Count <= 1)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // TODO: If all neighbours are already in `visible` skip exploring?
 | 
			
		||||
        
 | 
			
		||||
        var separators = new List<Plane>();
 | 
			
		||||
        GetSeparatingPlanes(separators, sourcePoly, previousPoly, false);
 | 
			
		||||
        GetSeparatingPlanes(separators, previousPoly, sourcePoly, true);
 | 
			
		||||
 | 
			
		||||
        // The case for this occuring is... interesting ( idk )
 | 
			
		||||
        if (separators.Count == 0)
 | 
			
		||||
        {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Clip all new polys and recurse
 | 
			
		||||
        foreach (var edgeIndex in _portalGraph[currentCellIdx])
 | 
			
		||||
        {
 | 
			
		||||
            var edge = _edges[edgeIndex];
 | 
			
		||||
            if (edge.Destination == previousCellIdx || previousPoly.IsCoplanar(edge.Poly) || sourcePoly.IsCoplanar(edge.Poly))
 | 
			
		||||
            {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var poly = new Poly(edge.Poly);
 | 
			
		||||
            foreach (var separator in separators)
 | 
			
		||||
            {
 | 
			
		||||
                ClipPolygonByPlane(ref poly, separator);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            if (poly.Vertices.Count == 0)
 | 
			
		||||
            {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            ExplorePortalRecursive(visible, sourcePoly, poly, currentCellIdx, edge.Destination, depth + 1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // private void ExplorePortalRecursive(
 | 
			
		||||
    //     HashSet<int> visible,
 | 
			
		||||
    //     Poly sourcePoly,
 | 
			
		||||
    //     Poly previousPoly,
 | 
			
		||||
    //     int previousCellIdx,
 | 
			
		||||
    //     int currentCellIdx,
 | 
			
		||||
    //     int depth)
 | 
			
		||||
    // {
 | 
			
		||||
    //     // TODO: Might need to lose this
 | 
			
		||||
    //     if (depth > 1024)
 | 
			
		||||
    //     {
 | 
			
		||||
    //         return;
 | 
			
		||||
    //     }
 | 
			
		||||
    //     
 | 
			
		||||
    //     visible.Add(currentCellIdx);
 | 
			
		||||
    //     
 | 
			
		||||
    //     // Only one edge out of the cell means we'd be going back on ourselves
 | 
			
		||||
    //     if (_portalGraph[currentCellIdx].Count <= 1)
 | 
			
		||||
    //     {
 | 
			
		||||
    //         return;
 | 
			
		||||
    //     }
 | 
			
		||||
    //     
 | 
			
		||||
    //     // TODO: If all neighbours are already in `visible` skip exploring?
 | 
			
		||||
    //     
 | 
			
		||||
    //     var separators = new List<Plane>();
 | 
			
		||||
    //     GetSeparatingPlanes(separators, sourcePoly, previousPoly, false);
 | 
			
		||||
    //     GetSeparatingPlanes(separators, previousPoly, sourcePoly, true);
 | 
			
		||||
    //
 | 
			
		||||
    //     // The case for this occuring is... interesting ( idk )
 | 
			
		||||
    //     if (separators.Count == 0)
 | 
			
		||||
    //     {
 | 
			
		||||
    //         return;
 | 
			
		||||
    //     }
 | 
			
		||||
    //     
 | 
			
		||||
    //     // Clip all new polys and recurse
 | 
			
		||||
    //     foreach (var edgeIndex in _portalGraph[currentCellIdx])
 | 
			
		||||
    //     {
 | 
			
		||||
    //         var edge = _edges[edgeIndex];
 | 
			
		||||
    //         if (edge.Destination == previousCellIdx || previousPoly.IsCoplanar(edge.Poly) || sourcePoly.IsCoplanar(edge.Poly))
 | 
			
		||||
    //         {
 | 
			
		||||
    //             continue;
 | 
			
		||||
    //         }
 | 
			
		||||
    //
 | 
			
		||||
    //         var poly = new Poly(edge.Poly);
 | 
			
		||||
    //         foreach (var separator in separators)
 | 
			
		||||
    //         {
 | 
			
		||||
    //             ClipPolygonByPlane(ref poly, separator);
 | 
			
		||||
    //         }
 | 
			
		||||
    //         
 | 
			
		||||
    //         if (poly.Vertices.Count == 0)
 | 
			
		||||
    //         {
 | 
			
		||||
    //             continue;
 | 
			
		||||
    //         }
 | 
			
		||||
    //         
 | 
			
		||||
    //         ExplorePortalRecursive(visible, sourcePoly, poly, currentCellIdx, edge.Destination, depth + 1);
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
 | 
			
		||||
    // 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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue