Lighting optimization: lightsources mark convex hulls that are within range but not visible as hidden and don't recalculate the light volume when these convex hulls change (= e.g. when opening/closing a door, only the lightsources that cast light to the door have to recalculated).

This commit is contained in:
Joonas Rikkonen
2018-03-05 23:22:07 +02:00
parent d57102271d
commit 8c04722385
2 changed files with 68 additions and 24 deletions
@@ -9,6 +9,7 @@ namespace Barotrauma.Lights
class ConvexHullList
{
private List<ConvexHull> list;
public HashSet<ConvexHull> IsHidden;
public readonly Submarine Submarine;
public List<ConvexHull> List
@@ -19,13 +20,16 @@ namespace Barotrauma.Lights
Debug.Assert(value != null);
Debug.Assert(!list.Contains(null));
list = value;
IsHidden.RemoveWhere(ch => !list.Contains(ch));
}
}
public ConvexHullList(Submarine submarine)
{
Submarine = submarine;
list = new List<ConvexHull>();
IsHidden = new HashSet<ConvexHull>();
}
}
@@ -34,15 +38,18 @@ namespace Barotrauma.Lights
public SegmentPoint Start;
public SegmentPoint End;
public ConvexHull ConvexHull;
public bool IsHorizontal;
public Segment(SegmentPoint start, SegmentPoint end)
public Segment(SegmentPoint start, SegmentPoint end, ConvexHull convexHull)
{
Start = start;
End = end;
ConvexHull = convexHull;
start.Segment = this;
end.Segment = this;
start.ConvexHull = convexHull;
end.ConvexHull = convexHull;
IsHorizontal = Math.Abs(start.Pos.X - end.Pos.X) > Math.Abs(start.Pos.Y - end.Pos.Y);
}
@@ -53,14 +60,13 @@ namespace Barotrauma.Lights
public Vector2 Pos;
public Vector2 WorldPos;
public Segment Segment;
public ConvexHull ConvexHull;
public SegmentPoint(Vector2 pos)
public SegmentPoint(Vector2 pos, ConvexHull convexHull)
{
Pos = pos;
WorldPos = pos;
Segment = null;
ConvexHull = convexHull;
}
public override string ToString()
@@ -244,13 +250,13 @@ namespace Barotrauma.Lights
for (int i = 0; i < 4; i++)
{
vertices[i] = new SegmentPoint(points[i]);
losVertices[i] = new SegmentPoint(points[i]);
vertices[i] = new SegmentPoint(points[i], this);
losVertices[i] = new SegmentPoint(points[i], this);
}
for (int i = 0; i < 4; i++)
{
segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4]);
segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4], this);
}
int margin = 0;
@@ -42,7 +42,22 @@ namespace Barotrauma.Lights
//(e.g. position or range of the lightsource has changed)
public bool NeedsHullCheck = true;
//do we need to recalculate the vertices of the light volume
public bool NeedsRecalculation = true;
private bool needsRecalculation;
public bool NeedsRecalculation
{
get { return needsRecalculation; }
set
{
if (!needsRecalculation && value)
{
foreach (ConvexHullList chList in hullsInRange)
{
chList.IsHidden.Clear();
}
}
needsRecalculation = value;
}
}
//when were the vertices of the light volume last calculated
private float lastRecalculationTime;
@@ -64,7 +79,7 @@ namespace Barotrauma.Lights
position = value;
if (Vector2.DistanceSquared(prevCalculatedPosition, position) < 5.0f * 5.0f) return;
NeedsHullCheck = true;
NeedsRecalculation = true;
prevCalculatedPosition = position;
@@ -117,7 +132,7 @@ namespace Barotrauma.Lights
range = MathHelper.Clamp(value, 0.0f, 2048.0f);
if (Math.Abs(prevCalculatedRange - range) < 10.0f) return;
NeedsHullCheck = true;
NeedsRecalculation = true;
prevCalculatedRange = range;
@@ -209,7 +224,7 @@ namespace Barotrauma.Lights
NeedsRecalculation = true;
}
if (chList.List.Any(ch => ch.LastVertexChangeTime > lastRecalculationTime))
if (chList.List.Any(ch => ch.LastVertexChangeTime > lastRecalculationTime && !chList.IsHidden.Contains(ch)))
{
NeedsRecalculation = true;
}
@@ -309,7 +324,16 @@ namespace Barotrauma.Lights
var hulls = new List<ConvexHull>();// ConvexHull.GetHullsInRange(position, range, ParentSub);
foreach (ConvexHullList chList in hullsInRange)
{
hulls.AddRange(chList.List);
//hulls.AddRange(chList.List);
foreach (ConvexHull hull in chList.List)
{
if (!chList.IsHidden.Contains(hull)) hulls.Add(hull);
//hulls.Add(hull);
}
foreach (ConvexHull hull in chList.List)
{
chList.IsHidden.Add(hull);
}
}
float bounds = range * 2;
@@ -353,7 +377,7 @@ namespace Barotrauma.Lights
SegmentPoint start = visibleSegments[i].Start;
SegmentPoint end = visibleSegments[i].End;
SegmentPoint mid = new SegmentPoint(intersectionVal);
SegmentPoint mid = new SegmentPoint(intersectionVal, null);
if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 25.0f ||
Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 25.0f)
@@ -361,8 +385,8 @@ namespace Barotrauma.Lights
continue;
}
Segment seg1 = new Segment(start, mid); seg1.IsHorizontal = visibleSegments[i].IsHorizontal;
Segment seg2 = new Segment(mid, end); seg2.IsHorizontal = visibleSegments[i].IsHorizontal;
Segment seg1 = new Segment(start, mid, visibleSegments[i].ConvexHull); seg1.IsHorizontal = visibleSegments[i].IsHorizontal;
Segment seg2 = new Segment(mid, end, visibleSegments[i].ConvexHull); seg2.IsHorizontal = visibleSegments[i].IsHorizontal;
visibleSegments[i] = seg1;
visibleSegments.Insert(i+1,seg2);
i--;
@@ -386,17 +410,17 @@ namespace Barotrauma.Lights
//(might be more effective to calculate if we actually need these extra points)
var boundaryCorners = new List<SegmentPoint> {
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y + bounds)),
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y - bounds)),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y - bounds)),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y + bounds))
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y + bounds), null),
new SegmentPoint(new Vector2(drawPos.X + bounds, drawPos.Y - bounds), null),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y - bounds), null),
new SegmentPoint(new Vector2(drawPos.X - bounds, drawPos.Y + bounds), null)
};
points.AddRange(boundaryCorners);
for (int i = 0; i < 4; i++)
{
visibleSegments.Add(new Segment(boundaryCorners[i], boundaryCorners[(i + 1) % 4]));
visibleSegments.Add(new Segment(boundaryCorners[i], boundaryCorners[(i + 1) % 4], null));
}
var compareCCW = new CompareSegmentPointCW(drawPos);
@@ -447,8 +471,15 @@ namespace Barotrauma.Lights
if (isPoint1 && isPoint2)
{
//hit at the current segmentpoint -> place the segmentpoint into the list
//hit at the current segmentpoint -> place the segmentpoint into the list
output.Add(p.WorldPos);
foreach (ConvexHullList hullList in hullsInRange)
{
hullList.IsHidden.Remove(p.ConvexHull);
hullList.IsHidden.Remove(seg1.ConvexHull);
hullList.IsHidden.Remove(seg2.ConvexHull);
}
}
else if (intersection1.First != intersection2.First)
{
@@ -456,6 +487,13 @@ namespace Barotrauma.Lights
//we definitely want to generate new geometry here
output.Add(isPoint1 ? p.WorldPos : intersection1.Second);
output.Add(isPoint2 ? p.WorldPos : intersection2.Second);
foreach (ConvexHullList hullList in hullsInRange)
{
hullList.IsHidden.Remove(p.ConvexHull);
hullList.IsHidden.Remove(seg1.ConvexHull);
hullList.IsHidden.Remove(seg2.ConvexHull);
}
}
//if neither of the conditions above are met, we just assume
//that the raycasts both resulted on the same segment