Better logic for determining which convex hulls are in the range of a lightsource (separate convexhull lists for each sub)

This commit is contained in:
Regalis
2016-06-30 17:50:02 +03:00
parent 38c5251005
commit aae24b2b2c
6 changed files with 218 additions and 67 deletions
+1 -1
View File
@@ -428,7 +428,7 @@ namespace Barotrauma
{ {
if (LightSource != null) if (LightSource != null)
{ {
LightSource.Submarine = body.Submarine; LightSource.ParentSub = body.Submarine;
LightSource.Position = Position; LightSource.Position = Position;
} }
@@ -102,7 +102,7 @@ namespace Barotrauma.Items.Components
: base (item, element) : base (item, element)
{ {
light = new LightSource(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.Position = item.Position;
light.CastShadows = castShadows; light.CastShadows = castShadows;
@@ -121,7 +121,7 @@ namespace Barotrauma.Items.Components
{ {
base.Update(deltaTime, cam); base.Update(deltaTime, cam);
light.Submarine = item.Submarine; light.ParentSub = item.Submarine;
ApplyStatusEffects(ActionType.OnActive, deltaTime); ApplyStatusEffects(ActionType.OnActive, deltaTime);
+36 -10
View File
@@ -42,9 +42,21 @@ namespace Barotrauma.Lights
} }
} }
class ConvexHullList
{
public readonly Submarine Submarine;
public List<ConvexHull> List;
public ConvexHullList(Submarine submarine)
{
Submarine = submarine;
List = new List<ConvexHull>();
}
}
class ConvexHull class ConvexHull
{ {
public static List<ConvexHull> list = new List<ConvexHull>(); public static List<ConvexHullList> HullLists = new List<ConvexHullList>();
static BasicEffect shadowEffect; static BasicEffect shadowEffect;
static BasicEffect penumbraEffect; static BasicEffect penumbraEffect;
@@ -116,15 +128,20 @@ namespace Barotrauma.Lights
Enabled = true; 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); UpdateIgnoredEdges(ch);
ch.UpdateIgnoredEdges(this); ch.UpdateIgnoredEdges(this);
} }
chList.List.Add(this);
list.Add(this);
} }
private void UpdateIgnoredEdges(ConvexHull ch) private void UpdateIgnoredEdges(ConvexHull ch)
@@ -219,7 +236,7 @@ namespace Barotrauma.Lights
{ {
foreach (KeyValuePair<LightSource, CachedShadow> cachedShadow in cachedShadows) foreach (KeyValuePair<LightSource, CachedShadow> cachedShadow in cachedShadows)
{ {
cachedShadow.Key.NeedsHullUpdate(); cachedShadow.Key.NeedsHullUpdate = true;
cachedShadow.Value.Dispose(); cachedShadow.Value.Dispose();
} }
cachedShadows.Clear(); cachedShadows.Clear();
@@ -377,13 +394,13 @@ namespace Barotrauma.Lights
if (parentEntity != null && parentEntity.Submarine != null) if (parentEntity != null && parentEntity.Submarine != null)
{ {
if (light.Submarine == null) if (light.ParentSub == null)
{ {
lightSourcePos -= parentEntity.Submarine.Position; 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(); 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);
}
}
} }
+15 -17
View File
@@ -73,7 +73,7 @@ namespace Barotrauma.Lights
{ {
foreach (LightSource light in lights) foreach (LightSource light in lights)
{ {
light.UpdateHullsInRange(); light.NeedsHullUpdate = true;
} }
} }
@@ -94,10 +94,8 @@ namespace Barotrauma.Lights
foreach (LightSource light in lights) foreach (LightSource light in lights)
{ {
if (light.Color.A < 0.01f || light.Range < 1.0f) continue; if (light.Color.A < 0.01f || light.Range < 1.0f || !light.CastShadows) continue;
//!!!!!!!!!!!!!!!! if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue;
if (light.hullsInRange == null) light.UpdateHullsInRange();
if (!light.hullsInRange.Any() || !MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue;
//clear alpha to 1 //clear alpha to 1
ClearAlphaToOne(graphics, spriteBatch); ClearAlphaToOne(graphics, spriteBatch);
@@ -107,12 +105,7 @@ namespace Barotrauma.Lights
graphics.RasterizerState = RasterizerState.CullNone; graphics.RasterizerState = RasterizerState.CullNone;
graphics.BlendState = CustomBlendStates.WriteToAlpha; graphics.BlendState = CustomBlendStates.WriteToAlpha;
foreach (ConvexHull ch in light.hullsInRange) light.DrawShadows(graphics, cam, shadowTransform);
{
//if (!MathUtils.CircleIntersectsRectangle(light.Position, light.Range, ch.BoundingBox)) continue;
//draw shadow
ch.DrawShadows(graphics, cam, light, shadowTransform, false);
}
//draw the light shape //draw the light shape
//where Alpha is 0, nothing will be written //where Alpha is 0, nothing will be written
@@ -131,7 +124,7 @@ namespace Barotrauma.Lights
foreach (LightSource light in 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; //if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue;
light.Draw(spriteBatch); light.Draw(spriteBatch);
@@ -186,12 +179,17 @@ namespace Barotrauma.Lights
Matrix shadowTransform = cam.ShaderTransform Matrix shadowTransform = cam.ShaderTransform
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f; * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
foreach (ConvexHull convexHull in ConvexHull.list) var convexHulls = LightSource.GetHullsInRange(viewTarget.Position, cam.WorldView.Width*0.75f, viewTarget.Submarine);
{
if (!convexHull.Intersects(camView)) continue;
//if (!camView.Intersects(convexHull.BoundingBox)) continue;
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); graphics.SetRenderTarget(null);
+154 -29
View File
@@ -12,7 +12,7 @@ namespace Barotrauma.Lights
{ {
private static Texture2D lightTexture; private static Texture2D lightTexture;
public List<ConvexHull> hullsInRange; private List<ConvexHullList> hullsInRange;
private Color color; private Color color;
@@ -24,7 +24,7 @@ namespace Barotrauma.Lights
private Sprite overrideLightTexture; private Sprite overrideLightTexture;
public Entity Submarine; public Entity ParentSub;
public bool CastShadows; public bool CastShadows;
@@ -33,6 +33,8 @@ namespace Barotrauma.Lights
private Vector2 prevHullUpdatePosition; private Vector2 prevHullUpdatePosition;
public bool NeedsHullUpdate;
private Vector2 position; private Vector2 position;
public Vector2 Position public Vector2 Position
{ {
@@ -44,7 +46,7 @@ namespace Barotrauma.Lights
if (Vector2.Distance(prevHullUpdatePosition, position) < 5.0f) return; if (Vector2.Distance(prevHullUpdatePosition, position) < 5.0f) return;
UpdateHullsInRange(); NeedsHullUpdate = true;
prevHullUpdatePosition = position; prevHullUpdatePosition = position;
} }
} }
@@ -57,7 +59,7 @@ namespace Barotrauma.Lights
public Vector2 WorldPosition public Vector2 WorldPosition
{ {
get { return (Submarine == null) ? position : position + Submarine.Position; } get { return (ParentSub == null) ? position : position + ParentSub.Position; }
} }
public static Texture2D LightTexture public static Texture2D LightTexture
@@ -88,7 +90,7 @@ namespace Barotrauma.Lights
range = MathHelper.Clamp(value, 0.0f, 2048.0f); range = MathHelper.Clamp(value, 0.0f, 2048.0f);
if (Math.Abs(prevHullUpdateRange - range) < 10.0f) return; if (Math.Abs(prevHullUpdateRange - range) < 10.0f) return;
UpdateHullsInRange(); NeedsHullUpdate = true;
prevHullUpdateRange = range; prevHullUpdateRange = range;
} }
} }
@@ -117,9 +119,9 @@ namespace Barotrauma.Lights
public LightSource(Vector2 position, float range, Color color, Submarine submarine) public LightSource(Vector2 position, float range, Color color, Submarine submarine)
{ {
hullsInRange = new List<ConvexHull>(); hullsInRange = new List<ConvexHullList>();
this.Submarine = submarine; this.ParentSub = submarine;
this.position = position; this.position = position;
this.range = range; this.range = range;
@@ -132,43 +134,166 @@ namespace Barotrauma.Lights
GameMain.LightManager.AddLight(this); GameMain.LightManager.AddLight(this);
} }
public void UpdateHullsInRange() public void DrawShadows(GraphicsDevice graphics, Camera cam, Matrix shadowTransform)
{ {
if (!CastShadows) return; if (!CastShadows) return;
if (hullsInRange == null) hullsInRange = new List<ConvexHull>();
hullsInRange.Clear();
if (range < 1.0f || color.A < 0.01f) return; 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;
{
if (ch.ParentEntity.Submarine != null)
{
lightPos -= ch.ParentEntity.Submarine.Position;
}
}
else if (ch.ParentEntity.Submarine != null && ch.ParentEntity.Submarine != Submarine)
{
lightPos -= (ch.ParentEntity.Submarine.Position - Submarine.Position);
}
if (MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)) foreach ( ConvexHull ch in hulls)
{ {
hullsInRange.Add(ch); 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);
}
} }
public void NeedsHullUpdate() private List<ConvexHull> GetHullsInRange(Submarine sub)
{ {
hullsInRange = null; var chList = hullsInRange.Find(x => x.Submarine == sub);
if (chList == null)
{
chList = new ConvexHullList(sub);
hullsInRange.Add(chList);
}
List<ConvexHull> list = chList.List;
Vector2 lightPos = position;
if (ParentSub == null)
{
//light and the convexhull are both outside
if (sub == null)
{
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 is outside, convexhull inside a sub
else
{
//todo: check
lightPos -= sub.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));
}
}
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 static List<ConvexHull> GetHullsInRange(Vector2 position, float range, Submarine ParentSub)
{
List<ConvexHull> list = new List<ConvexHull>();
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) public void Draw(SpriteBatch spriteBatch)
{ {
if (range > 1.0f) if (range > 1.0f)
+11 -9
View File
@@ -241,20 +241,22 @@ namespace Barotrauma
public static bool CircleIntersectsRectangle(Vector2 circlePos, float radius, Rectangle rect) 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 (xDist > (halfWidth + radius)) { return false; }
if (circleDistance.Y > (rect.Height / 2 + radius)) { return false; } if (xDist <= (halfWidth)) { return true; }
if (circleDistance.X <= (rect.Width / 2)) { return true; } float yDist = Math.Abs(circlePos.Y - rect.Center.Y);
if (circleDistance.Y <= (rect.Height / 2)) { return true; } int halfHeight = rect.Height / 2;
float distSqX = circleDistance.X - rect.Width / 2; if (yDist > (halfHeight + radius)) { return false; }
float distSqY = circleDistance.Y - rect.Height / 2; 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));
} }
/// <summary> /// <summary>