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