From 8c047223855491e5e6f655d5611d9e2343b5a71a Mon Sep 17 00:00:00 2001 From: Joonas Rikkonen Date: Mon, 5 Mar 2018 23:22:07 +0200 Subject: [PATCH] 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). --- .../Source/Map/Lights/ConvexHull.cs | 26 +++++--- .../Source/Map/Lights/LightSource.cs | 66 +++++++++++++++---- 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs b/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs index ca33df0bd..56610a33d 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Lights/ConvexHull.cs @@ -9,6 +9,7 @@ namespace Barotrauma.Lights class ConvexHullList { private List list; + public HashSet IsHidden; public readonly Submarine Submarine; public List 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(); + IsHidden = new HashSet(); } } @@ -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; diff --git a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightSource.cs b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightSource.cs index e4b3e0948..b56b33612 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Lights/LightSource.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Lights/LightSource.cs @@ -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.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 { - 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