From 39c9c782661f4c8b89d2517d945058176aa111ad Mon Sep 17 00:00:00 2001 From: Regalis Date: Sat, 2 Apr 2016 16:03:41 +0300 Subject: [PATCH] - level generation refactoring: moved most of the generation logic to a static GaveGenerator class + some minor cleanup - WIP method for splitting the voronoi cells to smaller ones and carving a small cave through them --- Subsurface/Barotrauma.csproj | 1 + Subsurface/Source/Map/Levels/CaveGenerator.cs | 554 ++++++++++++++ Subsurface/Source/Map/Levels/Level.cs | 676 ++++-------------- Subsurface/Source/Map/Levels/LevelRenderer.cs | 30 + Subsurface/Source/Map/Levels/Voronoi.cs | 12 + .../Source/Map/Levels/VoronoiElements.cs | 17 + Subsurface/Source/Map/Levels/WrappingWall.cs | 12 +- 7 files changed, 745 insertions(+), 557 deletions(-) create mode 100644 Subsurface/Source/Map/Levels/CaveGenerator.cs diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj index 1cb29cd22..a7f2cdc70 100644 --- a/Subsurface/Barotrauma.csproj +++ b/Subsurface/Barotrauma.csproj @@ -127,6 +127,7 @@ + diff --git a/Subsurface/Source/Map/Levels/CaveGenerator.cs b/Subsurface/Source/Map/Levels/CaveGenerator.cs new file mode 100644 index 000000000..ee789b836 --- /dev/null +++ b/Subsurface/Source/Map/Levels/CaveGenerator.cs @@ -0,0 +1,554 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Microsoft.Xna.Framework; +using Voronoi2; +using Microsoft.Xna.Framework.Graphics; +using FarseerPhysics; +using FarseerPhysics.Common; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Factories; + +namespace Barotrauma +{ + static class CaveGenerator + { + public static List CarveCave(List cells, Vector2 startPoint, out List newCells) + { + Voronoi voronoi = new Voronoi(1.0); + + List sites = new List(); + + float sideInterval = 400.0f; + + Vector4 edges = new Vector4( + cells.Min(x => x.edges.Min(e => e.point1.X)), + cells.Min(x => x.edges.Min(e => e.point1.Y)), + cells.Max(x => x.edges.Max(e => e.point1.X)), + cells.Max(x => x.edges.Max(e => e.point1.Y))); + + edges.X -= sideInterval * 2; + edges.Y -= sideInterval * 2; + edges.Z += sideInterval * 2; + edges.W += sideInterval * 2; + + Rectangle borders = new Rectangle((int)edges.X, (int)edges.Y, (int)(edges.Z - edges.X), (int)(edges.W - edges.Y)); + + for (float x = edges.X + sideInterval; x < edges.Z - sideInterval; x += sideInterval) + { + for (float y = edges.Y + sideInterval; y < edges.W - sideInterval; y += sideInterval) + { + sites.Add(new Vector2(x, y) + Rand.Vector(sideInterval*0.45f, false)); + } + } + + List graphEdges = voronoi.MakeVoronoiGraph(sites, edges.X, edges.Y, edges.Z, edges.W); + + List[,] cellGrid; + newCells = GraphEdgesToCells(graphEdges, borders, 1000, out cellGrid); + + //remove cells that aren't inside any of the original "base cells" + foreach (VoronoiCell cell in newCells) + { + if (cell.edges.Any(e => + e.point1.X == edges.X || e.point1.X == edges.Z || + e.point1.Y == edges.Z || e.point1.Y == edges.W)) + { + cell.CellType = CellType.Removed; + continue; + } + + if (cells.Any(c => c.IsPointInside(cell.Center))) continue; + foreach (GraphEdge edge in cell.edges) + { + var adjacent = edge.AdjacentCell(cell); + if (adjacent != null && adjacent.CellType != CellType.Removed) adjacent.CellType = CellType.Edge; + } + + cell.CellType = CellType.Removed; + } + + newCells.RemoveAll(newCell => newCell.CellType == CellType.Removed); + + VoronoiCell startCell = null; + float closestDist = 0.0f; + foreach (VoronoiCell cell in newCells) + { + float dist = Vector2.Distance(startPoint, cell.Center); + if (dist < closestDist || startCell == null) + { + startCell = cell; + closestDist = dist; + } + } + + startCell.CellType = CellType.Path; + + List path = new List() {startCell}; + //VoronoiCell pathCell = startCell; + //for (int i = 0; i < newCells.Count / 2; i++) + //{ + + // var allowdNextCells = new List(); + // foreach (GraphEdge edge in pathCell.edges) + // { + // var adjacent = edge.AdjacentCell(pathCell); + // if (adjacent == null || + // adjacent.CellType == CellType.Path || + // adjacent.CellType == CellType.Removed || + // adjacent.CellType == CellType.Edge) continue; + + // allowdNextCells.Add(adjacent); + // } + + // if (allowdNextCells.Count == 0) break; + + // pathCell = allowdNextCells[Rand.Int(allowdNextCells.Count, false)]; + // path.Add(pathCell); + //} + + return path; + } + + public static List GraphEdgesToCells(List graphEdges, Rectangle borders, float gridCellSize, out List[,] cellGrid) + { + List cells = new List(); + + cellGrid = new List[(int)Math.Ceiling(borders.Width / gridCellSize), (int)Math.Ceiling(borders.Height / gridCellSize)]; + for (int x = 0; x < borders.Width / gridCellSize; x++) + { + for (int y = 0; y < borders.Height / gridCellSize; y++) + { + cellGrid[x, y] = new List(); + } + } + + foreach (GraphEdge ge in graphEdges) + { + for (int i = 0; i < 2; i++) + { + Site site = (i == 0) ? ge.site1 : ge.site2; + + VoronoiCell cell = cellGrid[ + (int)Math.Floor((site.coord.x-borders.X) / gridCellSize), + (int)Math.Floor((site.coord.y-borders.Y) / gridCellSize)].Find(c => c.site == site); + + if (cell == null) + { + cell = new VoronoiCell(site); + cellGrid[ + (int)Math.Floor((cell.Center.X-borders.X) / gridCellSize), + (int)Math.Floor((cell.Center.Y - borders.Y) / gridCellSize)].Add(cell); + cells.Add(cell); + } + + if (ge.cell1 == null) + { + ge.cell1 = cell; + } + else + { + ge.cell2 = cell; + } + cell.edges.Add(ge); + } + } + + return cells; + } + + + private static Vector2 GetEdgeNormal(GraphEdge edge, VoronoiCell cell = null) + { + if (cell == null) cell = edge.AdjacentCell(null); + if (cell == null) return Vector2.UnitX; + + CompareCCW compare = new CompareCCW(cell.Center); + if (compare.Compare(edge.point1, edge.point2) == -1) + { + var temp = edge.point1; + edge.point1 = edge.point2; + edge.point2 = temp; + } + + Vector2 normal = Vector2.Zero; + + normal = Vector2.Normalize(edge.point2 - edge.point1); + Vector2 diffToCell = Vector2.Normalize(cell.Center - edge.point2); + + normal = new Vector2(-normal.Y, normal.X); + + if (Vector2.Dot(normal, diffToCell) < 0) + { + normal = -normal; + } + + return normal; + } + + public static List GeneratePath( + List pathNodes, List cells, List[,] cellGrid, + int gridCellSize, Rectangle limits, float wanderAmount = 0.3f, bool mirror = false, Vector2? gridOffset = null) + { + var targetCells = new List(); + for (int i = 0; i < pathNodes.Count; i++) + { + int cellIndex = FindCellIndex(pathNodes[i], cells, cellGrid, gridCellSize, 2, gridOffset); + targetCells.Add(cells[cellIndex]); + } + + return GeneratePath(targetCells, cells, cellGrid, gridCellSize, limits, wanderAmount, mirror); + } + + + public static List GeneratePath( + List targetCells, List cells, List[,] cellGrid, + int gridCellSize, Rectangle limits, float wanderAmount = 0.3f, bool mirror = false) + { + Stopwatch sw2 = new Stopwatch(); + sw2.Start(); + + //how heavily the path "steers" towards the endpoint + //lower values will cause the path to "wander" more, higher will make it head straight to the end + wanderAmount = MathHelper.Clamp(wanderAmount, 0.0f, 1.0f); + + List allowedEdges = new List(); + List pathCells = new List(); + + VoronoiCell currentCell = targetCells[0]; + currentCell.CellType = CellType.Path; + pathCells.Add(currentCell); + + int currentTargetIndex = 1; + + do + { + int edgeIndex = 0; + + allowedEdges.Clear(); + foreach (GraphEdge edge in currentCell.edges) + { + if (!limits.Contains(edge.AdjacentCell(currentCell).Center)) continue; + + allowedEdges.Add(edge); + } + + //steer towards target + if (Rand.Range(0.0f, 1.0f, false) > wanderAmount || allowedEdges.Count == 0) + { + for (int i = 0; i < currentCell.edges.Count; i++) + { + if (!MathUtils.LinesIntersect(currentCell.Center, targetCells[currentTargetIndex].Center, + currentCell.edges[i].point1, currentCell.edges[i].point2)) continue; + edgeIndex = i; + break; + } + } + //choose random edge (ignoring ones where the adjacent cell is outside limits) + else + { + + + //if (allowedEdges.Count==0) + //{ + // edgeIndex = Rand.Int(currentCell.edges.Count, false); + //} + //else + //{ + edgeIndex = Rand.Int(allowedEdges.Count, false); + if (mirror && edgeIndex > 0) edgeIndex = allowedEdges.Count - edgeIndex; + edgeIndex = currentCell.edges.IndexOf(allowedEdges[edgeIndex]); + //} + } + + currentCell = currentCell.edges[edgeIndex].AdjacentCell(currentCell); + currentCell.CellType = CellType.Path; + pathCells.Add(currentCell); + + if (currentCell == targetCells[currentTargetIndex]) + { + currentTargetIndex += 1; + if (currentTargetIndex >= targetCells.Count) break; + } + + } while (currentCell != targetCells[targetCells.Count - 1]); + + + Debug.WriteLine("gettooclose: " + sw2.ElapsedMilliseconds + " ms"); + sw2.Restart(); + + return pathCells; + } + + public static List GeneratePolygons(List cells, out List verticeList, bool setSolid=true) + { + verticeList = new List(); + var bodies = new List(); + + List tempVertices = new List(); + List bodyPoints = new List(); + + for (int n = cells.Count - 1; n >= 0; n-- ) + { + VoronoiCell cell = cells[n]; + + bodyPoints.Clear(); + tempVertices.Clear(); + foreach (GraphEdge ge in cell.edges) + { + if (Math.Abs(Vector2.Distance(ge.point1, ge.point2))<0.1f) continue; + if (!tempVertices.Contains(ge.point1)) tempVertices.Add(ge.point1); + if (!tempVertices.Contains(ge.point2)) tempVertices.Add(ge.point2); + + VoronoiCell adjacentCell = ge.AdjacentCell(cell); + //if (adjacentCell!=null && cells.Contains(adjacentCell)) continue; + + if (setSolid) ge.isSolid = (adjacentCell == null || !cells.Contains(adjacentCell)); + + if (!bodyPoints.Contains(ge.point1)) bodyPoints.Add(ge.point1); + if (!bodyPoints.Contains(ge.point2)) bodyPoints.Add(ge.point2); + } + + if (tempVertices.Count < 3 || bodyPoints.Count < 2) + { + cells.RemoveAt(n); + continue; + } + + var triangles = MathUtils.TriangulateConvexHull(tempVertices, cell.Center); + for (int i = 0; i < triangles.Count; i++) + { + foreach (Vector2 vertex in triangles[i]) + { + verticeList.Add(new VertexPositionColor(new Vector3(vertex, 0.0f), Color.Black)); + } + } + + if (bodyPoints.Count < 2) continue; + + if (bodyPoints.Count < 3) + { + foreach (Vector2 vertex in tempVertices) + { + if (bodyPoints.Contains(vertex)) continue; + bodyPoints.Add(vertex); + break; + } + } + + for (int i = 0; i < bodyPoints.Count; i++) + { + cell.bodyVertices.Add(bodyPoints[i]); + bodyPoints[i] = ConvertUnits.ToSimUnits(bodyPoints[i]); + } + + triangles = MathUtils.TriangulateConvexHull(bodyPoints, cell.Center); + + Body edgeBody = new Body(GameMain.World); + + for (int i = 0; i < triangles.Count; i++) + { + if (triangles[i][0].Y == triangles[i][1].Y && triangles[i][0].Y == triangles[i][2].Y) continue; + if (triangles[i][0].X == triangles[i][1].X && triangles[i][0].X == triangles[i][2].X) continue; + + if (Vector2.DistanceSquared(triangles[i][0], triangles[i][1]) < 0.1f) continue; + if (Vector2.DistanceSquared(triangles[i][1], triangles[i][2]) < 0.1f) continue; + + Vertices bodyVertices = new Vertices(triangles[i]); + FixtureFactory.AttachPolygon(bodyVertices, 5.0f, edgeBody); + } + + edgeBody.UserData = cell; + edgeBody.SleepingAllowed = false; + edgeBody.BodyType = BodyType.Kinematic; + edgeBody.CollisionCategories = Physics.CollisionLevel; + + cell.body = edgeBody; + bodies.Add(edgeBody); + } + + return bodies; + } + + public static VertexPositionTexture[] GenerateWallShapes(List cells) + { + float inwardThickness = 500.0f, outWardThickness = 30.0f; + + List verticeList = new List(); + + foreach (VoronoiCell cell in cells) + { + if (cell.body == null) continue; + foreach (GraphEdge edge in cell.edges) + { + if (edge.cell1 != null && edge.cell1.body == null) edge.cell1 = null; + if (edge.cell2 != null && edge.cell2.body == null) edge.cell2 = null; + + CompareCCW compare = new CompareCCW(cell.Center); + if (compare.Compare(edge.point1, edge.point2) == -1) + { + var temp = edge.point1; + edge.point1 = edge.point2; + edge.point2 = temp; + } + } + } + + foreach (VoronoiCell cell in cells) + { + if (cell.body == null) continue; + foreach (GraphEdge edge in cell.edges) + { + if (!edge.isSolid) continue; + + GraphEdge leftEdge = null, rightEdge = null; + + foreach (GraphEdge edge2 in cell.edges) + { + if (edge == edge2) continue; + if (edge.point1 == edge2.point1 || + edge.point1 == edge2.point2) + { + leftEdge = edge2; + } + else if (edge.point2 == edge2.point2 || edge.point2 == edge2.point1) + { + rightEdge = edge2; + } + } + + Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero; + + if (leftEdge == null) + { + leftNormal = GetEdgeNormal(edge, cell); + } + else + { + leftNormal = (leftEdge.isSolid) ? + Vector2.Normalize(GetEdgeNormal(leftEdge) + GetEdgeNormal(edge, cell)) : + Vector2.Normalize(leftEdge.Center - edge.point1); + } + + + if (!MathUtils.IsValid(leftNormal)) + { +#if DEBUG + DebugConsole.ThrowError("Invalid right normal"); +#endif + GameMain.World.RemoveBody(cell.body); + cell.body = null; + leftNormal = Vector2.UnitX; + break; + } + + + if (rightEdge == null) + { + rightNormal = GetEdgeNormal(edge, cell); + } + else + { + rightNormal = (rightEdge.isSolid) ? + Vector2.Normalize(GetEdgeNormal(rightEdge) + GetEdgeNormal(edge, cell)) : + Vector2.Normalize(rightEdge.Center - edge.point2); + } + + if (!MathUtils.IsValid(rightNormal)) + { +#if DEBUG + DebugConsole.ThrowError("Invalid right normal"); +#endif + GameMain.World.RemoveBody(cell.body); + cell.body = null; + rightNormal = Vector2.UnitX; + break; + } + + + + + for (int i = 0; i < 2; i++) + { + Vector2[] verts = new Vector2[3]; + VertexPositionTexture[] vertPos = new VertexPositionTexture[3]; + + + if (i == 0) + { + verts[0] = edge.point1 - leftNormal * outWardThickness; + verts[1] = edge.point2 - rightNormal * outWardThickness; + verts[2] = edge.point1 + leftNormal * inwardThickness; + + vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), Vector2.Zero); + vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX); + vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(0, 0.5f)); + } + else + { + verts[0] = edge.point1 + leftNormal * inwardThickness; + verts[1] = edge.point2 - rightNormal * outWardThickness; + verts[2] = edge.point2 + rightNormal * inwardThickness; + + vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(0.0f, 0.5f)); + vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX); + vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(1.0f, 0.5f)); + } + + var comparer = new CompareCCW((verts[0] + verts[1] + verts[2]) / 3.0f); + Array.Sort(verts, vertPos, comparer); + + for (int j = 0; j < 3; j++) + { + verticeList.Add(vertPos[j]); + } + } + } + } + + return verticeList.ToArray(); + } + + /// + /// find the index of the cell which the point is inside + /// (actually finds the cell whose center is closest, but it's always the correct cell assuming the point is inside the borders of the diagram) + /// + public static int FindCellIndex(Vector2 position,List cells, List[,] cellGrid, int gridCellSize, int searchDepth = 1, Vector2? offset = null) + { + float closestDist = 0.0f; + VoronoiCell closestCell = null; + + Vector2 gridOffset = offset == null ? Vector2.Zero : (Vector2)offset; + position -= gridOffset; + + int gridPosX = (int)Math.Floor(position.X / gridCellSize); + int gridPosY = (int)Math.Floor(position.Y / gridCellSize); + + for (int x = Math.Max(gridPosX - searchDepth, 0); x <= Math.Min(gridPosX + searchDepth, cellGrid.GetLength(0) - 1); x++) + { + for (int y = Math.Max(gridPosY - searchDepth, 0); y <= Math.Min(gridPosY + searchDepth, cellGrid.GetLength(1) - 1); y++) + { + for (int i = 0; i < cellGrid[x, y].Count; i++) + { + float dist = Vector2.Distance(cellGrid[x, y][i].Center, position); + if (closestDist != 0.0f && dist > closestDist) continue; + + closestDist = dist; + closestCell = cellGrid[x, y][i]; + } + } + } + + if (cells.IndexOf(closestCell) == -1) + { + int asdf = 1; + } + + return cells.IndexOf(closestCell); + } + + + } +} diff --git a/Subsurface/Source/Map/Levels/Level.cs b/Subsurface/Source/Map/Levels/Level.cs index 69a73c480..c5e99c239 100644 --- a/Subsurface/Source/Map/Levels/Level.cs +++ b/Subsurface/Source/Map/Levels/Level.cs @@ -44,7 +44,7 @@ namespace Barotrauma private int siteInterval; - public const int GridCellWidth = 2000; + public const int GridCellSize = 2000; private List[,] cellGrid; private WrappingWall[,] wrappingWalls; @@ -245,58 +245,17 @@ namespace Barotrauma Debug.WriteLine("MakeVoronoiGraph: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); - - cellGrid = new List[borders.Width / GridCellWidth, borders.Height / GridCellWidth]; - for (int x = 0; x < borders.Width / GridCellWidth; x++) - { - for (int y = 0; y < borders.Height / GridCellWidth; y++) - { - cellGrid[x, y] = new List(); - } - } - + //construct voronoi cells based on the graph edges - cells = new List(); - foreach (GraphEdge ge in graphEdges) - { - for (int i = 0; i < 2; i++) - { - Site site = (i == 0) ? ge.site1 : ge.site2; - - VoronoiCell cell = cellGrid[ - (int)Math.Floor(site.coord.x / GridCellWidth), - (int)Math.Floor(site.coord.y / GridCellWidth)].Find(c => c.site == site); - - if (cell == null) - { - cell = new VoronoiCell(site); - cellGrid[(int)Math.Floor(cell.Center.X / GridCellWidth), (int)Math.Floor(cell.Center.Y / GridCellWidth)].Add(cell); - cells.Add(cell); - } - - if (ge.cell1 == null) - { - ge.cell1 = cell; - } - else - { - ge.cell2 = cell; - } - cell.edges.Add(ge); - } - } + cells = CaveGenerator.GraphEdgesToCells(graphEdges, borders, GridCellSize, out cellGrid); Debug.WriteLine("find cells: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); + + List pathCells = CaveGenerator.GeneratePath(pathNodes, cells, cellGrid, GridCellSize, + new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), 0.3f, mirror); - - //if (mirror) - //{ - // pathNodes.Reverse(); - //} - - List pathCells = GeneratePath(pathNodes, cells, - new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), minWidth, 0.3f, mirror, true); + EnlargeMainPath(pathCells, minWidth); foreach (InterestingPosition positionOfInterest in positionsOfInterest) { @@ -305,20 +264,19 @@ namespace Barotrauma } startPosition.X = pathCells[0].Center.X; - //endPosition = pathCells[pathCells.Count - 1].Center; foreach (List tunnel in smallTunnels) { if (tunnel.Count<2) continue; //find the cell which the path starts from - int startCellIndex = FindCellIndex(tunnel[0]); + int startCellIndex = CaveGenerator.FindCellIndex(tunnel[0], cells, cellGrid, GridCellSize, 1); if (startCellIndex < 0) continue; //if it wasn't one of the cells in the main path, don't create a tunnel - if (!pathCells.Contains(cells[startCellIndex])) continue; + if (cells[startCellIndex].CellType != CellType.Path) continue; - var newPathCells = GeneratePath(tunnel, cells, pathBorders, 0.0f, 0.0f); + var newPathCells = CaveGenerator.GeneratePath(tunnel, cells, cellGrid, GridCellSize, pathBorders); positionsOfInterest.Add(new InterestingPosition(tunnel.Last(), false)); @@ -339,9 +297,13 @@ namespace Barotrauma // GeneratePath(rand, tunnelStart, new Vector2(tunnelStart.X, borders.Height), cells, pathBorders, minWidth, 0.1f, mirror) // ); //} + + cells = CleanCells(pathCells); + + pathCells.AddRange(CreateBottomHoles(Rand.Range(0.2f,0.8f, false), new Rectangle( (int)(borders.Width * 0.2f), 0, (int)(borders.Width * 0.6f), (int)(borders.Height * 0.3f)))); @@ -351,6 +313,45 @@ namespace Barotrauma cells.Remove(cell); } + //for (int i = 0; i < 3; i++) + //{ + // Vector2 startPoint = Vector2.Zero; + // VoronoiCell startCell = null; + + // while (true) + // { + // startCell = cells[Rand.Int(cells.Count, false)]; + + // GraphEdge startEdge = + // startCell.edges.Find(e => pathCells.Contains(e.AdjacentCell(startCell))); + + // if (startEdge != null) + // { + // startPoint = (startEdge.point1 + startEdge.point2) / 2.0f; + // break; + // } + // } + + // var caveCells = GetCells(startCell.Center, 1); + + // List caveSolidCells; + + // var cavePathCells = CaveGenerator.CarveCave(caveCells, startPoint, out caveSolidCells); + + // caveCells.ForEach(c => cells.Remove(c)); + + // //caveSolidCells = CleanCells(cavePathCells); + + // cells.AddRange(caveSolidCells); + + // foreach (VoronoiCell cell in cavePathCells) + // { + // cells.Remove(cell); + // } + + // pathCells.AddRange(cavePathCells); + //} + for (int x = 0; x < cellGrid.GetLength(0); x++) { for (int y = 0; y < cellGrid.GetLength(1); y++) @@ -361,14 +362,19 @@ namespace Barotrauma foreach (VoronoiCell cell in cells) { - cellGrid[(int)Math.Floor(cell.Center.X / GridCellWidth), (int)Math.Floor(cell.Center.Y / GridCellWidth)].Add(cell); + cellGrid[(int)Math.Floor(cell.Center.X / GridCellSize), (int)Math.Floor(cell.Center.Y / GridCellSize)].Add(cell); } + + startPosition.Y = borders.Height; endPosition.Y = borders.Height; - renderer.SetBodyVertices(GeneratePolygons(cells, pathCells)); - renderer.SetWallVertices(GenerateWallShapes(cells)); + List bodyVertices; + bodies = CaveGenerator.GeneratePolygons(cells, out bodyVertices); + + renderer.SetBodyVertices(bodyVertices.ToArray()); + renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells)); wrappingWalls = new WrappingWall[2, 2]; @@ -380,13 +386,11 @@ namespace Barotrauma wrappingWalls[side, i] = new WrappingWall(pathCells, cells, borders.Height * 0.5f, (side == 0 ? -1 : 1) * (i == 0 ? 1 : 2)); - wrappingWalls[side, i].SetBodyVertices(GeneratePolygons(wrappingWalls[side, i].Cells, new List(), false)); - wrappingWalls[side, i].SetWallVertices(GenerateWallShapes(wrappingWalls[side, i].Cells)); - //wrappingWalls[side, i].Cells[0].edges[1].isSolid = false; - //wrappingWalls[side, i].Cells[0].edges[3].isSolid = false; + List wrappingWallVertices; + CaveGenerator.GeneratePolygons(wrappingWalls[side, i].Cells, out wrappingWallVertices, false); - //wrappingWalls[side, i].Cells[wrappingWalls[side, i].Cells.Count-1].edges[1].isSolid = false; - //wrappingWalls[side, i].Cells[wrappingWalls[side, i].Cells.Count - 1].edges[3].isSolid = false; + wrappingWalls[side, i].SetBodyVertices(wrappingWallVertices.ToArray()); + wrappingWalls[side, i].SetWallVertices(CaveGenerator.GenerateWallShapes(wrappingWalls[side, i].Cells)); } } @@ -446,132 +450,6 @@ namespace Barotrauma Debug.WriteLine("**********************************************************************************"); } - private List GeneratePath(List points, List cells, Microsoft.Xna.Framework.Rectangle limits, float minWidth, float wanderAmount = 0.3f, bool mirror=false, bool placeWaypoints=false) - { - Stopwatch sw2 = new Stopwatch(); - sw2.Start(); - - //how heavily the path "steers" towards the endpoint - //lower values will cause the path to "wander" more, higher will make it head straight to the end - wanderAmount = MathHelper.Clamp(wanderAmount, 0.0f, 1.0f); - - List allowedEdges = new List(); - List pathCells = new List(); - - VoronoiCell[] targetCells = new VoronoiCell[points.Count]; - for (int i = 0; i wanderAmount || allowedEdges.Count == 0) - { - for (int i = 0; i < currentCell.edges.Count; i++) - { - if (!MathUtils.LinesIntersect(currentCell.Center, targetCells[currentTargetIndex].Center, - currentCell.edges[i].point1, currentCell.edges[i].point2)) continue; - edgeIndex = i; - break; - } - } - //choose random edge (ignoring ones where the adjacent cell is outside limits) - else - { - - - //if (allowedEdges.Count==0) - //{ - // edgeIndex = Rand.Int(currentCell.edges.Count, false); - //} - //else - //{ - edgeIndex = Rand.Int(allowedEdges.Count, false); - if (mirror && edgeIndex > 0) edgeIndex = allowedEdges.Count - edgeIndex; - edgeIndex = currentCell.edges.IndexOf(allowedEdges[edgeIndex]); - //} - } - - currentCell = currentCell.edges[edgeIndex].AdjacentCell(currentCell); - pathCells.Add(currentCell); - - if (currentCell==targetCells[currentTargetIndex]) - { - currentTargetIndex += 1; - if (currentTargetIndex>=targetCells.Length) break; - } - - } while (currentCell != targetCells[targetCells.Length-1]); - - if (placeWaypoints) - { - WayPoint newWaypoint = new WayPoint(new Rectangle((int)pathCells[0].Center.X, (int)(borders.Height + shaftHeight), 10, 10), null); - newWaypoint.MoveWithLevel = true; - - WayPoint prevWaypoint = newWaypoint; - - for (int i = 0; i < pathCells.Count; i++) - { - //clean "loops" from the path - for (int n = 0; n < i; n++) - { - if (pathCells[n] != pathCells[i]) continue; - - pathCells.RemoveRange(n+1, i-n); - break; - } - if (i >= pathCells.Count) break; - - newWaypoint = new WayPoint(new Rectangle((int)pathCells[i].Center.X, (int)pathCells[i].Center.Y, 10, 10), null); - newWaypoint.MoveWithLevel = true; - if (prevWaypoint != null) - { - prevWaypoint.linkedTo.Add(newWaypoint); - newWaypoint.linkedTo.Add(prevWaypoint); - } - prevWaypoint = newWaypoint; - } - - newWaypoint = new WayPoint(new Rectangle((int)pathCells[pathCells.Count - 1].Center.X, (int)(borders.Height + shaftHeight), 10, 10), null); - newWaypoint.MoveWithLevel = true; - - prevWaypoint.linkedTo.Add(newWaypoint); - newWaypoint.linkedTo.Add(prevWaypoint); - - } - - Debug.WriteLine("genpath: " + sw2.ElapsedMilliseconds + " ms"); - sw2.Restart(); - - List removedCells = GetTooCloseCells(pathCells, minWidth); - foreach (VoronoiCell removedCell in removedCells) - { - if (pathCells.Contains(removedCell)) continue; - pathCells.Add(removedCell); - } - - Debug.WriteLine("gettooclose: " + sw2.ElapsedMilliseconds + " ms"); - sw2.Restart(); - - return pathCells; - } private List CreateBottomHoles(float holeProbability, Rectangle limits) { @@ -593,6 +471,55 @@ namespace Barotrauma //} } + private void EnlargeMainPath(List pathCells, float minWidth) + { + + WayPoint newWaypoint = new WayPoint(new Rectangle((int)pathCells[0].Center.X, (int)(borders.Height + shaftHeight), 10, 10), null); + newWaypoint.MoveWithLevel = true; + + WayPoint prevWaypoint = newWaypoint; + + for (int i = 0; i < pathCells.Count; i++) + { + //clean "loops" from the path + for (int n = 0; n < i; n++) + { + if (pathCells[n] != pathCells[i]) continue; + + pathCells.RemoveRange(n + 1, i - n); + break; + } + if (i >= pathCells.Count) break; + + newWaypoint = new WayPoint(new Rectangle((int)pathCells[i].Center.X, (int)pathCells[i].Center.Y, 10, 10), null); + newWaypoint.MoveWithLevel = true; + if (prevWaypoint != null) + { + prevWaypoint.linkedTo.Add(newWaypoint); + newWaypoint.linkedTo.Add(prevWaypoint); + } + prevWaypoint = newWaypoint; + } + + newWaypoint = new WayPoint(new Rectangle((int)pathCells[pathCells.Count - 1].Center.X, (int)(borders.Height + shaftHeight), 10, 10), null); + newWaypoint.MoveWithLevel = true; + + prevWaypoint.linkedTo.Add(newWaypoint); + newWaypoint.linkedTo.Add(prevWaypoint); + + if (minWidth > 0.0f) + { + List removedCells = GetTooCloseCells(pathCells, minWidth); + foreach (VoronoiCell removedCell in removedCells) + { + if (removedCell.CellType == CellType.Path) continue; + + pathCells.Add(removedCell); + removedCell.CellType = CellType.Path; + } + } + } + private List GetTooCloseCells(List emptyCells, float minDistance) { List tooCloseCells = new List(); @@ -634,7 +561,7 @@ namespace Barotrauma { Vector2 cornerPos = position + new Vector2(x,y); - int cellIndex = FindCellIndex(cornerPos); + int cellIndex = CaveGenerator.FindCellIndex(cornerPos, cells, cellGrid, GridCellSize); if (cellIndex == -1) continue; if (!tooCloseCells.Contains(cells[cellIndex])) { @@ -672,294 +599,6 @@ namespace Barotrauma return newCells; } - /// - /// find the index of the cell which the point is inside - /// (actually finds the cell whose center is closest, but it's always the correct cell assuming the point is inside the borders of the diagram) - /// - private int FindCellIndex(Vector2 position, int searchDepth = 1) - { - float closestDist = 0.0f; - VoronoiCell closestCell = null; - - int gridPosX = (int)Math.Floor(position.X / GridCellWidth); - int gridPosY = (int)Math.Floor(position.Y / GridCellWidth); - - for (int x = Math.Max(gridPosX - searchDepth, 0); x <= Math.Min(gridPosX + searchDepth, cellGrid.GetLength(0) - 1); x++) - { - for (int y = Math.Max(gridPosY - searchDepth, 0); y <= Math.Min(gridPosY + searchDepth, cellGrid.GetLength(1) - 1); y++) - { - for (int i = 0; i < cellGrid[x, y].Count; i++) - { - float dist = Vector2.Distance(cellGrid[x, y][i].Center, position); - if (closestDist != 0.0f && dist > closestDist) continue; - - closestDist = dist; - closestCell = cellGrid[x, y][i]; - } - } - } - - return cells.IndexOf(closestCell); - } - - - private VertexPositionColor[] GeneratePolygons(List cells, List emptyCells, bool setSolid=true) - { - List verticeList = new List(); - //bodies = new List(); - - List tempVertices = new List(); - List bodyPoints = new List(); - - for (int n = cells.Count - 1; n >= 0; n-- ) - { - VoronoiCell cell = cells[n]; - - bodyPoints.Clear(); - tempVertices.Clear(); - foreach (GraphEdge ge in cell.edges) - { - if (Math.Abs(Vector2.Distance(ge.point1, ge.point2))<0.1f) continue; - if (!tempVertices.Contains(ge.point1)) tempVertices.Add(ge.point1); - if (!tempVertices.Contains(ge.point2)) tempVertices.Add(ge.point2); - - VoronoiCell adjacentCell = ge.AdjacentCell(cell); - if (adjacentCell!=null && cells.Contains(adjacentCell)) continue; - - if (setSolid) ge.isSolid = true; - - if (!bodyPoints.Contains(ge.point1)) bodyPoints.Add(ge.point1); - if (!bodyPoints.Contains(ge.point2)) bodyPoints.Add(ge.point2); - } - - if (tempVertices.Count < 3 || bodyPoints.Count < 2) - { - cells.RemoveAt(n); - continue; - } - - var triangles = MathUtils.TriangulateConvexHull(tempVertices, cell.Center); - for (int i = 0; i < triangles.Count; i++) - { - foreach (Vector2 vertex in triangles[i]) - { - verticeList.Add(new VertexPositionColor(new Vector3(vertex, 0.0f), Color.Black)); - } - } - - if (bodyPoints.Count < 2) continue; - - if (bodyPoints.Count < 3) - { - foreach (Vector2 vertex in tempVertices) - { - if (bodyPoints.Contains(vertex)) continue; - bodyPoints.Add(vertex); - break; - } - } - - for (int i = 0; i < bodyPoints.Count; i++) - { - cell.bodyVertices.Add(bodyPoints[i]); - bodyPoints[i] = ConvertUnits.ToSimUnits(bodyPoints[i]); - } - - triangles = MathUtils.TriangulateConvexHull(bodyPoints, cell.Center); - - Body edgeBody = new Body(GameMain.World); - - for (int i = 0; i < triangles.Count; i++) - { - if (triangles[i][0].Y == triangles[i][1].Y && triangles[i][0].Y == triangles[i][2].Y) continue; - if (triangles[i][0].X == triangles[i][1].X && triangles[i][0].X == triangles[i][2].X) continue; - - if (Vector2.DistanceSquared(triangles[i][0], triangles[i][1]) < 0.1f) continue; - if (Vector2.DistanceSquared(triangles[i][1], triangles[i][2]) < 0.1f) continue; - - Vertices bodyVertices = new Vertices(triangles[i]); - FixtureFactory.AttachPolygon(bodyVertices, 5.0f, edgeBody); - } - - edgeBody.UserData = cell; - edgeBody.SleepingAllowed = false; - edgeBody.BodyType = BodyType.Kinematic; - edgeBody.CollisionCategories = Physics.CollisionLevel; - - cell.body = edgeBody; - bodies.Add(edgeBody); - } - - return verticeList.ToArray(); - } - - private VertexPositionTexture[] GenerateWallShapes(List cells) - { - float inwardThickness = 500.0f, outWardThickness = 30.0f; - - List verticeList = new List(); - - foreach (VoronoiCell cell in cells) - { - if (cell.body == null) continue; - foreach (GraphEdge edge in cell.edges) - { - if (edge.cell1 != null && edge.cell1.body == null) edge.cell1 = null; - if (edge.cell2 != null && edge.cell2.body == null) edge.cell2 = null; - - CompareCCW compare = new CompareCCW(cell.Center); - if (compare.Compare(edge.point1, edge.point2) == -1) - { - var temp = edge.point1; - edge.point1 = edge.point2; - edge.point2 = temp; - } - } - } - - foreach (VoronoiCell cell in cells) - { - if (cell.body == null) continue; - foreach (GraphEdge edge in cell.edges) - { - if (!edge.isSolid) continue; - - GraphEdge leftEdge = null, rightEdge = null; - - foreach (GraphEdge edge2 in cell.edges) - { - if (edge == edge2) continue; - if (edge.point1 == edge2.point1 || - edge.point1 == edge2.point2) - { - leftEdge = edge2; - } - else if(edge.point2 == edge2.point2 || edge.point2 == edge2.point1) - { - rightEdge = edge2; - } - } - - Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero; - - if (leftEdge == null) - { - leftNormal = GetEdgeNormal(edge, cell); - } - else - { - leftNormal = (leftEdge.isSolid) ? - Vector2.Normalize(GetEdgeNormal(leftEdge) + GetEdgeNormal(edge, cell)) : - Vector2.Normalize(leftEdge.Center - edge.point1); - } - - - if (!leftNormal.IsValid()) - { -#if DEBUG - DebugConsole.ThrowError("Invalid right normal"); -#endif - GameMain.World.RemoveBody(cell.body); - cell.body = null; - leftNormal = Vector2.UnitX; - break; - } - - - if (rightEdge == null) - { - rightNormal = GetEdgeNormal(edge, cell); - } - else - { - rightNormal = (rightEdge.isSolid) ? - Vector2.Normalize(GetEdgeNormal(rightEdge) + GetEdgeNormal(edge, cell)) : - Vector2.Normalize(rightEdge.Center - edge.point2); - } - - if (!rightNormal.IsValid()) - { -#if DEBUG - DebugConsole.ThrowError("Invalid right normal"); -#endif - GameMain.World.RemoveBody(cell.body); - cell.body = null; - rightNormal = Vector2.UnitX; - break; - } - - - - - for (int i = 0; i < 2; i++) - { - Vector2[] verts = new Vector2[3]; - VertexPositionTexture[] vertPos = new VertexPositionTexture[3]; - - - if (i==0) - { - verts[0] = edge.point1 - leftNormal * outWardThickness; - verts[1] = edge.point2 - rightNormal * outWardThickness; - verts[2] = edge.point1 + leftNormal * inwardThickness; - - vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), Vector2.Zero); - vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX); - vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(0, 0.5f)); - } - else - { - verts[0] = edge.point1 + leftNormal * inwardThickness; - verts[1] = edge.point2 - rightNormal * outWardThickness; - verts[2] = edge.point2 + rightNormal * inwardThickness; - - vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], 0.0f), new Vector2(0.0f, 0.5f)); - vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], 0.0f), Vector2.UnitX); - vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], 0.0f), new Vector2(1.0f, 0.5f)); - } - - var comparer = new CompareCCW((verts[0] + verts[1] + verts[2]) / 3.0f); - Array.Sort(verts, vertPos, comparer); - - for (int j = 0; j<3; j++) - { - verticeList.Add(vertPos[j]); - } - } - } - } - - return verticeList.ToArray(); - } - - private Vector2 GetEdgeNormal(GraphEdge edge, VoronoiCell cell = null) - { - if (cell == null) cell = edge.AdjacentCell(null); - if (cell == null) return Vector2.UnitX; - - CompareCCW compare = new CompareCCW(cell.Center); - if (compare.Compare(edge.point1, edge.point2) == -1) - { - var temp = edge.point1; - edge.point1 = edge.point2; - edge.point2 = temp; - } - - Vector2 normal = Vector2.Zero; - - normal = Vector2.Normalize(edge.point2 - edge.point1); - Vector2 diffToCell = Vector2.Normalize(cell.Center - edge.point2); - - normal = new Vector2(-normal.Y, normal.X); - - if (Vector2.Dot(normal, diffToCell) < 0) - { - normal = -normal; - } - - return normal; - } - public Vector2 GetRandomItemPos(float offsetFromWall = 10.0f) { if (!positionsOfInterest.Any()) return Size*0.5f; @@ -1001,17 +640,13 @@ namespace Barotrauma if (!positionsOfInterest.Any()) return Size * 0.5f; if (preferLarge==null) - { return positionsOfInterest[Rand.Int(positionsOfInterest.Count, !useSyncedRand)].Position; - } - else - { - var positionsWithSpace = positionsOfInterest.FindAll(p => (bool)preferLarge == p.IsLarge); - if (!positionsWithSpace.Any()) return Size * 0.5f; + + var positionsWithSpace = positionsOfInterest.FindAll(p => (bool)preferLarge == p.IsLarge); + if (!positionsWithSpace.Any()) return Size * 0.5f; - return positionsWithSpace[Rand.Int(positionsWithSpace.Count, !useSyncedRand)].Position; - } + return positionsWithSpace[Rand.Int(positionsWithSpace.Count, !useSyncedRand)].Position; } public void Update (float deltaTime) @@ -1048,8 +683,8 @@ namespace Barotrauma public List GetCells(Vector2 pos, int searchDepth = 2) { - int gridPosX = (int)Math.Floor(pos.X / GridCellWidth); - int gridPosY = (int)Math.Floor(pos.Y / GridCellWidth); + int gridPosX = (int)Math.Floor(pos.X / GridCellSize); + int gridPosY = (int)Math.Floor(pos.Y / GridCellSize); int startX = Math.Max(gridPosX - searchDepth, 0); int endX = Math.Min(gridPosX + searchDepth, cellGrid.GetLength(0) - 1); @@ -1090,72 +725,8 @@ namespace Barotrauma return cells; } - public List GetCellEdges(Vector2 refPos, int searchDepth = 2, bool onlySolid = true) - { - int gridPosX = (int)Math.Floor(refPos.X / GridCellWidth); - int gridPosY = (int)Math.Floor(refPos.Y / GridCellWidth); - - int startX = Math.Max(gridPosX - searchDepth, 0); - int endX = Math.Min(gridPosX + searchDepth, cellGrid.GetLength(0) - 1); - - int startY = Math.Max(gridPosY - searchDepth, 0); - int endY = Math.Min(gridPosY + searchDepth, cellGrid.GetLength(1) - 1); - - List edges = new List(); - - for (int x = startX; x < endX; x++) - { - for (int y = startY; y < endY; y++) - { - foreach (VoronoiCell cell in cellGrid[x, y]) - { - for (int i = 0; i < cell.edges.Count; i++) - { - if (onlySolid && !cell.edges[i].isSolid) continue; - - Vector2 start = cell.edges[i].point1; - start.Y = -start.Y; - - Vector2 end = cell.edges[i].point2; - end.Y = -end.Y; - - edges.Add(new Vector2[] { start, end }); - //GUI.DrawLine(spriteBatch, start, end, (cell.body != null && cell.body.Enabled) ? Color.Green : Color.Red); - } - } - } - } - - for (int side = 0; side < 2; side++ ) - { - for (int n = 0 ; n<2; n++) - { - if (Vector2.Distance(wrappingWalls[side, n].MidPos, refPos) > WrappingWall.WallWidth) continue; - - foreach (VoronoiCell cell in wrappingWalls[side, n].Cells) - { - Vector2 offset = wrappingWalls[side, n].Offset; - for (int i = 0; i < cell.edges.Count; i++) - { - if (onlySolid && !cell.edges[i].isSolid) continue; - Vector2 start = cell.edges[i].point1 + offset; - start.Y = -start.Y; - - Vector2 end = cell.edges[i].point2 + offset; - end.Y = -end.Y; - - edges.Add(new Vector2[] { start, end }); - } - } - } - } - - return edges; - } - private void Unload() { - renderer.Dispose(); renderer = null; @@ -1173,11 +744,6 @@ namespace Barotrauma bodies = null; loaded = null; - - - - //vertexBuffer.Dispose(); - //vertexBuffer = null; } } diff --git a/Subsurface/Source/Map/Levels/LevelRenderer.cs b/Subsurface/Source/Map/Levels/LevelRenderer.cs index fe94afd35..577ce74a3 100644 --- a/Subsurface/Source/Map/Levels/LevelRenderer.cs +++ b/Subsurface/Source/Map/Levels/LevelRenderer.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; +using Voronoi2; namespace Barotrauma { @@ -157,6 +158,33 @@ namespace Barotrauma { Vector2 pos = new Vector2(0.0f, -level.Size.Y);// level.EndPosition; + var cells = level.GetCells(GameMain.GameScreen.Cam.WorldViewCenter, 2); + foreach (VoronoiCell cell in cells) + { + GUI.DrawRectangle(spriteBatch, new Vector2(cell.Center.X - 10.0f, -cell.Center.Y-10.0f), new Vector2(20.0f, 20.0f), Color.Cyan, true); + + GUI.DrawLine(spriteBatch, + new Vector2(cell.edges[0].point1.X, -cell.edges[0].point1.Y), + new Vector2(cell.Center.X, -cell.Center.Y), + Color.White); + + foreach (GraphEdge edge in cell.edges) + { + //GUI.DrawLine(spriteBatch, + // new Vector2(edge.point1.X, -edge.point1.Y), + // new Vector2(cell.Center.X, -cell.Center.Y), + // Color.White); + + GUI.DrawLine(spriteBatch, new Vector2(edge.point1.X, -edge.point1.Y), + new Vector2(edge.point2.X, -edge.point2.Y), Color.White); + } + + foreach (Vector2 point in cell.bodyVertices) + { + GUI.DrawRectangle(spriteBatch, new Vector2(point.X, -point.Y), new Vector2(10.0f, 10.0f), Color.White, true); + } + } + if (GameMain.GameScreen.Cam.WorldView.Y < -pos.Y - 512) return; pos.X = GameMain.GameScreen.Cam.WorldView.X -512.0f; @@ -169,6 +197,8 @@ namespace Barotrauma Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 0.0f); + + } diff --git a/Subsurface/Source/Map/Levels/Voronoi.cs b/Subsurface/Source/Map/Levels/Voronoi.cs index 3b456efbb..41a2252fa 100644 --- a/Subsurface/Source/Map/Levels/Voronoi.cs +++ b/Subsurface/Source/Map/Levels/Voronoi.cs @@ -972,6 +972,18 @@ namespace Voronoi2 return true; } + public List MakeVoronoiGraph(List sites, float minX, float minY, float maxX, float maxY) + { + double[] xVal = new double[sites.Count]; + double[] yVal = new double[sites.Count]; + for (int i = 0; i < sites.Count; i++) + { + xVal[i] = sites[i].X; + yVal[i] = sites[i].Y; + } + return generateVoronoi(xVal, yVal, minX, maxX, minY, maxY); + } + public List MakeVoronoiGraph(List sites, int width, int height) { double[] xVal = new double[sites.Count]; diff --git a/Subsurface/Source/Map/Levels/VoronoiElements.cs b/Subsurface/Source/Map/Levels/VoronoiElements.cs index b5ce48af9..57d0baff2 100644 --- a/Subsurface/Source/Map/Levels/VoronoiElements.cs +++ b/Subsurface/Source/Map/Levels/VoronoiElements.cs @@ -117,6 +117,11 @@ namespace Voronoi2 } } + public enum CellType + { + Solid, Empty, Edge, Path, Removed + } + public class VoronoiCell { public List edges; @@ -126,6 +131,8 @@ namespace Voronoi2 public Body body; + public CellType CellType; + public Vector2 Translation; public Vector2 Center @@ -174,6 +181,16 @@ namespace Voronoi2 //bodies = new List(); this.site = site; } + + public bool IsPointInside(Vector2 point) + { + foreach (GraphEdge edge in edges) + { + if (MathUtils.LinesIntersect(point, Center, edge.point1, edge.point2)) return false; + } + + return true; + } } public class GraphEdge diff --git a/Subsurface/Source/Map/Levels/WrappingWall.cs b/Subsurface/Source/Map/Levels/WrappingWall.cs index 797a34bfb..cd290a1e5 100644 --- a/Subsurface/Source/Map/Levels/WrappingWall.cs +++ b/Subsurface/Source/Map/Levels/WrappingWall.cs @@ -198,8 +198,16 @@ namespace Barotrauma protected virtual void Dispose(bool disposing) { - if (wallVertices!=null) wallVertices.Dispose(); - if (bodyVertices!=null) bodyVertices.Dispose(); + if (wallVertices != null) + { + wallVertices.Dispose(); + wallVertices = null; + } + if (bodyVertices != null) + { + bodyVertices.Dispose(); + bodyVertices = null; + } } } }