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

View File

@@ -428,7 +428,7 @@ namespace Barotrauma
{
if (LightSource != null)
{
LightSource.Submarine = body.Submarine;
LightSource.ParentSub = body.Submarine;
LightSource.Position = Position;
}

View File

@@ -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);

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
{
public static List<ConvexHull> list = new List<ConvexHull>();
public static List<ConvexHullList> HullLists = new List<ConvexHullList>();
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<LightSource, CachedShadow> 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);
}
}
}

View File

@@ -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);
}

View File

@@ -12,7 +12,7 @@ namespace Barotrauma.Lights
{
private static Texture2D lightTexture;
public List<ConvexHull> hullsInRange;
private List<ConvexHullList> 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<ConvexHull>();
hullsInRange = new List<ConvexHullList>();
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<ConvexHull>();
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<ConvexHull> GetHullsInRange(Submarine sub)
{
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)
{
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<ConvexHull> GetHullsInRange(Vector2 position, float range, Submarine ParentSub)
{
hullsInRange = null;
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)
{
if (range > 1.0f)

View File

@@ -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));
}
/// <summary>