Renamed project folders from Subsurface to Barotrauma
This commit is contained in:
@@ -0,0 +1,677 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Lights
|
||||
{
|
||||
/*class CachedShadow : IDisposable
|
||||
{
|
||||
public VertexBuffer ShadowBuffer;
|
||||
|
||||
public Vector2 LightPos;
|
||||
|
||||
public int ShadowVertexCount, PenumbraVertexCount;
|
||||
|
||||
public CachedShadow(VertexPositionColor[] shadowVertices, Vector2 lightPos, int shadowVertexCount, int penumbraVertexCount)
|
||||
{
|
||||
//var ShadowVertices = new VertexPositionColor [shadowVertices.Count()];
|
||||
//shadowVertices.CopyTo(ShadowVertices, 0);
|
||||
|
||||
ShadowBuffer = new VertexBuffer(GameMain.CurrGraphicsDevice, VertexPositionColor.VertexDeclaration, 6*2, BufferUsage.None);
|
||||
ShadowBuffer.SetData(shadowVertices, 0, shadowVertices.Length);
|
||||
|
||||
ShadowVertexCount = shadowVertexCount;
|
||||
PenumbraVertexCount = penumbraVertexCount;
|
||||
|
||||
LightPos = lightPos;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
ShadowBuffer.Dispose();
|
||||
}
|
||||
}*/
|
||||
|
||||
class ConvexHullList
|
||||
{
|
||||
private List<ConvexHull> list;
|
||||
|
||||
public readonly Submarine Submarine;
|
||||
public List<ConvexHull> List
|
||||
{
|
||||
get { return list; }
|
||||
set
|
||||
{
|
||||
Debug.Assert(value != null);
|
||||
Debug.Assert(!list.Contains(null));
|
||||
list = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ConvexHullList(Submarine submarine)
|
||||
{
|
||||
Submarine = submarine;
|
||||
list = new List<ConvexHull>();
|
||||
}
|
||||
}
|
||||
|
||||
class Segment
|
||||
{
|
||||
public SegmentPoint Start;
|
||||
public SegmentPoint End;
|
||||
|
||||
public bool IsHorizontal;
|
||||
|
||||
public Segment(SegmentPoint start, SegmentPoint end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
|
||||
start.Segment = this;
|
||||
end.Segment = this;
|
||||
|
||||
IsHorizontal = Math.Abs(start.Pos.X - end.Pos.X) > Math.Abs(start.Pos.Y - end.Pos.Y);
|
||||
}
|
||||
}
|
||||
|
||||
struct SegmentPoint
|
||||
{
|
||||
public Vector2 Pos;
|
||||
public Vector2 WorldPos;
|
||||
|
||||
public Segment Segment;
|
||||
|
||||
public SegmentPoint(Vector2 pos)
|
||||
{
|
||||
Pos = pos;
|
||||
WorldPos = pos;
|
||||
|
||||
Segment = null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Pos.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class ConvexHull
|
||||
{
|
||||
public static List<ConvexHullList> HullLists = new List<ConvexHullList>();
|
||||
static BasicEffect shadowEffect;
|
||||
static BasicEffect penumbraEffect;
|
||||
|
||||
//private Dictionary<LightSource, CachedShadow> cachedShadows;
|
||||
|
||||
public VertexBuffer ShadowBuffer;
|
||||
|
||||
Segment[] segments = new Segment[4];
|
||||
SegmentPoint[] vertices = new SegmentPoint[4];
|
||||
SegmentPoint[] losVertices = new SegmentPoint[4];
|
||||
|
||||
//private Vector2[] vertices;
|
||||
//private Vector2[] losVertices;
|
||||
|
||||
private bool[] backFacing;
|
||||
private bool[] ignoreEdge;
|
||||
|
||||
private VertexPositionColor[] shadowVertices;
|
||||
private VertexPositionTexture[] penumbraVertices;
|
||||
|
||||
int shadowVertexCount;
|
||||
|
||||
private Entity parentEntity;
|
||||
|
||||
private Rectangle boundingBox;
|
||||
|
||||
public Entity ParentEntity
|
||||
{
|
||||
get { return parentEntity; }
|
||||
|
||||
}
|
||||
|
||||
private bool enabled;
|
||||
public bool Enabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (enabled == value) return;
|
||||
enabled = value;
|
||||
LastVertexChangeTime = (float)Timing.TotalTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The elapsed gametime when the vertices of this hull last changed
|
||||
/// </summary>
|
||||
public float LastVertexChangeTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Rectangle BoundingBox
|
||||
{
|
||||
get { return boundingBox; }
|
||||
}
|
||||
|
||||
public ConvexHull(Vector2[] points, Color color, Entity parent)
|
||||
{
|
||||
if (shadowEffect == null)
|
||||
{
|
||||
shadowEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
|
||||
shadowEffect.VertexColorEnabled = true;
|
||||
}
|
||||
if (penumbraEffect == null)
|
||||
{
|
||||
penumbraEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
|
||||
penumbraEffect.TextureEnabled = true;
|
||||
//shadowEffect.VertexColorEnabled = true;
|
||||
penumbraEffect.LightingEnabled = false;
|
||||
penumbraEffect.Texture = TextureLoader.FromFile("Content/Lights/penumbra.png");
|
||||
}
|
||||
|
||||
parentEntity = parent;
|
||||
|
||||
//cachedShadows = new Dictionary<LightSource, CachedShadow>();
|
||||
|
||||
shadowVertices = new VertexPositionColor[6 * 2];
|
||||
penumbraVertices = new VertexPositionTexture[6];
|
||||
|
||||
//vertices = points;
|
||||
SetVertices(points);
|
||||
//CalculateDimensions();
|
||||
|
||||
backFacing = new bool[4];
|
||||
ignoreEdge = new bool[4];
|
||||
|
||||
Enabled = true;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
chList.List.Add(this);
|
||||
}
|
||||
|
||||
private void UpdateIgnoredEdges(ConvexHull ch)
|
||||
{
|
||||
if (ch == this) return;
|
||||
//ignore edges that are inside some other convex hull
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
if (vertices[i].Pos.X >= ch.boundingBox.X && vertices[i].Pos.X <= ch.boundingBox.Right &&
|
||||
vertices[i].Pos.Y >= ch.boundingBox.Y && vertices[i].Pos.Y <= ch.boundingBox.Bottom)
|
||||
{
|
||||
Vector2 p = vertices[(i + 1) % vertices.Length].Pos;
|
||||
|
||||
if (p.X >= ch.boundingBox.X && p.X <= ch.boundingBox.Right &&
|
||||
p.Y >= ch.boundingBox.Y && p.Y <= ch.boundingBox.Bottom)
|
||||
{
|
||||
ignoreEdge[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateDimensions()
|
||||
{
|
||||
float minX = vertices[0].Pos.X, minY = vertices[0].Pos.Y, maxX = vertices[0].Pos.X, maxY = vertices[0].Pos.Y;
|
||||
|
||||
for (int i = 1; i < vertices.Length; i++)
|
||||
{
|
||||
if (vertices[i].Pos.X < minX) minX = vertices[i].Pos.X;
|
||||
if (vertices[i].Pos.Y < minY) minY = vertices[i].Pos.Y;
|
||||
|
||||
if (vertices[i].Pos.X > maxX) maxX = vertices[i].Pos.X;
|
||||
if (vertices[i].Pos.Y > minY) maxY = vertices[i].Pos.Y;
|
||||
}
|
||||
|
||||
boundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
|
||||
}
|
||||
|
||||
public void Move(Vector2 amount)
|
||||
{
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
vertices[i].Pos += amount;
|
||||
losVertices[i].Pos += amount;
|
||||
|
||||
segments[i].Start.Pos += amount;
|
||||
segments[i].End.Pos += amount;
|
||||
}
|
||||
|
||||
LastVertexChangeTime = (float)Timing.TotalTime;
|
||||
|
||||
CalculateDimensions();
|
||||
}
|
||||
|
||||
public void SetVertices(Vector2[] points)
|
||||
{
|
||||
Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported");
|
||||
|
||||
LastVertexChangeTime = (float)Timing.TotalTime;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
vertices[i] = new SegmentPoint(points[i]);
|
||||
losVertices[i] = new SegmentPoint(points[i]);
|
||||
|
||||
}
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4]);
|
||||
}
|
||||
|
||||
int margin = 0;
|
||||
|
||||
if (Math.Abs(points[0].X - points[2].X) < Math.Abs(points[0].Y - points[1].Y))
|
||||
{
|
||||
losVertices[0].Pos = new Vector2(points[0].X + margin, points[0].Y);
|
||||
losVertices[1].Pos = new Vector2(points[1].X + margin, points[1].Y);
|
||||
losVertices[2].Pos = new Vector2(points[2].X - margin, points[2].Y);
|
||||
losVertices[3].Pos = new Vector2(points[3].X - margin, points[3].Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
losVertices[0].Pos = new Vector2(points[0].X, points[0].Y + margin);
|
||||
losVertices[1].Pos = new Vector2(points[1].X, points[1].Y - margin);
|
||||
losVertices[2].Pos = new Vector2(points[2].X, points[2].Y - margin);
|
||||
losVertices[3].Pos = new Vector2(points[3].X, points[3].Y + margin);
|
||||
}
|
||||
|
||||
CalculateDimensions();
|
||||
|
||||
if (parentEntity == null || ignoreEdge == null) return;
|
||||
for (int i = 0; i<4; i++)
|
||||
{
|
||||
ignoreEdge[i] = false;
|
||||
}
|
||||
|
||||
var chList = HullLists.Find(x => x.Submarine == parentEntity.Submarine);
|
||||
if (chList != null)
|
||||
{
|
||||
foreach (ConvexHull ch in chList.List)
|
||||
{
|
||||
UpdateIgnoredEdges(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*private void RemoveCachedShadow(Lights.LightSource light)
|
||||
{
|
||||
CachedShadow shadow = null;
|
||||
cachedShadows.TryGetValue(light, out shadow);
|
||||
|
||||
if (shadow != null)
|
||||
{
|
||||
shadow.Dispose();
|
||||
cachedShadows.Remove(light);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearCachedShadows()
|
||||
{
|
||||
foreach (KeyValuePair<LightSource, CachedShadow> cachedShadow in cachedShadows)
|
||||
{
|
||||
cachedShadow.Key.NeedsHullUpdate = true;
|
||||
cachedShadow.Value.Dispose();
|
||||
}
|
||||
cachedShadows.Clear();
|
||||
}*/
|
||||
|
||||
public bool Intersects(Rectangle rect)
|
||||
{
|
||||
if (!Enabled) return false;
|
||||
|
||||
Rectangle transformedBounds = boundingBox;
|
||||
if (parentEntity != null && parentEntity.Submarine != null)
|
||||
{
|
||||
transformedBounds.X += (int)parentEntity.Submarine.Position.X;
|
||||
transformedBounds.Y += (int)parentEntity.Submarine.Position.Y;
|
||||
}
|
||||
return transformedBounds.Intersects(rect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the segments that are facing towards viewPosition
|
||||
/// </summary>
|
||||
public List<Segment> GetVisibleSegments(Vector2 viewPosition)
|
||||
{
|
||||
List<Segment> visibleFaces = new List<Segment>();
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (ignoreEdge[i]) continue;
|
||||
|
||||
Vector2 pos1 = vertices[i].WorldPos;
|
||||
Vector2 pos2 = vertices[(i + 1) % 4].WorldPos;
|
||||
|
||||
Vector2 middle = (pos1 + pos2) / 2;
|
||||
|
||||
Vector2 L = viewPosition - middle;
|
||||
|
||||
Vector2 N = new Vector2(
|
||||
-(pos2.Y - pos1.Y),
|
||||
pos2.X - pos1.X);
|
||||
|
||||
if (Vector2.Dot(N, L) > 0)
|
||||
{
|
||||
visibleFaces.Add(segments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return visibleFaces;
|
||||
}
|
||||
|
||||
|
||||
public void RefreshWorldPositions()
|
||||
{
|
||||
if (parentEntity == null || parentEntity.Submarine == null) return;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
vertices[i].WorldPos = vertices[i].Pos + parentEntity.Submarine.DrawPosition;
|
||||
segments[i].Start.WorldPos = segments[i].Start.Pos + parentEntity.Submarine.DrawPosition;
|
||||
segments[i].End.WorldPos = segments[i].End.Pos + parentEntity.Submarine.DrawPosition;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateShadowVertices(Vector2 lightSourcePos, bool los = true)
|
||||
{
|
||||
shadowVertexCount = 0;
|
||||
|
||||
var vertices = los ? losVertices : this.vertices;
|
||||
|
||||
//compute facing of each edge, using N*L
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (ignoreEdge[i])
|
||||
{
|
||||
backFacing[i] = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 firstVertex = vertices[i].Pos;
|
||||
Vector2 secondVertex = vertices[(i+1) % 4].Pos;
|
||||
|
||||
Vector2 L = lightSourcePos - ((firstVertex + secondVertex) / 2.0f);
|
||||
|
||||
Vector2 N = new Vector2(
|
||||
-(secondVertex.Y - firstVertex.Y),
|
||||
secondVertex.X - firstVertex.X);
|
||||
|
||||
backFacing[i] = (Vector2.Dot(N, L) < 0) == los;
|
||||
}
|
||||
|
||||
//find beginning and ending vertices which
|
||||
//belong to the shadow
|
||||
int startingIndex = 0;
|
||||
int endingIndex = 0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int currentEdge = i;
|
||||
int nextEdge = (i + 1) % 4;
|
||||
|
||||
if (backFacing[currentEdge] && !backFacing[nextEdge])
|
||||
endingIndex = nextEdge;
|
||||
|
||||
if (!backFacing[currentEdge] && backFacing[nextEdge])
|
||||
startingIndex = nextEdge;
|
||||
}
|
||||
|
||||
//nr of vertices that are in the shadow
|
||||
if (endingIndex > startingIndex)
|
||||
shadowVertexCount = endingIndex - startingIndex + 1;
|
||||
else
|
||||
shadowVertexCount = 4 + 1 - startingIndex + endingIndex;
|
||||
|
||||
//shadowVertices = new VertexPositionColor[shadowVertexCount * 2];
|
||||
|
||||
//create a triangle strip that has the shape of the shadow
|
||||
int currentIndex = startingIndex;
|
||||
int svCount = 0;
|
||||
while (svCount != shadowVertexCount * 2)
|
||||
{
|
||||
Vector3 vertexPos = new Vector3(vertices[currentIndex].Pos, 0.0f);
|
||||
|
||||
int i = los ? svCount : svCount + 1;
|
||||
int j = los ? svCount + 1 : svCount;
|
||||
|
||||
//one vertex on the hull
|
||||
shadowVertices[i] = new VertexPositionColor();
|
||||
shadowVertices[i].Color = los ? Color.Black : Color.Transparent;
|
||||
shadowVertices[i].Position = vertexPos;
|
||||
|
||||
//one extruded by the light direction
|
||||
shadowVertices[j] = new VertexPositionColor();
|
||||
shadowVertices[j].Color = shadowVertices[i].Color;
|
||||
|
||||
Vector3 L2P = vertexPos - new Vector3(lightSourcePos, 0);
|
||||
L2P.Normalize();
|
||||
|
||||
shadowVertices[j].Position = new Vector3(lightSourcePos, 0) + L2P * 9000;
|
||||
|
||||
svCount += 2;
|
||||
currentIndex = (currentIndex + 1) % 4;
|
||||
}
|
||||
|
||||
if (los)
|
||||
{
|
||||
CalculatePenumbraVertices(startingIndex, endingIndex, lightSourcePos, los);
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculatePenumbraVertices(int startingIndex, int endingIndex, Vector2 lightSourcePos, bool los)
|
||||
{
|
||||
for (int n = 0; n < 4; n += 3)
|
||||
{
|
||||
Vector3 penumbraStart = new Vector3((n == 0) ? vertices[startingIndex].Pos : vertices[endingIndex].Pos, 0.0f);
|
||||
|
||||
penumbraVertices[n] = new VertexPositionTexture();
|
||||
penumbraVertices[n].Position = penumbraStart;
|
||||
penumbraVertices[n].TextureCoordinate = new Vector2(0.0f, 1.0f);
|
||||
//penumbraVertices[0].te = fow ? Color.Black : Color.Transparent;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
penumbraVertices[n + i + 1] = new VertexPositionTexture();
|
||||
Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0);
|
||||
vertexDir.Normalize();
|
||||
|
||||
Vector3 normal = (i == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f;
|
||||
if (n > 0) normal = -normal;
|
||||
|
||||
vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f);
|
||||
vertexDir.Normalize();
|
||||
penumbraVertices[n + i + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000;
|
||||
|
||||
if (los)
|
||||
{
|
||||
penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(1.0f, 0.0f) : Vector2.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
var temp = penumbraVertices[4];
|
||||
penumbraVertices[4] = penumbraVertices[5];
|
||||
penumbraVertices[5] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 DrawShadows(GraphicsDevice graphicsDevice, Camera cam, LightSource light, Matrix transform, bool los = true)
|
||||
{
|
||||
if (!Enabled) return;
|
||||
|
||||
Vector2 lightSourcePos = light.Position;
|
||||
|
||||
if (parentEntity != null && parentEntity.Submarine != null)
|
||||
{
|
||||
if (light.ParentSub == null)
|
||||
{
|
||||
lightSourcePos -= parentEntity.Submarine.Position;
|
||||
}
|
||||
else if (light.ParentSub != parentEntity.Submarine)
|
||||
{
|
||||
lightSourcePos += (light.ParentSub.Position-parentEntity.Submarine.Position);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CalculateShadowVertices(lightSourcePos, los);
|
||||
ShadowBuffer = new VertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionColor.VertexDeclaration, 6 * 2, BufferUsage.None);
|
||||
ShadowBuffer.SetData(shadowVertices, 0, shadowVertices.Length);
|
||||
|
||||
graphicsDevice.SetVertexBuffer(ShadowBuffer);
|
||||
shadowVertexCount = shadowVertices.Length;
|
||||
|
||||
DrawShadows(graphicsDevice, cam, transform, los);
|
||||
}
|
||||
|
||||
public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Vector2 lightSourcePos, Matrix transform, bool los = true)
|
||||
{
|
||||
if (!Enabled) return;
|
||||
|
||||
if (parentEntity != null && parentEntity.Submarine != null) lightSourcePos -= parentEntity.Submarine.Position;
|
||||
|
||||
CalculateShadowVertices(lightSourcePos, los);
|
||||
|
||||
DrawShadows(graphicsDevice, cam, transform, los);
|
||||
}
|
||||
|
||||
private void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Matrix transform, bool los = true)
|
||||
{
|
||||
Vector3 offset = Vector3.Zero;
|
||||
if (parentEntity != null && parentEntity.Submarine != null)
|
||||
{
|
||||
offset = new Vector3(parentEntity.Submarine.DrawPosition.X, parentEntity.Submarine.DrawPosition.Y, 0.0f);
|
||||
}
|
||||
|
||||
if (shadowVertexCount>0)
|
||||
{
|
||||
shadowEffect.World = Matrix.CreateTranslation(offset) * transform;
|
||||
|
||||
if (los)
|
||||
{
|
||||
shadowEffect.CurrentTechnique.Passes[0].Apply();
|
||||
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertexCount * 2 - 2, VertexPositionColor.VertexDeclaration);
|
||||
}
|
||||
else
|
||||
{
|
||||
shadowEffect.CurrentTechnique.Passes[0].Apply();
|
||||
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleStrip, 0, shadowVertexCount * 2 - 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (los)
|
||||
{
|
||||
penumbraEffect.World = shadowEffect.World;
|
||||
penumbraEffect.CurrentTechnique.Passes[0].Apply();
|
||||
|
||||
#if WINDOWS
|
||||
graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, penumbraVertices, 0, 2, VertexPositionTexture.VertexDeclaration);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
var chList = HullLists.Find(x => x.Submarine == parentEntity.Submarine);
|
||||
|
||||
if (chList != null)
|
||||
{
|
||||
chList.List.Remove(this);
|
||||
if (chList.List.Count == 0)
|
||||
{
|
||||
HullLists.Remove(chList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Lights
|
||||
{
|
||||
class LightManager
|
||||
{
|
||||
private const float AmbientLightUpdateInterval = 0.2f;
|
||||
private const float AmbientLightFalloff = 0.8f;
|
||||
|
||||
private static Entity viewTarget;
|
||||
|
||||
public static Entity ViewTarget
|
||||
{
|
||||
get { return viewTarget; }
|
||||
set {
|
||||
if (viewTarget == value) return;
|
||||
viewTarget = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Color AmbientLight;
|
||||
|
||||
RenderTarget2D lightMap, losTexture;
|
||||
|
||||
BasicEffect lightEffect;
|
||||
|
||||
private static Texture2D alphaClearTexture;
|
||||
|
||||
private List<LightSource> lights;
|
||||
|
||||
public bool LosEnabled = true;
|
||||
|
||||
public bool LightingEnabled = true;
|
||||
|
||||
public bool ObstructVision;
|
||||
|
||||
private Texture2D visionCircle;
|
||||
|
||||
private Dictionary<Hull, Color> hullAmbientLights;
|
||||
private Dictionary<Hull, Color> smoothedHullAmbientLights;
|
||||
|
||||
private float ambientLightUpdateTimer;
|
||||
|
||||
public LightManager(GraphicsDevice graphics)
|
||||
{
|
||||
lights = new List<LightSource>();
|
||||
|
||||
AmbientLight = new Color(20, 20, 20, 255);
|
||||
|
||||
visionCircle = Sprite.LoadTexture("Content/Lights/visioncircle.png");
|
||||
|
||||
var pp = graphics.PresentationParameters;
|
||||
|
||||
lightMap = new RenderTarget2D(graphics,
|
||||
GameMain.GraphicsWidth, GameMain.GraphicsHeight, false,
|
||||
pp.BackBufferFormat, pp.DepthStencilFormat, pp.MultiSampleCount,
|
||||
RenderTargetUsage.DiscardContents);
|
||||
|
||||
losTexture = new RenderTarget2D(graphics, GameMain.GraphicsWidth, GameMain.GraphicsHeight);
|
||||
|
||||
if (lightEffect == null)
|
||||
{
|
||||
lightEffect = new BasicEffect(GameMain.Instance.GraphicsDevice);
|
||||
lightEffect.VertexColorEnabled = false;
|
||||
|
||||
lightEffect.TextureEnabled = true;
|
||||
lightEffect.Texture = LightSource.LightTexture;
|
||||
}
|
||||
|
||||
hullAmbientLights = new Dictionary<Hull, Color>();
|
||||
smoothedHullAmbientLights = new Dictionary<Hull, Color>();
|
||||
|
||||
if (alphaClearTexture == null)
|
||||
{
|
||||
alphaClearTexture = TextureLoader.FromFile("Content/Lights/alphaOne.png");
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLight(LightSource light)
|
||||
{
|
||||
lights.Add(light);
|
||||
}
|
||||
|
||||
public void RemoveLight(LightSource light)
|
||||
{
|
||||
lights.Remove(light);
|
||||
}
|
||||
|
||||
public void OnMapLoaded()
|
||||
{
|
||||
foreach (LightSource light in lights)
|
||||
{
|
||||
light.NeedsHullCheck = true;
|
||||
light.NeedsRecalculation = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (ambientLightUpdateTimer > 0.0f)
|
||||
{
|
||||
ambientLightUpdateTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateAmbientLights();
|
||||
|
||||
ambientLightUpdateTimer = AmbientLightUpdateInterval;
|
||||
}
|
||||
|
||||
foreach (Hull hull in hullAmbientLights.Keys)
|
||||
{
|
||||
if (!smoothedHullAmbientLights.ContainsKey(hull))
|
||||
{
|
||||
smoothedHullAmbientLights.Add(hull, Color.TransparentBlack);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Hull hull in smoothedHullAmbientLights.Keys.ToList())
|
||||
{
|
||||
Color targetColor = Color.TransparentBlack;
|
||||
|
||||
hullAmbientLights.TryGetValue(hull, out targetColor);
|
||||
|
||||
smoothedHullAmbientLights[hull] = Color.Lerp(smoothedHullAmbientLights[hull], targetColor, deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Effect blur)
|
||||
{
|
||||
if (!LightingEnabled) return;
|
||||
|
||||
graphics.SetRenderTarget(lightMap);
|
||||
|
||||
Rectangle viewRect = cam.WorldView;
|
||||
viewRect.Y -= cam.WorldView.Height;
|
||||
|
||||
//clear to some small ambient light
|
||||
graphics.Clear(AmbientLight);
|
||||
graphics.BlendState = BlendState.Additive;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, null, null, null, cam.Transform);
|
||||
|
||||
Matrix transform = cam.ShaderTransform
|
||||
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
|
||||
|
||||
Vector3 offset = Vector3.Zero;// new Vector3(Submarine.MainSub.DrawPosition.X, Submarine.MainSub.DrawPosition.Y, 0.0f);
|
||||
|
||||
foreach (LightSource light in lights)
|
||||
{
|
||||
if (light.Color.A < 1 || light.Range < 1.0f || !light.CastShadows) continue;
|
||||
if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, light.Range, viewRect)) continue;
|
||||
|
||||
light.Draw(spriteBatch, lightEffect, transform);
|
||||
}
|
||||
|
||||
lightEffect.World = Matrix.CreateTranslation(offset) * transform;
|
||||
|
||||
GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.Additive);
|
||||
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
if (Character.Controlled.ClosestItem != null)
|
||||
{
|
||||
Character.Controlled.ClosestItem.IsHighlighted = true;
|
||||
Character.Controlled.ClosestItem.Draw(spriteBatch, false, true);
|
||||
Character.Controlled.ClosestItem.IsHighlighted = true;
|
||||
}
|
||||
else if (Character.Controlled.ClosestCharacter != null)
|
||||
{
|
||||
Character.Controlled.ClosestCharacter.Draw(spriteBatch);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Hull hull in smoothedHullAmbientLights.Keys)
|
||||
{
|
||||
if (smoothedHullAmbientLights[hull].A < 0.01f) continue;
|
||||
|
||||
var drawRect =
|
||||
hull.Submarine == null ?
|
||||
hull.Rect :
|
||||
new Rectangle((int)(hull.Submarine.DrawPosition.X + hull.Rect.X), (int)(hull.Submarine.DrawPosition.Y + hull.Rect.Y), hull.Rect.Width, hull.Rect.Height);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch,
|
||||
new Vector2(drawRect.X, -drawRect.Y),
|
||||
new Vector2(hull.Rect.Width, hull.Rect.Height),
|
||||
smoothedHullAmbientLights[hull] * 0.5f, true);
|
||||
}
|
||||
|
||||
spriteBatch.End();
|
||||
|
||||
//clear alpha, to avoid messing stuff up later
|
||||
//ClearAlphaToOne(graphics, spriteBatch);
|
||||
|
||||
graphics.SetRenderTarget(null);
|
||||
graphics.BlendState = BlendState.AlphaBlend;
|
||||
}
|
||||
|
||||
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
|
||||
{
|
||||
if (!LosEnabled && !ObstructVision) return;
|
||||
|
||||
graphics.SetRenderTarget(losTexture);
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, cam.Transform);
|
||||
|
||||
if (ObstructVision)
|
||||
{
|
||||
//graphics.Clear(Color.Black);
|
||||
|
||||
Vector2 diff = lookAtPosition - ViewTarget.WorldPosition;
|
||||
diff.Y = -diff.Y;
|
||||
float rotation = MathUtils.VectorToAngle(diff);
|
||||
|
||||
Vector2 scale = new Vector2(MathHelper.Clamp(diff.Length()/256.0f, 2.0f, 5.0f), 2.0f);
|
||||
|
||||
spriteBatch.Draw(visionCircle, new Vector2(ViewTarget.WorldPosition.X, -ViewTarget.WorldPosition.Y), null, Color.White, rotation,
|
||||
new Vector2(LightSource.LightTexture.Width*0.2f, LightSource.LightTexture.Height/2), scale, SpriteEffects.None, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphics.Clear(Color.White);
|
||||
}
|
||||
|
||||
spriteBatch.End();
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
if (LosEnabled && ViewTarget != null)
|
||||
{
|
||||
Vector2 pos = ViewTarget.WorldPosition;
|
||||
|
||||
Rectangle camView = new Rectangle(cam.WorldView.X, cam.WorldView.Y - cam.WorldView.Height, cam.WorldView.Width, cam.WorldView.Height);
|
||||
|
||||
Matrix shadowTransform = cam.ShaderTransform
|
||||
* Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f;
|
||||
|
||||
var convexHulls = ConvexHull.GetHullsInRange(viewTarget.Position, cam.WorldView.Width*0.75f, viewTarget.Submarine);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
private void CalculateAmbientLights()
|
||||
{
|
||||
hullAmbientLights.Clear();
|
||||
|
||||
foreach (LightSource light in lights)
|
||||
{
|
||||
if (light.Color.A < 1f || light.Range < 1.0f) continue;
|
||||
|
||||
var newAmbientLights = AmbientLightHulls(light);
|
||||
foreach (Hull hull in newAmbientLights.Keys)
|
||||
{
|
||||
if (hullAmbientLights.ContainsKey(hull))
|
||||
{
|
||||
//hull already lit by some other light source -> add the ambient lights up
|
||||
hullAmbientLights[hull] = new Color(
|
||||
hullAmbientLights[hull].R + newAmbientLights[hull].R,
|
||||
hullAmbientLights[hull].G + newAmbientLights[hull].G,
|
||||
hullAmbientLights[hull].B + newAmbientLights[hull].B,
|
||||
hullAmbientLights[hull].A + newAmbientLights[hull].A);
|
||||
}
|
||||
else
|
||||
{
|
||||
hullAmbientLights.Add(hull, newAmbientLights[hull]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add ambient light to the hull the lightsource is inside + all adjacent hulls connected by a gap
|
||||
/// </summary>
|
||||
private Dictionary<Hull, Color> AmbientLightHulls(LightSource light)
|
||||
{
|
||||
Dictionary<Hull, Color> hullAmbientLight = new Dictionary<Hull, Color>();
|
||||
|
||||
var hull = Hull.FindHull(light.WorldPosition);
|
||||
if (hull == null) return hullAmbientLight;
|
||||
|
||||
return AmbientLightHulls(hull, hullAmbientLight, light.Color * (light.Range/2000.0f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A flood fill algorithm that adds ambient light to all hulls the starting hull is connected to
|
||||
/// </summary>
|
||||
private Dictionary<Hull, Color> AmbientLightHulls(Hull hull, Dictionary<Hull, Color> hullAmbientLight, Color currColor)
|
||||
{
|
||||
if (hullAmbientLight.ContainsKey(hull))
|
||||
{
|
||||
if (hullAmbientLight[hull].A > currColor.A)
|
||||
return hullAmbientLight;
|
||||
else
|
||||
hullAmbientLight[hull] = currColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
hullAmbientLight.Add(hull, currColor);
|
||||
}
|
||||
|
||||
Color nextHullLight = currColor * AmbientLightFalloff;
|
||||
//light getting too dark to notice -> no need to spread further
|
||||
if (nextHullLight.A < 20) return hullAmbientLight;
|
||||
|
||||
//use hashset to make sure that each hull is only included once
|
||||
HashSet<Hull> hulls = new HashSet<Hull>();
|
||||
foreach (Gap g in hull.ConnectedGaps)
|
||||
{
|
||||
if (!g.IsRoomToRoom || !g.PassAmbientLight || g.Open < 0.5f) continue;
|
||||
|
||||
hulls.Add((g.linkedTo[0] == hull ? g.linkedTo[1] : g.linkedTo[0]) as Hull);
|
||||
}
|
||||
|
||||
foreach (Hull h in hulls)
|
||||
{
|
||||
hullAmbientLight = AmbientLightHulls(h, hullAmbientLight, nextHullLight);
|
||||
}
|
||||
|
||||
return hullAmbientLight;
|
||||
}
|
||||
|
||||
public void DrawLightMap(SpriteBatch spriteBatch, Effect effect)
|
||||
{
|
||||
if (!LightingEnabled) return;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, CustomBlendStates.Multiplicative, null, null, null, effect);
|
||||
spriteBatch.Draw(lightMap, Vector2.Zero, Color.White);
|
||||
spriteBatch.End();
|
||||
}
|
||||
|
||||
public void DrawLOS(SpriteBatch spriteBatch, Effect effect,bool renderingBackground)
|
||||
{
|
||||
if (!LosEnabled || ViewTarget == null) return;
|
||||
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, renderingBackground ? CustomBlendStates.LOS : CustomBlendStates.Multiplicative, null, null, null, effect);
|
||||
spriteBatch.Draw(losTexture, Vector2.Zero, Color.White);
|
||||
spriteBatch.End();
|
||||
|
||||
if (!renderingBackground) ObstructVision = false;
|
||||
}
|
||||
|
||||
public void ClearLights()
|
||||
{
|
||||
lights.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CustomBlendStates
|
||||
{
|
||||
static CustomBlendStates()
|
||||
{
|
||||
Multiplicative = new BlendState();
|
||||
Multiplicative.ColorSourceBlend = Multiplicative.AlphaSourceBlend = Blend.Zero;
|
||||
Multiplicative.ColorDestinationBlend = Multiplicative.AlphaDestinationBlend = Blend.SourceColor;
|
||||
Multiplicative.ColorBlendFunction = Multiplicative.AlphaBlendFunction = BlendFunction.Add;
|
||||
|
||||
WriteToAlpha = new BlendState();
|
||||
WriteToAlpha.ColorWriteChannels = ColorWriteChannels.Alpha;
|
||||
|
||||
MultiplyWithAlpha = new BlendState();
|
||||
MultiplyWithAlpha.ColorDestinationBlend = MultiplyWithAlpha.AlphaDestinationBlend = Blend.One;
|
||||
MultiplyWithAlpha.ColorSourceBlend = MultiplyWithAlpha.AlphaSourceBlend = Blend.DestinationAlpha;
|
||||
|
||||
LOS = new BlendState();
|
||||
LOS.ColorSourceBlend = LOS.AlphaSourceBlend = Blend.Zero;
|
||||
LOS.ColorDestinationBlend = LOS.AlphaDestinationBlend = Blend.InverseSourceColor;
|
||||
LOS.ColorBlendFunction = LOS.AlphaBlendFunction = BlendFunction.Add;
|
||||
}
|
||||
public static BlendState Multiplicative { get; private set; }
|
||||
public static BlendState WriteToAlpha { get; private set; }
|
||||
public static BlendState MultiplyWithAlpha { get; private set; }
|
||||
public static BlendState LOS { get; private set; }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,612 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Lights
|
||||
{
|
||||
class LightSource
|
||||
{
|
||||
private static Texture2D lightTexture;
|
||||
|
||||
private List<ConvexHullList> hullsInRange;
|
||||
|
||||
private Color color;
|
||||
private float range;
|
||||
|
||||
private Sprite overrideLightTexture;
|
||||
private Texture2D texture;
|
||||
|
||||
public Sprite LightSprite;
|
||||
|
||||
public Submarine ParentSub;
|
||||
|
||||
public bool CastShadows;
|
||||
|
||||
//what was the range of the light when lightvolumes were last calculated
|
||||
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;
|
||||
//do we need to recalculate the vertices of the light volume
|
||||
public bool NeedsRecalculation = true;
|
||||
|
||||
//when were the vertices of the light volume last calculated
|
||||
private float lastRecalculationTime;
|
||||
|
||||
private Dictionary<Submarine, Vector2> diffToSub;
|
||||
|
||||
private DynamicVertexBuffer lightVolumeBuffer;
|
||||
private DynamicIndexBuffer lightVolumeIndexBuffer;
|
||||
private int vertexCount;
|
||||
private int indexCount;
|
||||
|
||||
private Vector2 position;
|
||||
public Vector2 Position
|
||||
{
|
||||
get { return position; }
|
||||
set
|
||||
{
|
||||
if (position == value) return;
|
||||
position = value;
|
||||
|
||||
if (Vector2.Distance(prevCalculatedPosition, position) < 5.0f) return;
|
||||
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
prevCalculatedPosition = position;
|
||||
}
|
||||
}
|
||||
|
||||
private float rotation;
|
||||
public float Rotation
|
||||
{
|
||||
get { return rotation; }
|
||||
set
|
||||
{
|
||||
if (rotation == value) return;
|
||||
rotation = value;
|
||||
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 WorldPosition
|
||||
{
|
||||
get { return (ParentSub == null) ? position : position + ParentSub.Position; }
|
||||
}
|
||||
|
||||
public static Texture2D LightTexture
|
||||
{
|
||||
get
|
||||
{
|
||||
if (lightTexture == null)
|
||||
{
|
||||
lightTexture = TextureLoader.FromFile("Content/Lights/light.png");
|
||||
}
|
||||
|
||||
return lightTexture;
|
||||
}
|
||||
}
|
||||
|
||||
public Color Color
|
||||
{
|
||||
get { return color; }
|
||||
set { color = value; }
|
||||
}
|
||||
|
||||
public float Range
|
||||
{
|
||||
get { return range; }
|
||||
set
|
||||
{
|
||||
|
||||
range = MathHelper.Clamp(value, 0.0f, 2048.0f);
|
||||
if (Math.Abs(prevCalculatedRange - range) < 10.0f) return;
|
||||
|
||||
NeedsHullCheck = true;
|
||||
NeedsRecalculation = true;
|
||||
prevCalculatedRange = range;
|
||||
}
|
||||
}
|
||||
|
||||
public LightSource (XElement element)
|
||||
: this(Vector2.Zero, 100.0f, Color.White, null)
|
||||
{
|
||||
range = ToolBox.GetAttributeFloat(element, "range", 100.0f);
|
||||
color = new Color(ToolBox.GetAttributeVector4(element, "color", Vector4.One));
|
||||
|
||||
CastShadows = ToolBox.GetAttributeBool(element, "castshadows", true);
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "sprite":
|
||||
LightSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "lighttexture":
|
||||
overrideLightTexture = new Sprite(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LightSource(Vector2 position, float range, Color color, Submarine submarine)
|
||||
{
|
||||
hullsInRange = new List<ConvexHullList>();
|
||||
|
||||
this.ParentSub = submarine;
|
||||
|
||||
this.position = position;
|
||||
this.range = range;
|
||||
this.color = color;
|
||||
|
||||
CastShadows = true;
|
||||
|
||||
texture = LightTexture;
|
||||
|
||||
diffToSub = new Dictionary<Submarine, Vector2>();
|
||||
|
||||
GameMain.LightManager.AddLight(this);
|
||||
}
|
||||
|
||||
/*public void DrawShadows(GraphicsDevice graphics, Camera cam, Matrix shadowTransform)
|
||||
{
|
||||
if (!CastShadows) return;
|
||||
if (range < 1.0f || color.A < 0.01f) return;
|
||||
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
var hulls = GetHullsInRange(sub);
|
||||
|
||||
if (hulls == null) continue;
|
||||
|
||||
foreach ( ConvexHull ch in hulls)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Update the contents of ConvexHullList and check if we need to recalculate vertices
|
||||
/// </summary>
|
||||
private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub)
|
||||
{
|
||||
var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub);
|
||||
if (fullChList == null) return;
|
||||
|
||||
chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox));
|
||||
|
||||
NeedsHullCheck = true;
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
List<Submarine> subs = new List<Submarine>(Submarine.Loaded);
|
||||
subs.Add(null);
|
||||
|
||||
foreach (Submarine sub in subs)
|
||||
{
|
||||
//find the list of convexhulls that belong to the sub
|
||||
var chList = hullsInRange.Find(x => x.Submarine == sub);
|
||||
|
||||
//not found -> create one
|
||||
if (chList == null)
|
||||
{
|
||||
chList = new ConvexHullList(sub);
|
||||
hullsInRange.Add(chList);
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
|
||||
if (chList.List.Any(ch => ch.LastVertexChangeTime > lastRecalculationTime))
|
||||
{
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
|
||||
Vector2 lightPos = position;
|
||||
if (ParentSub == null)
|
||||
{
|
||||
//light and the convexhulls are both outside
|
||||
if (sub == null)
|
||||
{
|
||||
if (NeedsHullCheck)
|
||||
{
|
||||
RefreshConvexHullList(chList, lightPos, null);
|
||||
}
|
||||
}
|
||||
//light is outside, convexhulls inside a sub
|
||||
else
|
||||
{
|
||||
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))
|
||||
{
|
||||
if (chList.List.Count > 0) NeedsRecalculation = true;
|
||||
chList.List.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
RefreshConvexHullList(chList, lightPos, sub);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//light is inside, convexhull outside
|
||||
if (sub == null) continue;
|
||||
|
||||
//light and convexhull are both inside the same sub
|
||||
if (sub == ParentSub)
|
||||
{
|
||||
if (NeedsHullCheck)
|
||||
{
|
||||
RefreshConvexHullList(chList, lightPos, sub);
|
||||
}
|
||||
}
|
||||
//light and convexhull are inside different subs
|
||||
else
|
||||
{
|
||||
if (sub.DockedTo.Contains(ParentSub) && !NeedsHullCheck) continue;
|
||||
|
||||
lightPos -= (sub.Position - ParentSub.Position);
|
||||
|
||||
Rectangle subBorders = sub.Borders;
|
||||
subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height);
|
||||
|
||||
//don't draw any shadows if the light doesn't overlap with the borders of the sub
|
||||
if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders))
|
||||
{
|
||||
if (chList.List.Count > 0) NeedsRecalculation = true;
|
||||
chList.List.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
//recalculate vertices if the subs have moved > 5 px relative to each other
|
||||
Vector2 diff = ParentSub.WorldPosition - sub.WorldPosition;
|
||||
Vector2 prevDiff;
|
||||
if (!diffToSub.TryGetValue(sub, out prevDiff))
|
||||
{
|
||||
diffToSub.Add(sub, diff);
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
else if (Vector2.DistanceSquared(diff, prevDiff) > 5.0f*5.0f)
|
||||
{
|
||||
diffToSub[sub] = diff;
|
||||
NeedsRecalculation = true;
|
||||
}
|
||||
|
||||
RefreshConvexHullList(chList, lightPos, sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Vector2> FindRaycastHits()
|
||||
{
|
||||
if (!CastShadows) return null;
|
||||
if (range < 1.0f || color.A < 0.01f) return null;
|
||||
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
|
||||
|
||||
var hulls = new List<ConvexHull>();// ConvexHull.GetHullsInRange(position, range, ParentSub);
|
||||
foreach (ConvexHullList chList in hullsInRange)
|
||||
{
|
||||
hulls.AddRange(chList.List);
|
||||
}
|
||||
|
||||
//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();
|
||||
|
||||
var visibleHullSegments = hull.GetVisibleSegments(drawPos);
|
||||
visibleSegments.AddRange(visibleHullSegments);
|
||||
|
||||
foreach (Segment s in visibleHullSegments)
|
||||
{
|
||||
points.Add(s.Start);
|
||||
points.Add(s.End);
|
||||
}
|
||||
}
|
||||
|
||||
//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)
|
||||
var boundaryCorners = new List<SegmentPoint> {
|
||||
new SegmentPoint(new Vector2(drawPos.X + range*2, drawPos.Y + range*2)),
|
||||
new SegmentPoint(new Vector2(drawPos.X + range*2, drawPos.Y - range*2)),
|
||||
new SegmentPoint(new Vector2(drawPos.X - range*2, drawPos.Y - range*2)),
|
||||
new SegmentPoint(new Vector2(drawPos.X - range*2, drawPos.Y + range*2))
|
||||
};
|
||||
|
||||
points.AddRange(boundaryCorners);
|
||||
|
||||
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)
|
||||
{
|
||||
sb.AppendLine(sp.Pos.ToString());
|
||||
}
|
||||
DebugConsole.ThrowError(sb.ToString(), e);
|
||||
}
|
||||
|
||||
List<Vector2> output = new List<Vector2>();
|
||||
|
||||
//remove points that are very close to each other
|
||||
for (int i = 0; i < points.Count - 1; i++)
|
||||
{
|
||||
if (Math.Abs(points[i].WorldPos.X - points[i + 1].WorldPos.X) < 3 &&
|
||||
Math.Abs(points[i].WorldPos.Y - points[i + 1].WorldPos.Y) < 3)
|
||||
{
|
||||
points.RemoveAt(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
Vector2 intersection1 = RayCast(drawPos, drawPos + dir * range * 2 - dirNormal, visibleSegments);
|
||||
Vector2 intersection2 = RayCast(drawPos, drawPos + dir * range * 2 + dirNormal, visibleSegments);
|
||||
|
||||
//hit almost the same position -> only add one vertex to output
|
||||
if ((Math.Abs(intersection1.X - intersection2.X) < 5 &&
|
||||
Math.Abs(intersection1.Y - intersection2.Y) < 5))
|
||||
{
|
||||
output.Add(intersection1);
|
||||
}
|
||||
else
|
||||
{
|
||||
output.Add(intersection1);
|
||||
output.Add(intersection2);
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private Vector2 RayCast(Vector2 rayStart, Vector2 rayEnd, List<Segment> segments)
|
||||
{
|
||||
float closestDist = 0.0f;
|
||||
Vector2? closestIntersection = null;
|
||||
|
||||
foreach (Segment s in segments)
|
||||
{
|
||||
Vector2? intersection = MathUtils.GetAxisAlignedLineIntersection(rayStart, rayEnd, s.Start.WorldPos, s.End.WorldPos, s.IsHorizontal);
|
||||
|
||||
if (intersection != null)
|
||||
{
|
||||
float dist = Vector2.Distance((Vector2)intersection, rayStart);
|
||||
if (closestIntersection == null || dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
closestIntersection = intersection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestIntersection == null ? rayEnd : (Vector2)closestIntersection;
|
||||
}
|
||||
|
||||
private void CalculateLightVertices(List<Vector2> rayCastHits)
|
||||
{
|
||||
List<VertexPositionTexture> vertices = new List<VertexPositionTexture>();
|
||||
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
|
||||
|
||||
float cosAngle = (float)Math.Cos(Rotation);
|
||||
float sinAngle = -(float)Math.Sin(Rotation);
|
||||
|
||||
Vector2 uvOffset = Vector2.Zero;
|
||||
Vector2 overrideTextureDims = Vector2.One;
|
||||
if (overrideLightTexture != null)
|
||||
{
|
||||
overrideTextureDims = new Vector2(overrideLightTexture.SourceRect.Width, overrideLightTexture.SourceRect.Height);
|
||||
uvOffset = (overrideLightTexture.Origin / overrideTextureDims) - new Vector2(0.5f, 0.5f);
|
||||
}
|
||||
|
||||
// Add a vertex for the center of the mesh
|
||||
vertices.Add(new VertexPositionTexture(new Vector3(position.X, position.Y, 0),
|
||||
new Vector2(0.5f, 0.5f) + uvOffset));
|
||||
|
||||
// Add all the other encounter points as vertices
|
||||
// storing their world position as UV coordinates
|
||||
foreach (Vector2 vertex in rayCastHits)
|
||||
{
|
||||
Vector2 rawDiff = vertex - drawPos;
|
||||
Vector2 diff = rawDiff;
|
||||
diff /= range*2.0f;
|
||||
if (overrideLightTexture != null)
|
||||
{
|
||||
Vector2 originDiff = diff;
|
||||
|
||||
diff.X = originDiff.X * cosAngle - originDiff.Y * sinAngle;
|
||||
diff.Y = originDiff.X * sinAngle + originDiff.Y * cosAngle;
|
||||
diff *= (overrideTextureDims / overrideLightTexture.size) * 2.0f;
|
||||
|
||||
diff += uvOffset;
|
||||
}
|
||||
|
||||
vertices.Add(new VertexPositionTexture(new Vector3(position.X + rawDiff.X, position.Y + rawDiff.Y, 0),
|
||||
new Vector2(0.5f, 0.5f) + diff));
|
||||
}
|
||||
|
||||
// Compute the indices to form triangles
|
||||
List<short> indices = new List<short>();
|
||||
for (int i = 0; i < rayCastHits.Count - 1; i++)
|
||||
{
|
||||
indices.Add(0);
|
||||
indices.Add((short)((i + 2) % vertices.Count));
|
||||
indices.Add((short)((i + 1) % vertices.Count));
|
||||
}
|
||||
|
||||
indices.Add(0);
|
||||
indices.Add((short)(1));
|
||||
indices.Add((short)(vertices.Count - 1));
|
||||
|
||||
vertexCount = vertices.Count;
|
||||
indexCount = indices.Count;
|
||||
|
||||
//TODO: a better way to determine the size of the vertex buffer and handle changes in size?
|
||||
//now we just create a buffer for 64 verts and make it larger if needed
|
||||
if (lightVolumeBuffer == null)
|
||||
{
|
||||
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionTexture.VertexDeclaration, Math.Max(64, (int)(vertexCount*1.5)), BufferUsage.None);
|
||||
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), Math.Max(64*3, (int)(indexCount * 1.5)), BufferUsage.None);
|
||||
}
|
||||
else if (vertexCount > lightVolumeBuffer.VertexCount)
|
||||
{
|
||||
lightVolumeBuffer.Dispose();
|
||||
lightVolumeIndexBuffer.Dispose();
|
||||
|
||||
lightVolumeBuffer = new DynamicVertexBuffer(GameMain.Instance.GraphicsDevice, VertexPositionTexture.VertexDeclaration, (int)(vertexCount*1.5), BufferUsage.None);
|
||||
lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.Instance.GraphicsDevice, typeof(short), (int)(indexCount * 1.5), BufferUsage.None);
|
||||
}
|
||||
|
||||
lightVolumeBuffer.SetData<VertexPositionTexture>(vertices.ToArray());
|
||||
lightVolumeIndexBuffer.SetData<short>(indices.ToArray());
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, BasicEffect lightEffect, Matrix transform)
|
||||
{
|
||||
CheckHullsInRange();
|
||||
|
||||
Vector3 offset = ParentSub == null ? Vector3.Zero :
|
||||
new Vector3(ParentSub.DrawPosition.X, ParentSub.DrawPosition.Y, 0.0f);
|
||||
|
||||
lightEffect.World = Matrix.CreateTranslation(offset) * transform;
|
||||
|
||||
Vector2 drawPos = position;
|
||||
if (ParentSub != null) drawPos += ParentSub.DrawPosition;
|
||||
|
||||
drawPos.Y = -drawPos.Y;
|
||||
|
||||
/*if (range > 1.0f)
|
||||
{
|
||||
if (overrideLightTexture == null)
|
||||
{
|
||||
Vector2 center = new Vector2(LightTexture.Width / 2, LightTexture.Height / 2);
|
||||
float scale = range / (lightTexture.Width / 2.0f);
|
||||
|
||||
spriteBatch.Draw(lightTexture, drawPos, null, color * (color.A / 255.0f), 0, center, scale, SpriteEffects.None, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
overrideLightTexture.Draw(spriteBatch,
|
||||
drawPos, color * (color.A / 255.0f),
|
||||
overrideLightTexture.Origin, -Rotation,
|
||||
new Vector2(overrideLightTexture.size.X / overrideLightTexture.SourceRect.Width, overrideLightTexture.size.Y / overrideLightTexture.SourceRect.Height));
|
||||
}
|
||||
}
|
||||
|
||||
if (LightSprite != null)
|
||||
{
|
||||
LightSprite.Draw(spriteBatch, drawPos, Color, LightSprite.Origin, -Rotation, 1);
|
||||
}*/
|
||||
|
||||
if (NeedsRecalculation)
|
||||
{
|
||||
var verts = FindRaycastHits();
|
||||
CalculateLightVertices(verts);
|
||||
|
||||
lastRecalculationTime = (float)Timing.TotalTime;
|
||||
NeedsRecalculation = false;
|
||||
}
|
||||
|
||||
if (vertexCount == 0) return;
|
||||
|
||||
lightEffect.DiffuseColor = (new Vector3(color.R, color.G, color.B) * (color.A / 255.0f)) / 255.0f;// color.ToVector3();
|
||||
if (overrideLightTexture != null)
|
||||
{
|
||||
lightEffect.Texture = overrideLightTexture.Texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
lightEffect.Texture = LightTexture;
|
||||
}
|
||||
lightEffect.CurrentTechnique.Passes[0].Apply();
|
||||
|
||||
GameMain.Instance.GraphicsDevice.SetVertexBuffer(lightVolumeBuffer);
|
||||
GameMain.Instance.GraphicsDevice.Indices = lightVolumeIndexBuffer;
|
||||
|
||||
GameMain.Instance.GraphicsDevice.DrawIndexedPrimitives
|
||||
(
|
||||
PrimitiveType.TriangleList, 0, 0, indexCount / 3
|
||||
);
|
||||
}
|
||||
|
||||
/*public void FlipX()
|
||||
{
|
||||
if (LightSprite != null)
|
||||
{
|
||||
Vector2 lightOrigin = LightSprite.Origin;
|
||||
lightOrigin.X = LightSprite.SourceRect.Width - lightOrigin.X;
|
||||
LightSprite.Origin = lightOrigin;
|
||||
}
|
||||
|
||||
if (overrideLightTexture != null)
|
||||
{
|
||||
Vector2 lightOrigin = overrideLightTexture.Origin;
|
||||
lightOrigin.X = overrideLightTexture.SourceRect.Width - lightOrigin.X;
|
||||
overrideLightTexture.Origin = lightOrigin;
|
||||
}
|
||||
}*/
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
if (LightSprite != null) LightSprite.Remove();
|
||||
|
||||
if (lightVolumeBuffer != null)
|
||||
{
|
||||
lightVolumeBuffer.Dispose();
|
||||
lightVolumeBuffer = null;
|
||||
}
|
||||
|
||||
if (lightVolumeIndexBuffer != null)
|
||||
{
|
||||
lightVolumeIndexBuffer.Dispose();
|
||||
lightVolumeIndexBuffer = null;
|
||||
}
|
||||
|
||||
GameMain.LightManager.RemoveLight(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user