Build 1.1.4.0
This commit is contained in:
@@ -15,6 +15,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
public bool Persistent;
|
||||
|
||||
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; private set; } = new Dictionary<Identifier, SerializableProperty>();
|
||||
|
||||
[Serialize("1.0,1.0,1.0,1.0", IsPropertySaveable.Yes, alwaysUseInstanceValues: true), Editable]
|
||||
@@ -52,6 +53,10 @@ namespace Barotrauma.Lights
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = -360, MaxValueFloat = 360, ValueStep = 1, DecimalCount = 0)]
|
||||
public float Rotation { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, "Directional lights only shine in \"one direction\", meaning no shadows are cast behind them."+
|
||||
" Note that this does not affect how the light texture is drawn: if you want something like a conical spotlight, you should use an appropriate texture for that.")]
|
||||
public bool Directional { get; set; }
|
||||
|
||||
public Vector2 GetOffset() => Vector2.Transform(Offset, Matrix.CreateRotationZ(MathHelper.ToRadians(Rotation)));
|
||||
|
||||
private float flicker;
|
||||
@@ -203,7 +208,7 @@ namespace Barotrauma.Lights
|
||||
private VertexPositionColorTexture[] vertices;
|
||||
private short[] indices;
|
||||
|
||||
private readonly List<ConvexHullList> hullsInRange;
|
||||
private readonly List<ConvexHullList> convexHullsInRange;
|
||||
|
||||
public Texture2D texture;
|
||||
|
||||
@@ -222,9 +227,10 @@ namespace Barotrauma.Lights
|
||||
private float prevCalculatedRange;
|
||||
private Vector2 prevCalculatedPosition;
|
||||
|
||||
//do we need to recheck which convex hulls are within range
|
||||
//(e.g. position or range of the lightsource has changed)
|
||||
public bool NeedsHullCheck = true;
|
||||
//Which submarines' convex hulls are up to date? Resets when the item moves/rotates relative to the submarine.
|
||||
//Can contain null (means convex hulls that aren't part of any submarine).
|
||||
public HashSet<Submarine> HullsUpToDate = new HashSet<Submarine>();
|
||||
|
||||
//do we need to recalculate the vertices of the light volume
|
||||
private bool needsRecalculation;
|
||||
public bool NeedsRecalculation
|
||||
@@ -234,7 +240,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (!needsRecalculation && value)
|
||||
{
|
||||
foreach (ConvexHullList chList in hullsInRange)
|
||||
foreach (ConvexHullList chList in convexHullsInRange)
|
||||
{
|
||||
chList.IsHidden.Clear();
|
||||
}
|
||||
@@ -246,6 +252,18 @@ namespace Barotrauma.Lights
|
||||
//when were the vertices of the light volume last calculated
|
||||
public float LastRecalculationTime { get; private set; }
|
||||
|
||||
|
||||
private enum LightVertexState
|
||||
{
|
||||
UpToDate,
|
||||
PendingRayCasts,
|
||||
PendingVertexRecalculation,
|
||||
}
|
||||
|
||||
private LightVertexState state;
|
||||
|
||||
private Vector2 calculatedDrawPos;
|
||||
|
||||
private readonly Dictionary<Submarine, Vector2> diffToSub;
|
||||
|
||||
private DynamicVertexBuffer lightVolumeBuffer;
|
||||
@@ -254,7 +272,6 @@ namespace Barotrauma.Lights
|
||||
private int indexCount;
|
||||
|
||||
private Vector2 translateVertices;
|
||||
private float rotateVertices;
|
||||
|
||||
private readonly LightSourceParams lightSourceParams;
|
||||
|
||||
@@ -277,7 +294,7 @@ namespace Barotrauma.Lights
|
||||
return;
|
||||
}
|
||||
|
||||
NeedsHullCheck = true;
|
||||
HullsUpToDate.Clear();
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
}
|
||||
@@ -292,17 +309,20 @@ namespace Barotrauma.Lights
|
||||
if (Math.Abs(value - rotation) < 0.001f) { return; }
|
||||
rotation = value;
|
||||
|
||||
dir = new Vector2(MathF.Cos(rotation), -MathF.Sin(rotation));
|
||||
|
||||
if (Math.Abs(rotation - prevCalculatedRotation) < RotationRecalculationThreshold && vertices != null)
|
||||
{
|
||||
rotateVertices = rotation - prevCalculatedRotation;
|
||||
return;
|
||||
}
|
||||
|
||||
NeedsHullCheck = true;
|
||||
HullsUpToDate.Clear();
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 dir = Vector2.UnitX;
|
||||
|
||||
private Vector2 _spriteScale = Vector2.One;
|
||||
|
||||
public Vector2 SpriteScale
|
||||
@@ -368,7 +388,7 @@ namespace Barotrauma.Lights
|
||||
lightSourceParams.Range = value;
|
||||
if (Math.Abs(prevCalculatedRange - lightSourceParams.Range) < 10.0f) return;
|
||||
|
||||
NeedsHullCheck = true;
|
||||
HullsUpToDate.Clear();
|
||||
NeedsRecalculation = true;
|
||||
prevCalculatedRange = lightSourceParams.Range;
|
||||
}
|
||||
@@ -384,8 +404,8 @@ namespace Barotrauma.Lights
|
||||
set
|
||||
{
|
||||
NeedsRecalculation = true;
|
||||
NeedsHullCheck = true;
|
||||
lightTextureTargetSize = value;
|
||||
HullsUpToDate.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +444,7 @@ namespace Barotrauma.Lights
|
||||
public bool Enabled = true;
|
||||
|
||||
private readonly ISerializableEntity conditionalTarget;
|
||||
private readonly PropertyConditional.Comparison comparison;
|
||||
private readonly PropertyConditional.LogicalOperatorType logicalOperator;
|
||||
private readonly List<PropertyConditional> conditionals = new List<PropertyConditional>();
|
||||
|
||||
public LightSource(ContentXElement element, ISerializableEntity conditionalTarget = null)
|
||||
@@ -432,11 +452,8 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
lightSourceParams = new LightSourceParams(element);
|
||||
CastShadows = element.GetAttributeBool("castshadows", true);
|
||||
string comparison = element.GetAttributeString("comparison", null);
|
||||
if (comparison != null)
|
||||
{
|
||||
Enum.TryParse(comparison, ignoreCase: true, out this.comparison);
|
||||
}
|
||||
logicalOperator = element.GetAttributeEnum(nameof(logicalOperator),
|
||||
element.GetAttributeEnum("comparison", logicalOperator));
|
||||
|
||||
if (lightSourceParams.DeformableLightSpriteElement != null)
|
||||
{
|
||||
@@ -449,13 +466,7 @@ namespace Barotrauma.Lights
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "conditional":
|
||||
foreach (XAttribute attribute in subElement.Attributes())
|
||||
{
|
||||
if (PropertyConditional.IsValid(attribute))
|
||||
{
|
||||
conditionals.Add(new PropertyConditional(attribute));
|
||||
}
|
||||
}
|
||||
conditionals.AddRange(PropertyConditional.FromXElement(subElement));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -474,7 +485,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
public LightSource(Vector2 position, float range, Color color, Submarine submarine, bool addLight=true)
|
||||
{
|
||||
hullsInRange = new List<ConvexHullList>();
|
||||
convexHullsInRange = new List<ConvexHullList>();
|
||||
this.ParentSub = submarine;
|
||||
this.position = position;
|
||||
lightSourceParams = new LightSourceParams(range, color);
|
||||
@@ -515,19 +526,46 @@ namespace Barotrauma.Lights
|
||||
/// </summary>
|
||||
private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub)
|
||||
{
|
||||
var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub);
|
||||
var fullChList = ConvexHull.HullLists.FirstOrDefault(chList => chList.Submarine == sub);
|
||||
if (fullChList == null) { return; }
|
||||
|
||||
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, ch.BoundingBox));
|
||||
//used to check whether the lightsource hits the target hull if the light is directional
|
||||
Vector2 ray = new Vector2(dir.X, -dir.Y) * TextureRange;
|
||||
Vector2 normal = new Vector2(-ray.Y, ray.X);
|
||||
|
||||
NeedsHullCheck = true;
|
||||
chList.List.Clear();
|
||||
foreach (var convexHull in fullChList.List)
|
||||
{
|
||||
if (!convexHull.Enabled) { continue; }
|
||||
if (!MathUtils.CircleIntersectsRectangle(lightPos, TextureRange, convexHull.BoundingBox)) { continue; }
|
||||
if (lightSourceParams.Directional && false)
|
||||
{
|
||||
Rectangle bounds = convexHull.BoundingBox;
|
||||
//invert because GetLineRectangleIntersection uses the messed up rects that start from top-left
|
||||
bounds.Y -= bounds.Height;
|
||||
|
||||
//the ray can't hit if
|
||||
// center is in the opposite direction from the ray (cheapest check first)
|
||||
if (Vector2.Dot(ray, convexHull.BoundingBox.Center.ToVector2() - lightPos) <= 0 &&
|
||||
/*ray doesn't hit the convex hull*/
|
||||
!MathUtils.GetLineRectangleIntersection(lightPos, lightPos + ray, bounds, out _) &&
|
||||
/*normal vectors of the ray don't hit the convex hull */
|
||||
!MathUtils.GetLineRectangleIntersection(lightPos + normal, lightPos - normal, bounds, out _))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
chList.List.Add(convexHull);
|
||||
}
|
||||
chList.IsHidden.RemoveWhere(ch => !chList.List.Contains(ch));
|
||||
HullsUpToDate.Add(sub);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recheck which convex hulls are in range (if needed),
|
||||
/// and check if we need to recalculate vertices due to changes in the convex hulls
|
||||
/// </summary>
|
||||
private void CheckHullsInRange()
|
||||
private void CheckConvexHullsInRange()
|
||||
{
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
@@ -540,21 +578,13 @@ namespace Barotrauma.Lights
|
||||
private void CheckHullsInRange(Submarine sub)
|
||||
{
|
||||
//find the list of convexhulls that belong to the sub
|
||||
ConvexHullList chList = null;
|
||||
foreach (var ch in hullsInRange)
|
||||
{
|
||||
if (ch.Submarine == sub)
|
||||
{
|
||||
chList = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ConvexHullList chList = convexHullsInRange.FirstOrDefault(chList => chList.Submarine == sub);
|
||||
|
||||
//not found -> create one
|
||||
if (chList == null)
|
||||
{
|
||||
chList = new ConvexHullList(sub);
|
||||
hullsInRange.Add(chList);
|
||||
convexHullsInRange.Add(chList);
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
|
||||
@@ -573,7 +603,7 @@ namespace Barotrauma.Lights
|
||||
//light and the convexhulls are both outside
|
||||
if (sub == null)
|
||||
{
|
||||
if (NeedsHullCheck)
|
||||
if (!HullsUpToDate.Contains(null))
|
||||
{
|
||||
RefreshConvexHullList(chList, lightPos, null);
|
||||
}
|
||||
@@ -605,7 +635,7 @@ namespace Barotrauma.Lights
|
||||
//light and convexhull are both inside the same sub
|
||||
if (sub == ParentSub)
|
||||
{
|
||||
if (NeedsHullCheck)
|
||||
if (!HullsUpToDate.Contains(sub))
|
||||
{
|
||||
RefreshConvexHullList(chList, lightPos, sub);
|
||||
}
|
||||
@@ -613,7 +643,7 @@ namespace Barotrauma.Lights
|
||||
//light and convexhull are inside different subs
|
||||
else
|
||||
{
|
||||
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) { return; }
|
||||
if (sub.DockedTo.Contains(ParentSub) && HullsUpToDate.Contains(sub)) { return; }
|
||||
|
||||
lightPos -= (sub.Position - ParentSub.Position);
|
||||
|
||||
@@ -646,19 +676,45 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
private List<Vector2> FindRaycastHits()
|
||||
private static readonly object mutex = new object();
|
||||
|
||||
private readonly List<Segment> visibleSegments = new List<Segment>();
|
||||
private readonly List<SegmentPoint> points = new List<SegmentPoint>();
|
||||
private readonly List<Vector2> verts = new List<Vector2>();
|
||||
private readonly SegmentPoint[] boundaryCorners = new SegmentPoint[4];
|
||||
private void FindRaycastHits()
|
||||
{
|
||||
if (!CastShadows || Range < 1.0f || Color.A < 1) { return null; }
|
||||
if (!CastShadows || Range < 1.0f || Color.A < 1)
|
||||
{
|
||||
state = LightVertexState.PendingVertexRecalculation;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
||||
|
||||
var hulls = new List<ConvexHull>();
|
||||
foreach (ConvexHullList chList in hullsInRange)
|
||||
visibleSegments.Clear();
|
||||
foreach (ConvexHullList chList in convexHullsInRange)
|
||||
{
|
||||
foreach (ConvexHull hull in chList.List)
|
||||
{
|
||||
if (!chList.IsHidden.Contains(hull)) { hulls.Add(hull); }
|
||||
if (!chList.IsHidden.Contains(hull))
|
||||
{
|
||||
//find convexhull segments that are close enough and facing towards the light source
|
||||
lock (mutex)
|
||||
{
|
||||
hull.RefreshWorldPositions();
|
||||
hull.GetVisibleSegments(drawPos, visibleSegments);
|
||||
foreach (var visibleSegment in visibleSegments)
|
||||
{
|
||||
if (visibleSegment.ConvexHull?.ParentEntity?.Submarine != null)
|
||||
{
|
||||
visibleSegment.SubmarineDrawPos = visibleSegment.ConvexHull.ParentEntity.Submarine.DrawPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
foreach (ConvexHull hull in chList.List)
|
||||
{
|
||||
@@ -666,27 +722,19 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
}
|
||||
|
||||
float bounds = TextureRange;
|
||||
//find convexhull segments that are close enough and facing towards the light source
|
||||
List<Segment> visibleSegments = new List<Segment>();
|
||||
List<SegmentPoint> points = new List<SegmentPoint>();
|
||||
foreach (ConvexHull hull in hulls)
|
||||
{
|
||||
hull.RefreshWorldPositions();
|
||||
hull.GetVisibleSegments(drawPos, visibleSegments, ignoreEdges: false);
|
||||
}
|
||||
state = LightVertexState.PendingRayCasts;
|
||||
GameMain.LightManager.AddRayCastTask(this, drawPos, rotation);
|
||||
}
|
||||
|
||||
//add a square-shaped boundary to make sure we've got something to construct the triangles from
|
||||
//even if there aren't enough hull segments around the light source
|
||||
|
||||
//(might be more effective to calculate if we actually need these extra points)
|
||||
|
||||
public void RayCastTask(Vector2 drawPos, float rotation)
|
||||
{
|
||||
Vector2 drawOffset = Vector2.Zero;
|
||||
float boundsExtended = bounds;
|
||||
float boundsExtended = TextureRange;
|
||||
if (OverrideLightTexture != null)
|
||||
{
|
||||
float cosAngle = (float)Math.Cos(Rotation);
|
||||
float sinAngle = -(float)Math.Sin(Rotation);
|
||||
float cosAngle = (float)Math.Cos(rotation);
|
||||
float sinAngle = -(float)Math.Sin(rotation);
|
||||
|
||||
var overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
|
||||
@@ -706,12 +754,16 @@ namespace Barotrauma.Lights
|
||||
drawOffset.Y = origin.X * sinAngle + origin.Y * cosAngle;
|
||||
}
|
||||
|
||||
var boundaryCorners = new SegmentPoint[] {
|
||||
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X + boundsExtended, drawPos.Y + drawOffset.Y + boundsExtended), null),
|
||||
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X + boundsExtended, drawPos.Y + drawOffset.Y - boundsExtended), null),
|
||||
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X - boundsExtended, drawPos.Y + drawOffset.Y - boundsExtended), null),
|
||||
new SegmentPoint(new Vector2(drawPos.X + drawOffset.X - boundsExtended, drawPos.Y + drawOffset.Y + boundsExtended), null)
|
||||
};
|
||||
//add a square-shaped boundary to make sure we've got something to construct the triangles from
|
||||
//even if there aren't enough hull segments around the light source
|
||||
|
||||
//(might be more effective to calculate if we actually need these extra points)
|
||||
Vector2 boundsMin = drawPos + drawOffset + new Vector2(-boundsExtended, -boundsExtended);
|
||||
Vector2 boundsMax = drawPos + drawOffset + new Vector2(boundsExtended, boundsExtended);
|
||||
boundaryCorners[0] = new SegmentPoint(boundsMax, null);
|
||||
boundaryCorners[1] = new SegmentPoint(new Vector2(boundsMax.X, boundsMin.Y), null);
|
||||
boundaryCorners[2] = new SegmentPoint(boundsMin, null);
|
||||
boundaryCorners[3] = new SegmentPoint(new Vector2(boundsMin.X, boundsMax.Y), null);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
@@ -719,199 +771,200 @@ namespace Barotrauma.Lights
|
||||
visibleSegments.Add(s);
|
||||
}
|
||||
|
||||
//Generate new points at the intersections between segments
|
||||
//This is necessary for the light volume to generate properly on some subs
|
||||
for (int i = 0; i < visibleSegments.Count; i++)
|
||||
lock (mutex)
|
||||
{
|
||||
Vector2 p1a = visibleSegments[i].Start.WorldPos;
|
||||
Vector2 p1b = visibleSegments[i].End.WorldPos;
|
||||
|
||||
for (int j = i + 1; j < visibleSegments.Count; j++)
|
||||
//Generate new points at the intersections between segments
|
||||
//This is necessary for the light volume to generate properly on some subs
|
||||
for (int i = 0; i < visibleSegments.Count; i++)
|
||||
{
|
||||
//ignore intersections between parallel axis-aligned segments
|
||||
if (visibleSegments[i].IsAxisAligned && visibleSegments[j].IsAxisAligned &&
|
||||
visibleSegments[i].IsHorizontal == visibleSegments[j].IsHorizontal)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Vector2 p1a = visibleSegments[i].Start.WorldPos;
|
||||
Vector2 p1b = visibleSegments[i].End.WorldPos;
|
||||
|
||||
Vector2 p2a = visibleSegments[j].Start.WorldPos;
|
||||
Vector2 p2b = visibleSegments[j].End.WorldPos;
|
||||
|
||||
if (Vector2.DistanceSquared(p1a, p2a) < 5.0f ||
|
||||
Vector2.DistanceSquared(p1a, p2b) < 5.0f ||
|
||||
Vector2.DistanceSquared(p1b, p2a) < 5.0f ||
|
||||
Vector2.DistanceSquared(p1b, p2b) < 5.0f)
|
||||
for (int j = i + 1; j < visibleSegments.Count; j++)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool intersects;
|
||||
Vector2 intersection = Vector2.Zero;
|
||||
if (visibleSegments[i].IsAxisAligned)
|
||||
{
|
||||
intersects = MathUtils.GetAxisAlignedLineIntersection(p2a, p2b, p1a, p1b, visibleSegments[i].IsHorizontal, out intersection);
|
||||
}
|
||||
else if (visibleSegments[j].IsAxisAligned)
|
||||
{
|
||||
intersects = MathUtils.GetAxisAlignedLineIntersection(p1a, p1b, p2a, p2b, visibleSegments[j].IsHorizontal, out intersection);
|
||||
}
|
||||
else
|
||||
{
|
||||
intersects = MathUtils.GetLineIntersection(p1a, p1b, p2a, p2b, out intersection);
|
||||
}
|
||||
|
||||
if (intersects)
|
||||
{
|
||||
SegmentPoint start = visibleSegments[i].Start;
|
||||
SegmentPoint end = visibleSegments[i].End;
|
||||
SegmentPoint mid = new SegmentPoint(intersection, null);
|
||||
if (visibleSegments[i].ConvexHull?.ParentEntity?.Submarine != null)
|
||||
{
|
||||
mid.Pos -= visibleSegments[i].ConvexHull.ParentEntity.Submarine.DrawPosition;
|
||||
}
|
||||
|
||||
if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 5.0f ||
|
||||
Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 5.0f)
|
||||
//ignore intersections between parallel axis-aligned segments
|
||||
if (visibleSegments[i].IsAxisAligned && visibleSegments[j].IsAxisAligned &&
|
||||
visibleSegments[i].IsHorizontal == visibleSegments[j].IsHorizontal)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Segment seg1 = new Segment(start, mid, visibleSegments[i].ConvexHull)
|
||||
{
|
||||
IsHorizontal = visibleSegments[i].IsHorizontal,
|
||||
};
|
||||
Vector2 p2a = visibleSegments[j].Start.WorldPos;
|
||||
Vector2 p2b = visibleSegments[j].End.WorldPos;
|
||||
|
||||
Segment seg2 = new Segment(mid, end, visibleSegments[i].ConvexHull)
|
||||
if (Vector2.DistanceSquared(p1a, p2a) < 5.0f ||
|
||||
Vector2.DistanceSquared(p1a, p2b) < 5.0f ||
|
||||
Vector2.DistanceSquared(p1b, p2a) < 5.0f ||
|
||||
Vector2.DistanceSquared(p1b, p2b) < 5.0f)
|
||||
{
|
||||
IsHorizontal = visibleSegments[i].IsHorizontal
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
visibleSegments[i] = seg1;
|
||||
visibleSegments.Insert(i + 1, seg2);
|
||||
bool intersects;
|
||||
Vector2 intersection = Vector2.Zero;
|
||||
if (visibleSegments[i].IsAxisAligned)
|
||||
{
|
||||
intersects = MathUtils.GetAxisAlignedLineIntersection(p2a, p2b, p1a, p1b, visibleSegments[i].IsHorizontal, out intersection);
|
||||
}
|
||||
else if (visibleSegments[j].IsAxisAligned)
|
||||
{
|
||||
intersects = MathUtils.GetAxisAlignedLineIntersection(p1a, p1b, p2a, p2b, visibleSegments[j].IsHorizontal, out intersection);
|
||||
}
|
||||
else
|
||||
{
|
||||
intersects = MathUtils.GetLineIntersection(p1a, p1b, p2a, p2b, out intersection);
|
||||
}
|
||||
|
||||
if (intersects)
|
||||
{
|
||||
SegmentPoint start = visibleSegments[i].Start;
|
||||
SegmentPoint end = visibleSegments[i].End;
|
||||
SegmentPoint mid = new SegmentPoint(intersection, null);
|
||||
mid.Pos -= visibleSegments[i].SubmarineDrawPos;
|
||||
|
||||
if (Vector2.DistanceSquared(start.WorldPos, mid.WorldPos) < 5.0f ||
|
||||
Vector2.DistanceSquared(end.WorldPos, mid.WorldPos) < 5.0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Segment seg1 = new Segment(start, mid, visibleSegments[i].ConvexHull)
|
||||
{
|
||||
IsHorizontal = visibleSegments[i].IsHorizontal,
|
||||
};
|
||||
|
||||
Segment seg2 = new Segment(mid, end, visibleSegments[i].ConvexHull)
|
||||
{
|
||||
IsHorizontal = visibleSegments[i].IsHorizontal
|
||||
};
|
||||
|
||||
visibleSegments[i] = seg1;
|
||||
visibleSegments.Insert(i + 1, seg2);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
points.Clear();
|
||||
//remove segments that fall out of bounds
|
||||
for (int i = 0; i < visibleSegments.Count; i++)
|
||||
{
|
||||
Segment s = visibleSegments[i];
|
||||
if (Math.Abs(s.Start.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
||||
Math.Abs(s.Start.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f ||
|
||||
Math.Abs(s.End.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
||||
Math.Abs(s.End.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f)
|
||||
{
|
||||
visibleSegments.RemoveAt(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
points.Add(s.Start);
|
||||
points.Add(s.End);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove segments that fall out of bounds
|
||||
for (int i = 0; i < visibleSegments.Count; i++)
|
||||
{
|
||||
Segment s = visibleSegments[i];
|
||||
if (Math.Abs(s.Start.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
||||
Math.Abs(s.Start.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f ||
|
||||
Math.Abs(s.End.WorldPos.X - drawPos.X - drawOffset.X) > boundsExtended + 1.0f ||
|
||||
Math.Abs(s.End.WorldPos.Y - drawPos.Y - drawOffset.Y) > boundsExtended + 1.0f)
|
||||
//remove points that are very close to each other
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
{
|
||||
visibleSegments.RemoveAt(i);
|
||||
i--;
|
||||
for (int j = Math.Min(i + 4, points.Count - 1); j > i; j--)
|
||||
{
|
||||
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < 6 &&
|
||||
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < 6)
|
||||
{
|
||||
points.RemoveAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
var compareCCW = new CompareSegmentPointCW(drawPos);
|
||||
try
|
||||
{
|
||||
points.Add(s.Start);
|
||||
points.Add(s.End);
|
||||
points.Sort(compareCCW);
|
||||
}
|
||||
}
|
||||
|
||||
visibleSegments = visibleSegments.OrderBy(s => MathUtils.LineToPointDistanceSquared(s.Start.WorldPos, s.End.WorldPos, drawPos)).ToList();
|
||||
|
||||
var compareCCW = new CompareSegmentPointCW(drawPos);
|
||||
try
|
||||
{
|
||||
points.Sort(compareCCW);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: " + drawPos + ", Hull verts:\n");
|
||||
foreach (SegmentPoint sp in points)
|
||||
catch (Exception e)
|
||||
{
|
||||
sb.AppendLine(sp.Pos.ToString());
|
||||
StringBuilder sb = new StringBuilder("Constructing light volumes failed! Light pos: " + drawPos + ", Hull verts:\n");
|
||||
foreach (SegmentPoint sp in points)
|
||||
{
|
||||
sb.AppendLine(sp.Pos.ToString());
|
||||
}
|
||||
DebugConsole.ThrowError(sb.ToString(), e);
|
||||
}
|
||||
DebugConsole.ThrowError(sb.ToString(), e);
|
||||
}
|
||||
|
||||
List<Vector2> output = new List<Vector2>();
|
||||
//List<Pair<int, Vector2>> preOutput = new List<Pair<int, Vector2>>();
|
||||
visibleSegments.Sort((s1, s2) =>
|
||||
MathUtils.LineToPointDistanceSquared(s1.Start.WorldPos, s1.End.WorldPos, drawPos)
|
||||
.CompareTo(MathUtils.LineToPointDistanceSquared(s2.Start.WorldPos, s2.End.WorldPos, drawPos)));
|
||||
|
||||
verts.Clear();
|
||||
foreach (SegmentPoint p in points)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos);
|
||||
Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;
|
||||
|
||||
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
|
||||
var intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
|
||||
if (intersection1.index < 0) { return; }
|
||||
var intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
|
||||
if (intersection2.index < 0) { return; }
|
||||
|
||||
Segment seg1 = visibleSegments[intersection1.index];
|
||||
Segment seg2 = visibleSegments[intersection2.index];
|
||||
|
||||
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
|
||||
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
|
||||
|
||||
if (isPoint1 && isPoint2)
|
||||
{
|
||||
//hit at the current segmentpoint -> place the segmentpoint into the list
|
||||
verts.Add(p.WorldPos);
|
||||
|
||||
foreach (ConvexHullList hullList in convexHullsInRange)
|
||||
{
|
||||
hullList.IsHidden.Remove(p.ConvexHull);
|
||||
hullList.IsHidden.Remove(seg1.ConvexHull);
|
||||
hullList.IsHidden.Remove(seg2.ConvexHull);
|
||||
}
|
||||
}
|
||||
else if (intersection1.index != intersection2.index)
|
||||
{
|
||||
//the raycasts landed on different segments
|
||||
//we definitely want to generate new geometry here
|
||||
verts.Add(isPoint1 ? p.WorldPos : intersection1.pos);
|
||||
verts.Add(isPoint2 ? p.WorldPos : intersection2.pos);
|
||||
|
||||
foreach (ConvexHullList hullList in convexHullsInRange)
|
||||
{
|
||||
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
|
||||
//and creating geometry here would be wasteful
|
||||
}
|
||||
}
|
||||
|
||||
//remove points that are very close to each other
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
for (int i = 0; i < verts.Count - 1; i++)
|
||||
{
|
||||
for (int j = Math.Min(i + 4, points.Count-1); j > i; j--)
|
||||
for (int j = Math.Min(i + 4, verts.Count - 1); j > i; j--)
|
||||
{
|
||||
if (Math.Abs(points[i].WorldPos.X - points[j].WorldPos.X) < 6 &&
|
||||
Math.Abs(points[i].WorldPos.Y - points[j].WorldPos.Y) < 6)
|
||||
if (Math.Abs(verts[i].X - verts[j].X) < 6 &&
|
||||
Math.Abs(verts[i].Y - verts[j].Y) < 6)
|
||||
{
|
||||
points.RemoveAt(j);
|
||||
verts.RemoveAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (SegmentPoint p in points)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(p.WorldPos - drawPos);
|
||||
Vector2 dirNormal = new Vector2(-dir.Y, dir.X) * 3;
|
||||
|
||||
//do two slightly offset raycasts to hit the segment itself and whatever's behind it
|
||||
var intersection1 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 - dirNormal, visibleSegments);
|
||||
var intersection2 = RayCast(drawPos, drawPos + dir * boundsExtended * 2 + dirNormal, visibleSegments);
|
||||
|
||||
if (intersection1.index < 0) return null;
|
||||
if (intersection2.index < 0) return null;
|
||||
Segment seg1 = visibleSegments[intersection1.index];
|
||||
Segment seg2 = visibleSegments[intersection2.index];
|
||||
|
||||
bool isPoint1 = MathUtils.LineToPointDistanceSquared(seg1.Start.WorldPos, seg1.End.WorldPos, p.WorldPos) < 25.0f;
|
||||
bool isPoint2 = MathUtils.LineToPointDistanceSquared(seg2.Start.WorldPos, seg2.End.WorldPos, p.WorldPos) < 25.0f;
|
||||
|
||||
if (isPoint1 && isPoint2)
|
||||
{
|
||||
//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.index != intersection2.index)
|
||||
{
|
||||
//the raycasts landed on different segments
|
||||
//we definitely want to generate new geometry here
|
||||
output.Add(isPoint1 ? p.WorldPos : intersection1.pos);
|
||||
output.Add(isPoint2 ? p.WorldPos : intersection2.pos);
|
||||
|
||||
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
|
||||
//and creating geometry here would be wasteful
|
||||
}
|
||||
|
||||
//remove points that are very close to each other
|
||||
for (int i = 0; i < output.Count - 1; i++)
|
||||
{
|
||||
for (int j = Math.Min(i + 4, output.Count - 1); j > i; j--)
|
||||
{
|
||||
if (Math.Abs(output[i].X - output[j].X) < 6 &&
|
||||
Math.Abs(output[i].Y - output[j].Y) < 6)
|
||||
{
|
||||
output.RemoveAt(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
calculatedDrawPos = drawPos;
|
||||
state = LightVertexState.PendingVertexRecalculation;
|
||||
}
|
||||
|
||||
private (int index, Vector2 pos) RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
|
||||
private static (int index, Vector2 pos) RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
|
||||
{
|
||||
Vector2? closestIntersection = null;
|
||||
int segment = -1;
|
||||
@@ -936,13 +989,13 @@ namespace Barotrauma.Lights
|
||||
//same for the x-axis
|
||||
if (s.Start.WorldPos.X > s.End.WorldPos.X)
|
||||
{
|
||||
if (s.Start.WorldPos.X < minX) continue;
|
||||
if (s.End.WorldPos.X > maxX) continue;
|
||||
if (s.Start.WorldPos.X < minX) { continue; }
|
||||
if (s.End.WorldPos.X > maxX) { continue; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s.End.WorldPos.X < minX) continue;
|
||||
if (s.Start.WorldPos.X > maxX) continue;
|
||||
if (s.End.WorldPos.X < minX) { continue; }
|
||||
if (s.Start.WorldPos.X > maxX) { continue; }
|
||||
}
|
||||
|
||||
bool intersects;
|
||||
@@ -986,14 +1039,11 @@ namespace Barotrauma.Lights
|
||||
indices = new short[indexCount];
|
||||
}
|
||||
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null) { drawPos += ParentSub.DrawPosition; }
|
||||
|
||||
float cosAngle = (float)Math.Cos(Rotation);
|
||||
float sinAngle = -(float)Math.Sin(Rotation);
|
||||
Vector2 drawPos = calculatedDrawPos;
|
||||
|
||||
Vector2 uvOffset = Vector2.Zero;
|
||||
Vector2 overrideTextureDims = Vector2.One;
|
||||
Vector2 dir = this.dir;
|
||||
if (OverrideLightTexture != null)
|
||||
{
|
||||
overrideTextureDims = new Vector2(OverrideLightTexture.SourceRect.Width, OverrideLightTexture.SourceRect.Height);
|
||||
@@ -1002,8 +1052,7 @@ namespace Barotrauma.Lights
|
||||
if (LightSpriteEffect == SpriteEffects.FlipHorizontally)
|
||||
{
|
||||
origin.X = OverrideLightTexture.SourceRect.Width - origin.X;
|
||||
cosAngle = -cosAngle;
|
||||
sinAngle = -sinAngle;
|
||||
dir = -dir;
|
||||
}
|
||||
if (LightSpriteEffect == SpriteEffects.FlipVertically) { origin.Y = OverrideLightTexture.SourceRect.Height - origin.Y; }
|
||||
uvOffset = (origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
|
||||
@@ -1041,7 +1090,7 @@ namespace Barotrauma.Lights
|
||||
|
||||
//calculate normal of first segment
|
||||
Vector2 nDiff1 = vertex - nextVertex;
|
||||
float tx = nDiff1.X; nDiff1.X = -nDiff1.Y; nDiff1.Y = tx;
|
||||
nDiff1 = new Vector2(-nDiff1.Y, nDiff1.X);
|
||||
nDiff1 /= Math.Max(Math.Abs(nDiff1.X), Math.Abs(nDiff1.Y));
|
||||
//if the normal is pointing towards the light origin
|
||||
//rather than away from it, invert it
|
||||
@@ -1049,21 +1098,23 @@ namespace Barotrauma.Lights
|
||||
|
||||
//calculate normal of second segment
|
||||
Vector2 nDiff2 = prevVertex - vertex;
|
||||
tx = nDiff2.X; nDiff2.X = -nDiff2.Y; nDiff2.Y = tx;
|
||||
nDiff2 /= Math.Max(Math.Abs(nDiff2.X),Math.Abs(nDiff2.Y));
|
||||
nDiff2 = new Vector2(-nDiff2.Y, nDiff2.X);
|
||||
nDiff2 /= Math.Max(Math.Abs(nDiff2.X), Math.Abs(nDiff2.Y));
|
||||
//if the normal is pointing towards the light origin
|
||||
//rather than away from it, invert it
|
||||
if (Vector2.DistanceSquared(nDiff2, rawDiff) > Vector2.DistanceSquared(-nDiff2, rawDiff)) nDiff2 = -nDiff2;
|
||||
|
||||
//add the normals together and use some magic numbers to create
|
||||
//a somewhat useful/good-looking blur
|
||||
Vector2 nDiff = nDiff1 * 40.0f;
|
||||
if (MathUtils.GetLineIntersection(vertex + (nDiff1 * 40.0f), nextVertex + (nDiff1 * 40.0f), vertex + (nDiff2 * 40.0f), prevVertex + (nDiff2 * 40.0f), true, out Vector2 intersection))
|
||||
float blurDistance = 40.0f;
|
||||
Vector2 nDiff = nDiff1 * blurDistance;
|
||||
if (MathUtils.GetLineIntersection(vertex + (nDiff1 * blurDistance), nextVertex + (nDiff1 * blurDistance), vertex + (nDiff2 * blurDistance), prevVertex + (nDiff2 * blurDistance), true, out Vector2 intersection))
|
||||
{
|
||||
nDiff = intersection - vertex;
|
||||
if (nDiff.LengthSquared() > 10000.0f)
|
||||
if (nDiff.LengthSquared() > 100.0f * 100.0f)
|
||||
{
|
||||
nDiff /= Math.Max(Math.Abs(nDiff.X), Math.Abs(nDiff.Y)); nDiff *= 100.0f;
|
||||
nDiff /= Math.Max(Math.Abs(nDiff.X), Math.Abs(nDiff.Y));
|
||||
nDiff *= 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1074,8 +1125,8 @@ namespace Barotrauma.Lights
|
||||
//calculate texture coordinates based on the light's rotation
|
||||
Vector2 originDiff = diff;
|
||||
|
||||
diff.X = originDiff.X * cosAngle - originDiff.Y * sinAngle;
|
||||
diff.Y = originDiff.X * sinAngle + originDiff.Y * cosAngle;
|
||||
diff.X = originDiff.X * dir.X - originDiff.Y * dir.Y;
|
||||
diff.Y = originDiff.X * dir.Y + originDiff.Y * dir.X;
|
||||
diff *= (overrideTextureDims / OverrideLightTexture.size);// / (1.0f - Math.Max(Math.Abs(uvOffset.X), Math.Abs(uvOffset.Y)));
|
||||
diff += uvOffset;
|
||||
}
|
||||
@@ -1161,7 +1212,6 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
|
||||
translateVertices = Vector2.Zero;
|
||||
rotateVertices = 0.0f;
|
||||
prevCalculatedPosition = position;
|
||||
prevCalculatedRotation = rotation;
|
||||
}
|
||||
@@ -1181,9 +1231,6 @@ namespace Barotrauma.Lights
|
||||
}
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
float cosAngle = (float)Math.Cos(Rotation);
|
||||
float sinAngle = -(float)Math.Sin(Rotation);
|
||||
|
||||
float bounds = TextureRange;
|
||||
|
||||
if (OverrideLightTexture != null)
|
||||
@@ -1195,8 +1242,8 @@ namespace Barotrauma.Lights
|
||||
origin /= Math.Max(overrideTextureDims.X, overrideTextureDims.Y);
|
||||
origin *= TextureRange;
|
||||
|
||||
drawPos.X += origin.X * sinAngle + origin.Y * cosAngle;
|
||||
drawPos.Y += origin.X * cosAngle + origin.Y * sinAngle;
|
||||
drawPos.X += origin.X * dir.Y + origin.Y * dir.X;
|
||||
drawPos.Y += origin.X * dir.X + origin.Y * dir.Y;
|
||||
}
|
||||
|
||||
//add a square-shaped boundary to make sure we've got something to construct the triangles from
|
||||
@@ -1302,7 +1349,7 @@ namespace Barotrauma.Lights
|
||||
{
|
||||
if (conditionals.None()) { return; }
|
||||
if (conditionalTarget == null) { return; }
|
||||
if (comparison == PropertyConditional.Comparison.And)
|
||||
if (logicalOperator == PropertyConditional.LogicalOperatorType.And)
|
||||
{
|
||||
Enabled = conditionals.All(c => c.Matches(conditionalTarget));
|
||||
}
|
||||
@@ -1335,35 +1382,43 @@ namespace Barotrauma.Lights
|
||||
return;
|
||||
}
|
||||
|
||||
CheckHullsInRange();
|
||||
CheckConvexHullsInRange();
|
||||
|
||||
if (NeedsRecalculation && allowRecalculation)
|
||||
{
|
||||
recalculationCount++;
|
||||
var verts = FindRaycastHits();
|
||||
if (verts == null)
|
||||
if (state == LightVertexState.UpToDate)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Failed to generate vertices for a light source. Range: {Range}, color: {Color}, brightness: {CurrentBrightness}, parent: {ParentBody?.UserData ?? "Unknown"}");
|
||||
#endif
|
||||
Enabled = false;
|
||||
return;
|
||||
recalculationCount++;
|
||||
FindRaycastHits();
|
||||
}
|
||||
else if (state == LightVertexState.PendingVertexRecalculation)
|
||||
{
|
||||
if (verts == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError($"Failed to generate vertices for a light source. Range: {Range}, color: {Color}, brightness: {CurrentBrightness}, parent: {ParentBody?.UserData ?? "Unknown"}");
|
||||
#endif
|
||||
Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
CalculateLightVertices(verts);
|
||||
CalculateLightVertices(verts);
|
||||
|
||||
LastRecalculationTime = (float)Timing.TotalTime;
|
||||
NeedsRecalculation = false;
|
||||
LastRecalculationTime = (float)Timing.TotalTime;
|
||||
NeedsRecalculation = false;
|
||||
state = LightVertexState.UpToDate;
|
||||
}
|
||||
}
|
||||
|
||||
if (vertexCount == 0) { return; }
|
||||
|
||||
Vector2 offset = ParentSub == null ? Vector2.Zero : ParentSub.DrawPosition;
|
||||
lightEffect.World =
|
||||
Matrix.CreateTranslation(-new Vector3(position, 0.0f)) *
|
||||
Matrix.CreateRotationZ(rotateVertices - MathHelper.ToRadians(LightSourceParams.Rotation)) *
|
||||
Matrix.CreateRotationZ(MathHelper.ToRadians(LightSourceParams.Rotation)) *
|
||||
Matrix.CreateTranslation(new Vector3(position + offset + translateVertices, 0.0f)) *
|
||||
transform;
|
||||
|
||||
if (vertexCount == 0) { return; }
|
||||
|
||||
lightEffect.DiffuseColor = (new Vector3(Color.R, Color.G, Color.B) * (Color.A / 255.0f * CurrentBrightness)) / 255.0f;
|
||||
if (OverrideLightTexture != null)
|
||||
@@ -1387,9 +1442,9 @@ namespace Barotrauma.Lights
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
hullsInRange.Clear();
|
||||
HullsUpToDate.Clear();
|
||||
convexHullsInRange.Clear();
|
||||
diffToSub.Clear();
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
|
||||
vertexCount = 0;
|
||||
|
||||
Reference in New Issue
Block a user