Make use of the PVS to reduce light casts

This commit is contained in:
Jarrod Doyle 2025-01-04 23:08:18 +00:00
parent ceb86c0a97
commit c37739b029
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
2 changed files with 94 additions and 7 deletions

View File

@ -371,11 +371,79 @@ public class LightMapper
if (!_mission.TryGetChunk<WorldRep>("WREXT", out var worldRep))
return;
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 visible = pvs.GetVisible(i);
// // Console.WriteLine($"Cell {i}: Count({visible.Length}), Visible[{string.Join(" ", visible)}]");
// }
});
// TODO: Move this functionality to the LGS library
// We set up light indices in separately from lighting because the actual
// lighting phase takes a lot of shortcuts that we don't want
Parallel.ForEach(worldRep.Cells, cell =>
// Parallel.ForEach(worldRep.Cells, cell =>
Parallel.For(0, worldRep.Cells.Length, i =>
{
var cell = worldRep.Cells[i];
cell.LightIndexCount = 0;
cell.LightIndices.Clear();
@ -403,15 +471,23 @@ public class LightMapper
// cache, so we want this to be as minimal as possible. Additionally large
// lists actually cause performance issues!
var cellAabb = new MathUtils.Aabb(cell.Vertices);
foreach (var light in _lights)
for (var j = 0; j < _lights.Count; j++)
{
if (MathUtils.Intersects(new MathUtils.Sphere(light.Position, light.Radius), cellAabb))
var light = _lights[j];
if (!MathUtils.Intersects(new MathUtils.Sphere(light.Position, light.Radius), cellAabb))
{
continue;
}
if (!lightVisibleCells[j].Contains(i))
{
continue;
}
cell.LightIndexCount++;
cell.LightIndices.Add((ushort)light.LightTableIndex);
cell.LightIndices[0]++;
}
}
if (cell.LightIndexCount > 97)
{
@ -437,8 +513,10 @@ public class LightMapper
if (overLit > 0)
{
Console.WriteLine($"WARNING: Overlit cells detected: {overLit}/{worldRep.Cells.Length}, MaxLights: {maxLights} / 96");
Console.WriteLine($"WARNING: Overlit cells detected: {overLit}/{worldRep.Cells.Length}");
}
Console.WriteLine($"MaxLights: {maxLights} / 96");
}
}
@ -619,6 +697,7 @@ public class LightMapper
// Firstly we need to add the light to the cells anim light palette
// Secondly we need to set the appropriate bit of the lightmap's
// bitmask. Finally we need to check if the lightmap needs another layer
// TODO: Handle too many lights for a layer
if (light.Anim)
{
// TODO: Don't recalculate this for every point lol

View File

@ -80,6 +80,14 @@ public static class MathUtils
return (sphere.Position - other.Position).Length() <= rsum;
}
public static bool Intersects(Aabb aabb, Vector3 point)
{
var min = aabb.Min;
var max = aabb.Max;
return point.X >= min.X && point.Y >= min.Y && point.Z >= min.Z &&
point.X <= max.X && point.Y <= max.Y && point.Z <= max.Z;
}
public static float DistanceFromPlane(Plane plane, Vector3 point)
{
return Math.Abs(Vector3.Dot(plane.Normal, point) + plane.D) / plane.Normal.Length();