diff --git a/Subsurface/Source/GameMain.cs b/Subsurface/Source/GameMain.cs index fb6ac0329..e8c171971 100644 --- a/Subsurface/Source/GameMain.cs +++ b/Subsurface/Source/GameMain.cs @@ -103,6 +103,15 @@ namespace Barotrauma { get { return NetworkMember as GameClient; } } + + /// + /// Total seconds elapsed after startup + /// + public double TotalElapsedTime + { + get; + private set; + } public GameMain() { @@ -297,11 +306,12 @@ namespace Barotrauma protected override void Update(GameTime gameTime) { Timing.Accumulator += gameTime.ElapsedGameTime.TotalSeconds; - bool paused = true; while (Timing.Accumulator >= Timing.Step) { + TotalElapsedTime = gameTime.TotalGameTime.TotalSeconds; + fixedTime.IsRunningSlowly = gameTime.IsRunningSlowly; TimeSpan addTime = new TimeSpan(0, 0, 0, 0, 16); fixedTime.ElapsedGameTime = addTime; diff --git a/Subsurface/Source/Map/Lights/ConvexHull.cs b/Subsurface/Source/Map/Lights/ConvexHull.cs index 7ac0d6c67..2740c7693 100644 --- a/Subsurface/Source/Map/Lights/ConvexHull.cs +++ b/Subsurface/Source/Map/Lights/ConvexHull.cs @@ -110,7 +110,7 @@ namespace Barotrauma.Lights //private Dictionary cachedShadows; public VertexBuffer ShadowBuffer; - + Segment[] segments = new Segment[4]; SegmentPoint[] vertices = new SegmentPoint[4]; SegmentPoint[] losVertices = new SegmentPoint[4]; @@ -136,10 +136,28 @@ namespace Barotrauma.Lights } + private bool enabled; public bool Enabled + { + get + { + return enabled; + } + set + { + if (enabled == value) return; + enabled = value; + LastVertexChangeTime = (float)GameMain.Instance.TotalElapsedTime; + } + } + + /// + /// The elapsed gametime when the vertices of this hull last changed + /// + public float LastVertexChangeTime { get; - set; + private set; } public Rectangle BoundingBox @@ -197,6 +215,8 @@ namespace Barotrauma.Lights 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 && @@ -233,13 +253,15 @@ namespace Barotrauma.Lights { for (int i = 0; i < vertices.Length; i++) { - vertices[i].Pos += amount; - losVertices[i].Pos += amount; + vertices[i].Pos += amount; + losVertices[i].Pos += amount; - segments[i].Start.Pos += amount; - segments[i].End.Pos += amount; + segments[i].Start.Pos += amount; + segments[i].End.Pos += amount; } + LastVertexChangeTime = (float)GameMain.Instance.TotalElapsedTime; + CalculateDimensions(); } @@ -247,6 +269,8 @@ namespace Barotrauma.Lights { Debug.Assert(points.Length == 4, "Only rectangular convex hulls are supported"); + LastVertexChangeTime = (float)GameMain.Instance.TotalElapsedTime; + for (int i = 0; i < 4; i++) { vertices[i] = new SegmentPoint(points[i]); @@ -256,10 +280,6 @@ namespace Barotrauma.Lights for (int i = 0; i < 4; i++) { segments[i] = new Segment(vertices[i], vertices[(i + 1) % 4]); - - //vertices[i].Segments[1] = segments[i]; - //vertices[(i + 1) % 4].Segments[0] = segments[i]; - } int margin = 0; @@ -280,6 +300,21 @@ namespace Barotrauma.Lights } 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) diff --git a/Subsurface/Source/Map/Lights/LightManager.cs b/Subsurface/Source/Map/Lights/LightManager.cs index 21364e4f9..b1ae2f9f9 100644 --- a/Subsurface/Source/Map/Lights/LightManager.cs +++ b/Subsurface/Source/Map/Lights/LightManager.cs @@ -94,7 +94,8 @@ namespace Barotrauma.Lights { foreach (LightSource light in lights) { - light.NeedsHullUpdate = true; + light.NeedsHullCheck = true; + light.NeedsRecalculation = true; } } @@ -152,16 +153,16 @@ namespace Barotrauma.Lights Vector3 offset = Vector3.Zero;// new Vector3(Submarine.MainSub.DrawPosition.X, Submarine.MainSub.DrawPosition.Y, 0.0f); - lightEffect.World = Matrix.CreateTranslation(offset) * transform; - 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); + light.Draw(spriteBatch, lightEffect, transform); } + lightEffect.World = Matrix.CreateTranslation(offset) * transform; + GameMain.ParticleManager.Draw(spriteBatch, false, Particles.ParticleBlendState.Additive); if (Character.Controlled != null) diff --git a/Subsurface/Source/Map/Lights/LightSource.cs b/Subsurface/Source/Map/Lights/LightSource.cs index cd303ff82..4368b05f3 100644 --- a/Subsurface/Source/Map/Lights/LightSource.cs +++ b/Subsurface/Source/Map/Lights/LightSource.cs @@ -15,27 +15,35 @@ namespace Barotrauma.Lights private List hullsInRange; private Color color; - private float range; - - public SpriteEffects SpriteEffect = SpriteEffects.None; - + + private Sprite overrideLightTexture; private Texture2D texture; + public SpriteEffects SpriteEffect = SpriteEffects.None; public Sprite LightSprite; - private Sprite overrideLightTexture; - public Submarine ParentSub; public bool CastShadows; - //what was the range of the light when HullsInRange were last updated - private float prevHullUpdateRange; + //what was the range of the light when lightvolumes were last calculated + private float prevCalculatedRange; + private Vector2 prevCalculatedPosition; - private Vector2 prevHullUpdatePosition; + //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; - public bool NeedsHullUpdate; + //when were the vertices of the light volume last calculated + private float lastRecalculationTime; + + private DynamicVertexBuffer lightVolumeBuffer; + private DynamicIndexBuffer lightVolumeIndexBuffer; + private int vertexCount; + private int indexCount; private Vector2 position; public Vector2 Position @@ -46,10 +54,11 @@ namespace Barotrauma.Lights if (position == value) return; position = value; - if (Vector2.Distance(prevHullUpdatePosition, position) < 5.0f) return; + if (Vector2.Distance(prevCalculatedPosition, position) < 5.0f) return; - NeedsHullUpdate = true; - prevHullUpdatePosition = position; + NeedsHullCheck = true; + NeedsRecalculation = true; + prevCalculatedPosition = position; } } @@ -90,10 +99,11 @@ namespace Barotrauma.Lights { range = MathHelper.Clamp(value, 0.0f, 2048.0f); - if (Math.Abs(prevHullUpdateRange - range) < 10.0f) return; + if (Math.Abs(prevCalculatedRange - range) < 10.0f) return; - NeedsHullUpdate = true; - prevHullUpdateRange = range; + NeedsHullCheck = true; + NeedsRecalculation = true; + prevCalculatedRange = range; } } @@ -130,9 +140,9 @@ namespace Barotrauma.Lights this.color = color; CastShadows = true; - + texture = LightTexture; - + GameMain.LightManager.AddLight(this); } @@ -162,82 +172,112 @@ namespace Barotrauma.Lights { ch.DrawShadows(graphics, cam, this, shadowTransform, false); } - } - - private List GetHullsInRange(Submarine sub) - { - //find the current list of hulls in range - var chList = hullsInRange.Find(x => x.Submarine == sub); - - //not found -> create one - if (chList == null) - { - chList = new ConvexHullList(sub); - hullsInRange.Add(chList); - } - - Vector2 lightPos = position; - if (ParentSub == null) - { - //light and the convexhull are both outside - if (sub == null) - { - if (NeedsHullUpdate) - { - var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - if (fullChList != null) - chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); - } - } - //light is outside, convexhull 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)) return null; - - var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); - } - } - else - { - //light is inside, convexhull outside - if (sub == null) return null; - - //light and convexhull are both inside the same sub - if (sub == ParentSub) - { - if (NeedsHullUpdate) - { - var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); - } - } - //light and convexhull are inside different subs - else - { - if (sub.DockedTo.Contains(ParentSub) && !NeedsHullUpdate) return chList.List; - - lightPos -= (sub.Position - ParentSub.Position); - - Rectangle subBorders = sub.Borders; - subBorders.Location += sub.HiddenSubPosition.ToPoint() - new Point(0, sub.Borders.Height); - - //only draw if the light overlaps with the sub - if (!MathUtils.CircleIntersectsRectangle(lightPos, range, subBorders)) return null; - - var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); - chList.List = fullChList.List.FindAll(ch => MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); - } - } - - return chList.List; }*/ + + /// + /// Update the contents of ConvexHullList and check if we need to recalculate vertices + /// + private void RefreshConvexHullList(ConvexHullList chList, Vector2 lightPos, Submarine sub) + { + var fullChList = ConvexHull.HullLists.Find(x => x.Submarine == sub); + chList.List = fullChList.List.FindAll(ch => ch.Enabled && MathUtils.CircleIntersectsRectangle(lightPos, range, ch.BoundingBox)); + + NeedsHullCheck = true; + } + + /// + /// Recheck which convex hulls are in range (if needed), + /// and check if we need to recalculate vertices due to changes in the convex hulls + /// + private void CheckHullsInRange() + { + List subs = new List(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); + + //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); + } + } + } + } private List FindRaycastHits() { @@ -247,7 +287,11 @@ namespace Barotrauma.Lights Vector2 drawPos = position; if (ParentSub != null) drawPos += ParentSub.DrawPosition; - var hulls = ConvexHull.GetHullsInRange(position, range, ParentSub); + var hulls = new List();// 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 visibleSegments = new List(); @@ -256,6 +300,11 @@ namespace Barotrauma.Lights { hull.RefreshWorldPositions(); + if (hull.ParentEntity is Item) + { + int gfhdfgh = 1; + } + var visibleHullSegments = hull.GetVisibleSegments(drawPos); visibleSegments.AddRange(visibleHullSegments); @@ -342,8 +391,7 @@ namespace Barotrauma.Lights return closestIntersection == null ? rayEnd : (Vector2)closestIntersection; } - private void CalculateVertices(List encounters, - out VertexPositionTexture[] vertexArray, out short[] indexArray) + private void CalculateLightVertices(List rayCastHits) { List vertices = new List(); @@ -351,22 +399,22 @@ namespace Barotrauma.Lights if (ParentSub != null) drawPos += ParentSub.DrawPosition; // Add a vertex for the center of the mesh - vertices.Add(new VertexPositionTexture(new Vector3(drawPos.X, drawPos.Y, 0), + vertices.Add(new VertexPositionTexture(new Vector3(position.X, position.Y, 0), new Vector2(0.5f, 0.5f))); // Add all the other encounter points as vertices // storing their world position as UV coordinates - foreach (Vector2 vertex in encounters) + foreach (Vector2 vertex in rayCastHits) { Vector2 diff = vertex - drawPos; - vertices.Add(new VertexPositionTexture(new Vector3(vertex.X, vertex.Y, 0), + vertices.Add(new VertexPositionTexture(new Vector3(position.X + diff.X, position.Y + diff.Y, 0), new Vector2(0.5f, 0.5f) + diff / range / 2)); } // Compute the indices to form triangles List indices = new List(); - for (int i = 0; i < encounters.Count - 1; i++) + for (int i = 0; i < rayCastHits.Count - 1; i++) { indices.Add(0); indices.Add((short)((i + 2) % vertices.Count)); @@ -377,12 +425,38 @@ namespace Barotrauma.Lights indices.Add((short)(1)); indices.Add((short)(vertices.Count - 1)); - vertexArray = vertices.ToArray(); - indexArray = indices.ToArray(); + 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.CurrGraphicsDevice, VertexPositionTexture.VertexDeclaration, Math.Max(64, (int)(vertexCount*1.5)), BufferUsage.None); + lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.CurrGraphicsDevice, 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.CurrGraphicsDevice, VertexPositionTexture.VertexDeclaration, (int)(vertexCount*1.5), BufferUsage.None); + lightVolumeIndexBuffer = new DynamicIndexBuffer(GameMain.CurrGraphicsDevice, typeof(short), (int)(indexCount * 1.5), BufferUsage.None); + } + + lightVolumeBuffer.SetData(vertices.ToArray()); + lightVolumeIndexBuffer.SetData(indices.ToArray()); } - public void Draw(SpriteBatch spriteBatch, BasicEffect lightEffect) + 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; @@ -412,32 +486,26 @@ namespace Barotrauma.Lights //LightSprite.Draw(spriteBatch, drawPos, Color, LightSprite.Origin, -Rotation, 1, SpriteEffect); } - var verts = FindRaycastHits(); - - /*for (int i = 0; i < verts.Count; i++) + if (NeedsRecalculation) { - Color[] clrs = new Color[] { Color.Green, Color.Cyan, Color.Red, Color.White, Color.Magenta }; + var verts = FindRaycastHits(); + CalculateLightVertices(verts); - Color clr = clrs[i % clrs.Length]; - - // GUI.DrawString(spriteBatch, new Vector2(verts[i].X, -verts[i].Y), verts[i].ToString(), clr); - GUI.DrawString(spriteBatch, new Vector2(verts[i].X, -verts[i].Y), i.ToString(), clr); - GUI.DrawLine(spriteBatch, drawPos, new Vector2(verts[i].X, -verts[i].Y), clr, 0,3); - }*/ - - // Generate a triangle list from the encounter points - VertexPositionTexture[] vertices; - short[] indices; - CalculateVertices(verts, out vertices, out indices); + lastRecalculationTime = (float)GameMain.Instance.TotalElapsedTime; + NeedsRecalculation = false; + } - if (vertices.Length == 0) return; + if (vertexCount == 0) return; lightEffect.DiffuseColor = (new Vector3(color.R, color.G, color.B) * (color.A / 255.0f)) / 255.0f;// color.ToVector3(); lightEffect.CurrentTechnique.Passes[0].Apply(); - - GameMain.CurrGraphicsDevice.DrawUserIndexedPrimitives + + GameMain.CurrGraphicsDevice.SetVertexBuffer(lightVolumeBuffer); + GameMain.CurrGraphicsDevice.Indices = lightVolumeIndexBuffer; + + GameMain.CurrGraphicsDevice.DrawIndexedPrimitives ( - PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3 + PrimitiveType.TriangleList, 0, 0, indexCount / 3 ); } @@ -463,6 +531,18 @@ namespace Barotrauma.Lights { if (LightSprite != null) LightSprite.Remove(); + if (lightVolumeBuffer != null) + { + lightVolumeBuffer.Dispose(); + lightVolumeBuffer = null; + } + + if (lightVolumeIndexBuffer != null) + { + lightVolumeIndexBuffer.Dispose(); + lightVolumeIndexBuffer = null; + } + GameMain.LightManager.RemoveLight(this); } }