PVS now works it's just slow as fuck
This commit is contained in:
parent
317b17ab02
commit
9c371b0d6f
|
@ -371,71 +371,63 @@ public class LightMapper
|
||||||
if (!_mission.TryGetChunk<WorldRep>("WREXT", out var worldRep))
|
if (!_mission.TryGetChunk<WorldRep>("WREXT", out var worldRep))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// var lightVisibleCells = Timing.TimeStage("Light PVS", () =>
|
||||||
var lightVisibleCells = Timing.TimeStage("Light PVS", () =>
|
|
||||||
{
|
|
||||||
var cellCount = worldRep.Cells.Length;
|
|
||||||
var aabbs = new MathUtils.Aabb[worldRep.Cells.Length];
|
|
||||||
Parallel.For(0, cellCount, i => aabbs[i] = new MathUtils.Aabb(worldRep.Cells[i].Vertices));
|
|
||||||
|
|
||||||
var lightCellMap = new int[_lights.Count];
|
|
||||||
Parallel.For(0, _lights.Count, i =>
|
|
||||||
{
|
|
||||||
lightCellMap[i] = -1;
|
|
||||||
var light = _lights[i];
|
|
||||||
for (var j = 0; j < cellCount; j++)
|
|
||||||
{
|
|
||||||
if (!MathUtils.Intersects(aabbs[j], light.Position))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Half-space contained
|
|
||||||
var cell = worldRep.Cells[j];
|
|
||||||
var contained = true;
|
|
||||||
for (var k = 0; k < cell.PlaneCount; k++)
|
|
||||||
{
|
|
||||||
var plane = cell.Planes[k];
|
|
||||||
if (MathUtils.DistanceFromPlane(plane, light.Position) < -MathUtils.Epsilon)
|
|
||||||
{
|
|
||||||
contained = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contained)
|
|
||||||
{
|
|
||||||
lightCellMap[i] = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var lightVisibleCells = new List<int[]>(_lights.Count);
|
|
||||||
var pvs = new PotentiallyVisibleSet(worldRep.Cells);
|
|
||||||
for (var i = 0; i < _lights.Count; i++)
|
|
||||||
{
|
|
||||||
var cellIdx = lightCellMap[i];
|
|
||||||
if (cellIdx == -1)
|
|
||||||
{
|
|
||||||
lightVisibleCells.Add([]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
var visibleSet = pvs.GetVisible(lightCellMap[i]);
|
|
||||||
lightVisibleCells.Add(visibleSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lightVisibleCells;
|
|
||||||
|
|
||||||
// TODO: This isn't actually thread safe :)
|
|
||||||
// Parallel.For(0, worldRep.Cells.Length, i => pvs.GetVisible(i));
|
|
||||||
// for (var i = 0; i < worldRep.Cells.Length; i++)
|
|
||||||
// {
|
// {
|
||||||
// pvs.GetVisible(i);
|
// var cellCount = worldRep.Cells.Length;
|
||||||
// // var visible = pvs.GetVisible(i);
|
// var aabbs = new MathUtils.Aabb[worldRep.Cells.Length];
|
||||||
// // Console.WriteLine($"Cell {i}: Count({visible.Length}), Visible[{string.Join(" ", visible)}]");
|
// Parallel.For(0, cellCount, i => aabbs[i] = new MathUtils.Aabb(worldRep.Cells[i].Vertices));
|
||||||
|
//
|
||||||
|
// var lightCellMap = new int[_lights.Count];
|
||||||
|
// Parallel.For(0, _lights.Count, i =>
|
||||||
|
// {
|
||||||
|
// lightCellMap[i] = -1;
|
||||||
|
// var light = _lights[i];
|
||||||
|
// for (var j = 0; j < cellCount; j++)
|
||||||
|
// {
|
||||||
|
// if (!MathUtils.Intersects(aabbs[j], light.Position))
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
// }
|
// }
|
||||||
});
|
//
|
||||||
|
// // Half-space contained
|
||||||
|
// var cell = worldRep.Cells[j];
|
||||||
|
// var contained = true;
|
||||||
|
// for (var k = 0; k < cell.PlaneCount; k++)
|
||||||
|
// {
|
||||||
|
// var plane = cell.Planes[k];
|
||||||
|
// if (MathUtils.DistanceFromPlane(plane, light.Position) < -MathUtils.Epsilon)
|
||||||
|
// {
|
||||||
|
// contained = false;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (contained)
|
||||||
|
// {
|
||||||
|
// lightCellMap[i] = j;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// var lightVisibleCells = new List<int[]>(_lights.Count);
|
||||||
|
// var pvs = new PotentiallyVisibleSet(worldRep.Cells);
|
||||||
|
// for (var i = 0; i < _lights.Count; i++)
|
||||||
|
// {
|
||||||
|
// var cellIdx = lightCellMap[i];
|
||||||
|
// if (cellIdx == -1)
|
||||||
|
// {
|
||||||
|
// lightVisibleCells.Add([]);
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// var visibleSet = pvs.GetVisible(lightCellMap[i]);
|
||||||
|
// lightVisibleCells.Add(visibleSet);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Console.WriteLine($"17: [{string.Join(", ", pvs.GetVisible(17))}]");
|
||||||
|
//
|
||||||
|
// return lightVisibleCells;
|
||||||
|
// });
|
||||||
|
|
||||||
// TODO: Move this functionality to the LGS library
|
// TODO: Move this functionality to the LGS library
|
||||||
// We set up light indices in separately from lighting because the actual
|
// We set up light indices in separately from lighting because the actual
|
||||||
|
@ -479,10 +471,10 @@ public class LightMapper
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lightVisibleCells[j].Contains(i))
|
// if (!lightVisibleCells[j].Contains(i))
|
||||||
{
|
// {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
|
||||||
cell.LightIndexCount++;
|
cell.LightIndexCount++;
|
||||||
cell.LightIndices.Add((ushort)light.LightTableIndex);
|
cell.LightIndices.Add((ushort)light.LightTableIndex);
|
||||||
|
@ -491,7 +483,7 @@ public class LightMapper
|
||||||
|
|
||||||
if (cell.LightIndexCount > 97)
|
if (cell.LightIndexCount > 97)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"WARNING: Too many lights in cell at ({cell.SphereCenter}): {cell.LightIndexCount - 1} / 96");
|
// Console.WriteLine($"WARNING: Too many lights in cell at ({cell.SphereCenter}): {cell.LightIndexCount - 1} / 96");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -641,12 +633,15 @@ public class LightMapper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var lightIdx in cell.LightIndices)
|
// foreach (var lightIdx in cell.LightIndices)
|
||||||
|
for (var i = 0; i < cell.LightIndexCount; i++)
|
||||||
{
|
{
|
||||||
if (lightIdx == 0)
|
var lightIdx = cell.LightIndices[i];
|
||||||
|
if (i == 0 || lightIdx == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var light = _lights[lightIdx - 1];
|
var light = _lights[lightIdx - 1];
|
||||||
|
|
||||||
// Check if plane normal is facing towards the light
|
// Check if plane normal is facing towards the light
|
||||||
|
@ -661,7 +656,7 @@ public class LightMapper
|
||||||
// If there aren't *any* points on the plane that are in range of the light
|
// If there aren't *any* points on the plane that are in range of the light
|
||||||
// then none of the lightmap points will be so we can discard.
|
// then none of the lightmap points will be so we can discard.
|
||||||
// The more compact a map is the less effective this is
|
// The more compact a map is the less effective this is
|
||||||
var planeDist = MathUtils.DistanceFromPlane(plane, light.Position);
|
var planeDist = Math.Abs(MathUtils.DistanceFromPlane(plane, light.Position));
|
||||||
if (planeDist > light.Radius)
|
if (planeDist > light.Radius)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -5,26 +5,48 @@ namespace KeepersCompound.Lightmapper;
|
||||||
|
|
||||||
public class PotentiallyVisibleSet
|
public class PotentiallyVisibleSet
|
||||||
{
|
{
|
||||||
private class Edge
|
private struct Edge
|
||||||
{
|
{
|
||||||
public int Destination;
|
public int Destination;
|
||||||
public Poly Poly;
|
public Poly Poly;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"<Destination: {Destination}, Poly: {Poly}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Poly(Vector3[] vertices, Plane plane)
|
private struct Poly
|
||||||
{
|
{
|
||||||
public Vector3[] Vertices = vertices;
|
public Vector3[] Vertices;
|
||||||
public Plane Plane = plane;
|
public readonly Plane Plane;
|
||||||
|
|
||||||
|
public Poly(Vector3[] vertices, Plane plane)
|
||||||
|
{
|
||||||
|
Vertices = vertices;
|
||||||
|
Plane = plane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Poly(Poly other)
|
||||||
|
{
|
||||||
|
// TODO: Can this be reverted?
|
||||||
|
var vs = new Vector3[other.Vertices.Length];
|
||||||
|
for (var i = 0; i < vs.Length; i++)
|
||||||
|
{
|
||||||
|
vs[i] = other.Vertices[i];
|
||||||
|
}
|
||||||
|
Vertices = vs;
|
||||||
|
Plane = new Plane(other.Plane.Normal, other.Plane.D);
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsCoplanar(Poly other)
|
public bool IsCoplanar(Poly other)
|
||||||
{
|
{
|
||||||
// TODO: should this be in mathutils?
|
return MathUtils.IsCoplanar(Plane, other.Plane);
|
||||||
const float e = MathUtils.Epsilon;
|
}
|
||||||
var m = Plane.D / other.Plane.D;
|
|
||||||
|
|
||||||
var n0 = Plane.Normal;
|
public override string ToString()
|
||||||
var n1 = other.Plane.Normal * m;
|
{
|
||||||
return Math.Abs(n0.X - n1.X) < e && Math.Abs(n0.Y - n1.Y) < e && Math.Abs(n0.Z - n1.Z) < e;
|
return $"<Plane: {Plane}, Vertices: [{string.Join(", ", Vertices)}]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +54,8 @@ public class PotentiallyVisibleSet
|
||||||
private readonly List<Edge> _edges;
|
private readonly List<Edge> _edges;
|
||||||
private readonly Dictionary<int, HashSet<int>> _visibilitySet;
|
private readonly Dictionary<int, HashSet<int>> _visibilitySet;
|
||||||
|
|
||||||
|
private const float Epsilon = 0.1f;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - This is a conservative algorithm based on Matt's Ramblings Quake PVS video
|
// - This is a conservative algorithm based on Matt's Ramblings Quake PVS video
|
||||||
// - Build portal graph (or just use WR directly)
|
// - Build portal graph (or just use WR directly)
|
||||||
|
@ -134,17 +158,14 @@ public class PotentiallyVisibleSet
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we get to the recursive section
|
ExplorePortalRecursive(visible, edge.Poly, new Poly(innerEdge.Poly), neighbourIdx, innerEdge.Destination, 0);
|
||||||
ComputeClippedVisibility(visible, edge.Poly, innerEdge.Poly, neighbourIdx, innerEdge.Destination, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return visible;
|
return visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Name this better
|
private void ExplorePortalRecursive(
|
||||||
// TODO: This *should* be poly's not edges
|
|
||||||
private void ComputeClippedVisibility(
|
|
||||||
HashSet<int> visible,
|
HashSet<int> visible,
|
||||||
Poly sourcePoly,
|
Poly sourcePoly,
|
||||||
Poly previousPoly,
|
Poly previousPoly,
|
||||||
|
@ -152,40 +173,59 @@ public class PotentiallyVisibleSet
|
||||||
int currentCellIdx,
|
int currentCellIdx,
|
||||||
int depth)
|
int depth)
|
||||||
{
|
{
|
||||||
if (depth > 2048)
|
// TODO: Might need to lose this
|
||||||
|
if (depth > 1024)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
visible.Add(currentCellIdx);
|
visible.Add(currentCellIdx);
|
||||||
|
|
||||||
// Generate separating planes
|
// 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>();
|
var separators = new List<Plane>();
|
||||||
separators.AddRange(GetSeparatingPlanes(sourcePoly, previousPoly, false));
|
GetSeparatingPlanes(separators, sourcePoly, previousPoly, false);
|
||||||
separators.AddRange(GetSeparatingPlanes(previousPoly, sourcePoly, true));
|
GetSeparatingPlanes(separators, previousPoly, sourcePoly, true);
|
||||||
|
|
||||||
|
// The case for this occuring is... interesting ( idk )
|
||||||
|
if (separators.Count == 0)
|
||||||
|
{
|
||||||
|
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))
|
if (edge.Destination == previousCellIdx || previousPoly.IsCoplanar(edge.Poly) || sourcePoly.IsCoplanar(edge.Poly))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var poly = separators.Aggregate(edge.Poly, ClipPolygonByPlane);
|
var poly = new Poly(edge.Poly);
|
||||||
|
foreach (var separator in separators)
|
||||||
|
{
|
||||||
|
poly = ClipPolygonByPlane(poly, separator);
|
||||||
|
}
|
||||||
|
|
||||||
if (poly.Vertices.Length == 0)
|
if (poly.Vertices.Length == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputeClippedVisibility(visible, sourcePoly, poly, currentCellIdx, edge.Destination, depth + 1);
|
ExplorePortalRecursive(visible, sourcePoly, poly, currentCellIdx, edge.Destination, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Plane> GetSeparatingPlanes(Poly p0, Poly p1, bool flip)
|
// 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)
|
||||||
{
|
{
|
||||||
var separators = new List<Plane>();
|
|
||||||
for (var i = 0; i < p0.Vertices.Length; i++)
|
for (var i = 0; i < p0.Vertices.Length; i++)
|
||||||
{
|
{
|
||||||
// brute force all combinations
|
// brute force all combinations
|
||||||
|
@ -196,43 +236,67 @@ public class PotentiallyVisibleSet
|
||||||
{
|
{
|
||||||
var v2 = p1.Vertices[j];
|
var v2 = p1.Vertices[j];
|
||||||
|
|
||||||
var normal = Vector3.Normalize(Vector3.Cross(v1 - v0, v2 - v0));
|
var normal = Vector3.Cross(v1 - v0, v2 - v0);
|
||||||
var d = Vector3.Dot(v2, normal);
|
if (normal.LengthSquared() < Epsilon)
|
||||||
var plane = new Plane(normal, d);
|
|
||||||
|
|
||||||
// Depending on how the edges were built, the resulting plane might be facing the wrong way
|
|
||||||
if (MathUtils.DistanceFromPlane(p0.Plane, v2) < MathUtils.Epsilon)
|
|
||||||
{
|
{
|
||||||
plane.Normal = -plane.Normal;
|
// colinear (or near colinear) points will produce an invalid plane
|
||||||
plane.D = -plane.D;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// All points should be behind/on the plane
|
normal = Vector3.Normalize(normal);
|
||||||
|
var d = -Vector3.Dot(v2, normal);
|
||||||
|
|
||||||
|
// Depending on how the edges were built, the resulting plane might be facing the wrong way
|
||||||
|
var distanceToSource = MathUtils.DistanceFromPlane(p0.Plane, v2);
|
||||||
|
if (distanceToSource > Epsilon)
|
||||||
|
{
|
||||||
|
normal = -normal;
|
||||||
|
d = -d;
|
||||||
|
}
|
||||||
|
|
||||||
|
var plane = new Plane(normal, d);
|
||||||
|
|
||||||
|
if (MathUtils.IsCoplanar(plane, flip ? p0.Plane : p1.Plane))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All points should be in front of the plane (except for the point used to create it)
|
||||||
|
var invalid = false;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
for (var k = 0; k < p1.Vertices.Length; k++)
|
for (var k = 0; k < p1.Vertices.Length; k++)
|
||||||
{
|
{
|
||||||
if (k == j || MathUtils.DistanceFromPlane(plane, p1.Vertices[k]) > MathUtils.Epsilon)
|
if (k == j)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dist = MathUtils.DistanceFromPlane(plane, p1.Vertices[k]);
|
||||||
|
if (dist > Epsilon)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
else if (dist < -Epsilon)
|
||||||
|
{
|
||||||
|
invalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count != p1.Vertices.Length)
|
if (invalid || count == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flip)
|
if (flip)
|
||||||
{
|
{
|
||||||
plane.Normal = -plane.Normal;
|
plane.Normal = -normal;
|
||||||
plane.D = -plane.D;
|
plane.D = -d;
|
||||||
}
|
}
|
||||||
|
|
||||||
separators.Add(plane);
|
separators.Add(plane);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return separators;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Side
|
private enum Side
|
||||||
|
@ -247,6 +311,10 @@ public class PotentiallyVisibleSet
|
||||||
private static Poly ClipPolygonByPlane(Poly poly, Plane plane)
|
private static Poly ClipPolygonByPlane(Poly poly, Plane plane)
|
||||||
{
|
{
|
||||||
var vertexCount = poly.Vertices.Length;
|
var vertexCount = poly.Vertices.Length;
|
||||||
|
if (vertexCount == 0)
|
||||||
|
{
|
||||||
|
return poly;
|
||||||
|
}
|
||||||
|
|
||||||
// Firstly we want to tally up what side of the plane each point of the poly is on
|
// Firstly we want to tally up what side of the plane each point of the poly is on
|
||||||
// This is used both to early out if nothing/everything is clipped, and to aid the clipping
|
// This is used both to early out if nothing/everything is clipped, and to aid the clipping
|
||||||
|
@ -258,23 +326,24 @@ public class PotentiallyVisibleSet
|
||||||
var distance = MathUtils.DistanceFromPlane(plane, poly.Vertices[i]);
|
var distance = MathUtils.DistanceFromPlane(plane, poly.Vertices[i]);
|
||||||
distances[i] = distance;
|
distances[i] = distance;
|
||||||
sides[i] = distance switch {
|
sides[i] = distance switch {
|
||||||
> MathUtils.Epsilon => Side.Back,
|
> Epsilon => Side.Front,
|
||||||
<-MathUtils.Epsilon => Side.Front,
|
<-Epsilon => Side.Back,
|
||||||
_ => Side.On,
|
_ => Side.On,
|
||||||
};
|
};
|
||||||
counts[(int)sides[i]]++;
|
counts[(int)sides[i]]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything is within the half-space, so we don't need to clip anything
|
// Everything is within the half-space, so we don't need to clip anything
|
||||||
if (counts[(int)Side.Back] == 0)
|
if (counts[(int)Side.Back] == 0 && counts[(int)Side.On] != vertexCount)
|
||||||
{
|
{
|
||||||
return new Poly(poly.Vertices, poly.Plane);
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything is outside the half-space, so we clip everything
|
// Everything is outside the half-space, so we clip everything
|
||||||
if (counts[(int)Side.Back] == vertexCount)
|
if (counts[(int)Side.Front] == 0)
|
||||||
{
|
{
|
||||||
return new Poly([], poly.Plane);
|
poly.Vertices = [];
|
||||||
|
return poly;
|
||||||
}
|
}
|
||||||
|
|
||||||
var vertices = new List<Vector3>();
|
var vertices = new List<Vector3>();
|
||||||
|
@ -295,7 +364,7 @@ public class PotentiallyVisibleSet
|
||||||
// We only need to do any clipping if we've swapped from front-to-back or vice versa
|
// We only need to do any clipping if we've swapped from front-to-back or vice versa
|
||||||
// If either the current or next side is On then that's where we would have clipped to
|
// If either the current or next side is On then that's where we would have clipped to
|
||||||
// anyway so we also don't need to do anything
|
// anyway so we also don't need to do anything
|
||||||
if (side == Side.On || nextSide == Side.On || side != nextSide)
|
if (side == Side.On || nextSide == Side.On || side == nextSide)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -306,6 +375,7 @@ public class PotentiallyVisibleSet
|
||||||
vertices.Add(splitVertex);
|
vertices.Add(splitVertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Poly([..vertices], poly.Plane);
|
poly.Vertices = [..vertices];
|
||||||
|
return poly;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -90,7 +90,15 @@ public static class MathUtils
|
||||||
|
|
||||||
public static float DistanceFromPlane(Plane plane, Vector3 point)
|
public static float DistanceFromPlane(Plane plane, Vector3 point)
|
||||||
{
|
{
|
||||||
return Math.Abs(Vector3.Dot(plane.Normal, point) + plane.D) / plane.Normal.Length();
|
return (Vector3.Dot(plane.Normal, point) + plane.D) / plane.Normal.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsCoplanar(Plane p0, Plane p1)
|
||||||
|
{
|
||||||
|
var m = p0.D / p1.D;
|
||||||
|
var n0 = p0.Normal;
|
||||||
|
var n1 = p1.Normal * m;
|
||||||
|
return Math.Abs(n0.X - n1.X) < Epsilon && Math.Abs(n0.Y - n1.Y) < Epsilon && Math.Abs(n0.Z - n1.Z) < Epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PlanePointMapper
|
public record PlanePointMapper
|
||||||
|
|
Loading…
Reference in New Issue