From aae24b2b2c39e5ecafc97ee25519751472783843 Mon Sep 17 00:00:00 2001 From: Regalis Date: Thu, 30 Jun 2016 17:50:02 +0300 Subject: [PATCH] Better logic for determining which convex hulls are in the range of a lightsource (separate convexhull lists for each sub) --- Subsurface/Source/Characters/Limb.cs | 2 +- .../Items/Components/Signal/LightComponent.cs | 4 +- Subsurface/Source/Map/Lights/ConvexHull.cs | 46 ++++- Subsurface/Source/Map/Lights/LightManager.cs | 34 ++-- Subsurface/Source/Map/Lights/LightSource.cs | 179 +++++++++++++++--- Subsurface/Source/Utils/MathUtils.cs | 20 +- 6 files changed, 218 insertions(+), 67 deletions(-) diff --git a/Subsurface/Source/Characters/Limb.cs b/Subsurface/Source/Characters/Limb.cs index 2a77e3192..049c30f8d 100644 --- a/Subsurface/Source/Characters/Limb.cs +++ b/Subsurface/Source/Characters/Limb.cs @@ -428,7 +428,7 @@ namespace Barotrauma { if (LightSource != null) { - LightSource.Submarine = body.Submarine; + LightSource.ParentSub = body.Submarine; LightSource.Position = Position; } diff --git a/Subsurface/Source/Items/Components/Signal/LightComponent.cs b/Subsurface/Source/Items/Components/Signal/LightComponent.cs index 4fa63fdd7..a761383d3 100644 --- a/Subsurface/Source/Items/Components/Signal/LightComponent.cs +++ b/Subsurface/Source/Items/Components/Signal/LightComponent.cs @@ -102,7 +102,7 @@ namespace Barotrauma.Items.Components : base (item, element) { light = new LightSource(element); - light.Submarine = item.CurrentHull == null ? null : item.CurrentHull.Submarine; + light.ParentSub = item.CurrentHull == null ? null : item.CurrentHull.Submarine; light.Position = item.Position; light.CastShadows = castShadows; @@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components { base.Update(deltaTime, cam); - light.Submarine = item.Submarine; + light.ParentSub = item.Submarine; ApplyStatusEffects(ActionType.OnActive, deltaTime); diff --git a/Subsurface/Source/Map/Lights/ConvexHull.cs b/Subsurface/Source/Map/Lights/ConvexHull.cs index f9312bf65..73e791966 100644 --- a/Subsurface/Source/Map/Lights/ConvexHull.cs +++ b/Subsurface/Source/Map/Lights/ConvexHull.cs @@ -42,9 +42,21 @@ namespace Barotrauma.Lights } } + class ConvexHullList + { + public readonly Submarine Submarine; + public List List; + + public ConvexHullList(Submarine submarine) + { + Submarine = submarine; + List = new List(); + } + } + class ConvexHull { - public static List list = new List(); + public static List HullLists = new List(); static BasicEffect shadowEffect; static BasicEffect penumbraEffect; @@ -116,15 +128,20 @@ namespace Barotrauma.Lights Enabled = true; - foreach (ConvexHull ch in list) + var chList = HullLists.Find(x => x.Submarine == parent.Submarine); + if (chList == null) + { + chList = new ConvexHullList(parent.Submarine); + HullLists.Add(chList); + } + + foreach (ConvexHull ch in chList.List) { UpdateIgnoredEdges(ch); ch.UpdateIgnoredEdges(this); } - - list.Add(this); - + chList.List.Add(this); } private void UpdateIgnoredEdges(ConvexHull ch) @@ -219,7 +236,7 @@ namespace Barotrauma.Lights { foreach (KeyValuePair cachedShadow in cachedShadows) { - cachedShadow.Key.NeedsHullUpdate(); + cachedShadow.Key.NeedsHullUpdate = true; cachedShadow.Value.Dispose(); } cachedShadows.Clear(); @@ -377,13 +394,13 @@ namespace Barotrauma.Lights if (parentEntity != null && parentEntity.Submarine != null) { - if (light.Submarine == null) + if (light.ParentSub == null) { lightSourcePos -= parentEntity.Submarine.Position; } - else if (light.Submarine != parentEntity.Submarine) + else if (light.ParentSub != parentEntity.Submarine) { - lightSourcePos += (light.Submarine.Position-parentEntity.Submarine.Position); + lightSourcePos += (light.ParentSub.Position-parentEntity.Submarine.Position); } } @@ -467,7 +484,16 @@ namespace Barotrauma.Lights { ClearCachedShadows(); - list.Remove(this); + var chList = HullLists.Find(x => x.Submarine == parentEntity.Submarine); + + if (chList != null) + { + chList.List.Remove(this); + if (chList.List.Count == 0) + { + HullLists.Remove(chList); + } + } } diff --git a/Subsurface/Source/Map/Lights/LightManager.cs b/Subsurface/Source/Map/Lights/LightManager.cs index cde8e7f5c..7281cf055 100644 --- a/Subsurface/Source/Map/Lights/LightManager.cs +++ b/Subsurface/Source/Map/Lights/LightManager.cs @@ -73,7 +73,7 @@ namespace Barotrauma.Lights { foreach (LightSource light in lights) { - light.UpdateHullsInRange(); + light.NeedsHullUpdate = true; } } @@ -94,10 +94,8 @@ namespace Barotrauma.Lights foreach (LightSource light in lights) { - if (light.Color.A < 0.01f || light.Range < 1.0f) continue; - //!!!!!!!!!!!!!!!! - if (light.hullsInRange == null) light.UpdateHullsInRange(); - if (!light.hullsInRange.Any() || !MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue; + if (light.Color.A < 0.01f || light.Range < 1.0f || !light.CastShadows) continue; + if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue; //clear alpha to 1 ClearAlphaToOne(graphics, spriteBatch); @@ -107,12 +105,7 @@ namespace Barotrauma.Lights graphics.RasterizerState = RasterizerState.CullNone; graphics.BlendState = CustomBlendStates.WriteToAlpha; - foreach (ConvexHull ch in light.hullsInRange) - { - //if (!MathUtils.CircleIntersectsRectangle(light.Position, light.Range, ch.BoundingBox)) continue; - //draw shadow - ch.DrawShadows(graphics, cam, light, shadowTransform, false); - } + light.DrawShadows(graphics, cam, shadowTransform); //draw the light shape //where Alpha is 0, nothing will be written @@ -131,7 +124,7 @@ namespace Barotrauma.Lights foreach (LightSource light in lights) { - if (light.hullsInRange==null || light.hullsInRange.Any() || light.Color.A < 0.01f) continue; + if (light.Color.A < 0.01f || light.Range < 1.0f || light.CastShadows) continue; //if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue; light.Draw(spriteBatch); @@ -186,13 +179,18 @@ namespace Barotrauma.Lights Matrix shadowTransform = cam.ShaderTransform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f; - foreach (ConvexHull convexHull in ConvexHull.list) - { - if (!convexHull.Intersects(camView)) continue; - //if (!camView.Intersects(convexHull.BoundingBox)) continue; + var convexHulls = LightSource.GetHullsInRange(viewTarget.Position, cam.WorldView.Width*0.75f, viewTarget.Submarine); - convexHull.DrawShadows(graphics, cam, pos, shadowTransform); - } + if (convexHulls != null) + { + foreach (ConvexHull convexHull in convexHulls) + { + if (!convexHull.Intersects(camView)) continue; + //if (!camView.Intersects(convexHull.BoundingBox)) continue; + + convexHull.DrawShadows(graphics, cam, pos, shadowTransform); + } + } } graphics.SetRenderTarget(null); } diff --git a/Subsurface/Source/Map/Lights/LightSource.cs b/Subsurface/Source/Map/Lights/LightSource.cs index 030f7e8e2..0542cd359 100644 --- a/Subsurface/Source/Map/Lights/LightSource.cs +++ b/Subsurface/Source/Map/Lights/LightSource.cs @@ -12,7 +12,7 @@ namespace Barotrauma.Lights { private static Texture2D lightTexture; - public List hullsInRange; + private List hullsInRange; private Color color; @@ -24,7 +24,7 @@ namespace Barotrauma.Lights private Sprite overrideLightTexture; - public Entity Submarine; + public Entity ParentSub; public bool CastShadows; @@ -33,6 +33,8 @@ namespace Barotrauma.Lights private Vector2 prevHullUpdatePosition; + public bool NeedsHullUpdate; + private Vector2 position; public Vector2 Position { @@ -43,8 +45,8 @@ namespace Barotrauma.Lights position = value; if (Vector2.Distance(prevHullUpdatePosition, position) < 5.0f) return; - - UpdateHullsInRange(); + + NeedsHullUpdate = true; prevHullUpdatePosition = position; } } @@ -57,7 +59,7 @@ namespace Barotrauma.Lights public Vector2 WorldPosition { - get { return (Submarine == null) ? position : position + Submarine.Position; } + get { return (ParentSub == null) ? position : position + ParentSub.Position; } } public static Texture2D LightTexture @@ -87,8 +89,8 @@ namespace Barotrauma.Lights range = MathHelper.Clamp(value, 0.0f, 2048.0f); if (Math.Abs(prevHullUpdateRange - range) < 10.0f) return; - - UpdateHullsInRange(); + + NeedsHullUpdate = true; prevHullUpdateRange = range; } } @@ -117,9 +119,9 @@ namespace Barotrauma.Lights public LightSource(Vector2 position, float range, Color color, Submarine submarine) { - hullsInRange = new List(); + hullsInRange = new List(); - this.Submarine = submarine; + this.ParentSub = submarine; this.position = position; this.range = range; @@ -132,43 +134,166 @@ namespace Barotrauma.Lights GameMain.LightManager.AddLight(this); } - public void UpdateHullsInRange() + public void DrawShadows(GraphicsDevice graphics, Camera cam, Matrix shadowTransform) { if (!CastShadows) return; - - if (hullsInRange == null) hullsInRange = new List(); - - hullsInRange.Clear(); if (range < 1.0f || color.A < 0.01f) return; - foreach (ConvexHull ch in ConvexHull.list) + foreach (Submarine sub in Submarine.Loaded) { - Vector2 lightPos = position; + var hulls = GetHullsInRange(sub); - if (Submarine==null) + if (hulls == null) continue; + + foreach ( ConvexHull ch in hulls) { - if (ch.ParentEntity.Submarine != null) + ch.DrawShadows(graphics, cam, this, shadowTransform, false); + } + } + + var outsideHulls = GetHullsInRange(null); + + NeedsHullUpdate = false; + + if (outsideHulls == null) return; + foreach (ConvexHull ch in outsideHulls) + { + ch.DrawShadows(graphics, cam, this, shadowTransform, false); + } + } + + private List GetHullsInRange(Submarine sub) + { + var chList = hullsInRange.Find(x => x.Submarine == sub); + + if (chList == null) + { + chList = new ConvexHullList(sub); + hullsInRange.Add(chList); + } + List list = chList.List; + + + Vector2 lightPos = position; + if (ParentSub == null) + { + //light and the convexhull are both outside + if (sub == null) + { + if (NeedsHullUpdate) { - lightPos -= ch.ParentEntity.Submarine.Position; + var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); + + list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); + chList.List = list; } } - else if (ch.ParentEntity.Submarine != null && ch.ParentEntity.Submarine != Submarine) + //light is outside, convexhull inside a sub + else { - lightPos -= (ch.ParentEntity.Submarine.Position - Submarine.Position); - } + //todo: check + lightPos -= sub.Position; - if (MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)) - { - hullsInRange.Add(ch); + Rectangle subBorders = sub.Borders; + subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height); + + //only draw if the light overlaps with the sub + if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) return null; + + var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); + list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); } } + else + { + //light is inside, convexhull outside + if (sub == null) return null; + + //light and convexhull are both inside the same sub + if (sub == ParentSub) + { + if (NeedsHullUpdate) + { + var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); + + list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); + chList.List = list; + } + } + //light and convexhull are inside different subs + else + { + lightPos -= (sub.Position - ParentSub.Position); + + Rectangle subBorders = sub.Borders; + subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height); + + //only draw if the light overlaps with the sub + if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) return null; + + var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); + list = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); + } + } + + return list; } - public void NeedsHullUpdate() + public static List GetHullsInRange(Vector2 position, float range, Submarine ParentSub) { - hullsInRange = null; + List list = new List(); + + foreach (ConvexHullList chList in ConvexHull.HullLists) + { + Vector2 lightPos = position; + if (ParentSub == null) + { + //light and the convexhull are both outside + if (chList.Submarine == null) + { + list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox))); + + } + //light is outside, convexhull inside a sub + else + { + if (!MathUtils.CircleIntersectsRectangle(lightPos - chList.Submarine.WorldPosition, range, chList.Submarine.Borders)) continue; + + lightPos -= (chList.Submarine.WorldPosition - chList.Submarine.HiddenSubPosition); + + list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox))); + } + } + else + { + //light is inside, convexhull outside + if (chList.Submarine == null) continue; + + //light and convexhull are both inside the same sub + if (chList.Submarine == ParentSub) + { + list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox))); + } + //light and convexhull are inside different subs + else + { + lightPos -= (chList.Submarine.Position - ParentSub.Position); + + Rectangle subBorders = chList.Submarine.Borders; + subBorders.Location += chList.Submarine.HiddenSubPosition.ToPoint() - new Point(0, chList.Submarine.Borders.Height); + + if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) continue; + + list.AddRange(chList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox))); + } + } + } + + + return list; } + public void Draw(SpriteBatch spriteBatch) { if (range > 1.0f) diff --git a/Subsurface/Source/Utils/MathUtils.cs b/Subsurface/Source/Utils/MathUtils.cs index cb0b98697..695be7904 100644 --- a/Subsurface/Source/Utils/MathUtils.cs +++ b/Subsurface/Source/Utils/MathUtils.cs @@ -241,20 +241,22 @@ namespace Barotrauma public static bool CircleIntersectsRectangle(Vector2 circlePos, float radius, Rectangle rect) { - Vector2 circleDistance = new Vector2(Math.Abs(circlePos.X - rect.Center.X), Math.Abs(circlePos.Y -rect.Center.Y)); + float xDist = Math.Abs(circlePos.X - rect.Center.X); + int halfWidth = rect.Width / 2; - if (circleDistance.X > (rect.Width / 2 + radius)) { return false; } - if (circleDistance.Y > (rect.Height / 2 + radius)) { return false; } + if (xDist > (halfWidth + radius)) { return false; } + if (xDist <= (halfWidth)) { return true; } - if (circleDistance.X <= (rect.Width / 2)) { return true; } - if (circleDistance.Y <= (rect.Height / 2)) { return true; } + float yDist = Math.Abs(circlePos.Y - rect.Center.Y); + int halfHeight = rect.Height / 2; - float distSqX = circleDistance.X - rect.Width / 2; - float distSqY = circleDistance.Y - rect.Height / 2; + if (yDist > (halfHeight + radius)) { return false; } + if (yDist <= (halfHeight)) { return true; } - float cornerDistanceSq = distSqX * distSqX + distSqY * distSqY; + float distSqX = xDist - halfWidth; + float distSqY = yDist - halfHeight; - return (cornerDistanceSq <= (radius * radius)); + return (distSqX * distSqX + distSqY * distSqY <= (radius * radius)); } ///