Ejemplo n.º 1
0
        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 <VertexPositionTexture> GenerateWallEdgeVertices(List <VoronoiCell> cells, Level level, float zCoord)
        {
            float outWardThickness = level.GenerationParams.WallEdgeExpandOutwardsAmount;

            List <VertexPositionTexture> vertices = new List <VertexPositionTexture>();

            foreach (VoronoiCell cell in cells)
            {
                Vector2 minVert       = cell.Edges[0].Point1;
                Vector2 maxVert       = cell.Edges[0].Point1;
                float   circumference = 0.0f;
                foreach (GraphEdge edge in cell.Edges)
                {
                    circumference += Vector2.Distance(edge.Point1, edge.Point2);
                    minVert        = new Vector2(
                        Math.Min(minVert.X, edge.Point1.X),
                        Math.Min(minVert.Y, edge.Point1.Y));
                    maxVert = new Vector2(
                        Math.Max(maxVert.X, edge.Point1.X),
                        Math.Max(maxVert.Y, edge.Point1.Y));
                }
                Vector2 center = (minVert + maxVert) / 2;
                foreach (GraphEdge edge in cell.Edges)
                {
                    if (!edge.IsSolid)
                    {
                        continue;
                    }

                    GraphEdge leftEdge         = cell.Edges.Find(e => e != edge && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
                    var       leftAdjacentCell = leftEdge?.AdjacentCell(cell);
                    if (leftAdjacentCell != null)
                    {
                        var adjEdge = leftAdjacentCell.Edges.Find(e => e != leftEdge && e.IsSolid && (edge.Point1.NearlyEquals(e.Point1) || edge.Point1.NearlyEquals(e.Point2)));
                        if (adjEdge != null)
                        {
                            leftEdge = adjEdge;
                        }
                    }

                    GraphEdge rightEdge         = cell.Edges.Find(e => e != edge && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
                    var       rightAdjacentCell = rightEdge?.AdjacentCell(cell);
                    if (rightAdjacentCell != null)
                    {
                        var adjEdge = rightAdjacentCell.Edges.Find(e => e != rightEdge && e.IsSolid && (edge.Point2.NearlyEquals(e.Point1) || edge.Point2.NearlyEquals(e.Point2)));
                        if (adjEdge != null)
                        {
                            rightEdge = adjEdge;
                        }
                    }

                    Vector2 leftNormal = Vector2.Zero, rightNormal = Vector2.Zero;

                    float inwardThickness1 = level.GenerationParams.WallEdgeExpandInwardsAmount;
                    float inwardThickness2 = level.GenerationParams.WallEdgeExpandInwardsAmount;
                    if (leftEdge != null && !leftEdge.IsSolid)
                    {
                        leftNormal = edge.Point1.NearlyEquals(leftEdge.Point1) ?
                                     Vector2.Normalize(leftEdge.Point2 - leftEdge.Point1) :
                                     Vector2.Normalize(leftEdge.Point1 - leftEdge.Point2);
                    }
                    else if (leftEdge != null)
                    {
                        leftNormal = -Vector2.Normalize(edge.GetNormal(cell) + leftEdge.GetNormal(leftAdjacentCell ?? cell));
                        if (!MathUtils.IsValid(leftNormal))
                        {
                            leftNormal = -edge.GetNormal(cell);
                        }
                    }
                    else
                    {
                        leftNormal = Vector2.Normalize(cell.Center - edge.Point1);
                    }
                    inwardThickness1 = Math.Min(Vector2.Distance(edge.Point1, cell.Center), inwardThickness1);

                    if (!MathUtils.IsValid(leftNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid left normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidLeftNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid left normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + leftNormal + ", seed: " + level.Seed + ")");

                        if (cell.Body != null)
                        {
                            if (GameMain.World.BodyList.Contains(cell.Body))
                            {
                                GameMain.World.Remove(cell.Body);
                            }
                            cell.Body = null;
                        }
                        leftNormal = Vector2.UnitX;
                        break;
                    }

                    if (rightEdge != null && !rightEdge.IsSolid)
                    {
                        rightNormal = edge.Point2.NearlyEquals(rightEdge.Point1) ?
                                      Vector2.Normalize(rightEdge.Point2 - rightEdge.Point1) :
                                      Vector2.Normalize(rightEdge.Point1 - rightEdge.Point2);
                    }
                    else if (rightEdge != null)
                    {
                        rightNormal = -Vector2.Normalize(edge.GetNormal(cell) + rightEdge.GetNormal(rightAdjacentCell ?? cell));
                        if (!MathUtils.IsValid(rightNormal))
                        {
                            rightNormal = -edge.GetNormal(cell);
                        }
                    }
                    else
                    {
                        rightNormal = Vector2.Normalize(cell.Center - edge.Point2);
                    }
                    inwardThickness2 = Math.Min(Vector2.Distance(edge.Point2, cell.Center), inwardThickness2);

                    if (!MathUtils.IsValid(rightNormal))
                    {
#if DEBUG
                        DebugConsole.ThrowError("Invalid right normal");
#endif
                        GameAnalyticsManager.AddErrorEventOnce("CaveGenerator.GenerateWallShapes:InvalidRightNormal:" + level.Seed,
                                                               GameAnalyticsSDK.Net.EGAErrorSeverity.Warning,
                                                               "Invalid right normal (leftedge: " + leftEdge + ", rightedge: " + rightEdge + ", normal: " + rightNormal + ", seed: " + level.Seed + ")");

                        if (cell.Body != null)
                        {
                            if (GameMain.World.BodyList.Contains(cell.Body))
                            {
                                GameMain.World.Remove(cell.Body);
                            }
                            cell.Body = null;
                        }
                        rightNormal = Vector2.UnitX;
                        break;
                    }

                    float point1UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point1 - center));
                    float point2UV = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(edge.Point2 - center));
                    //handle wrapping around 0/360
                    if (point1UV - point2UV > MathHelper.Pi)
                    {
                        point1UV -= MathHelper.TwoPi;
                    }
                    int textureRepeatCount = (int)Math.Max(circumference / 2 / level.GenerationParams.WallEdgeTextureWidth, 1);
                    point1UV = point1UV / MathHelper.TwoPi * textureRepeatCount;
                    point2UV = point2UV / MathHelper.TwoPi * textureRepeatCount;

                    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 * inwardThickness1;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 0.0f));
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point1UV, 1.0f));
                        }
                        else
                        {
                            verts[0] = edge.Point1 + leftNormal * inwardThickness1;
                            verts[1] = edge.Point2 - rightNormal * outWardThickness;
                            verts[2] = edge.Point2 + rightNormal * inwardThickness2;

                            vertPos[0] = new VertexPositionTexture(new Vector3(verts[0], zCoord), new Vector2(point1UV, 1.0f));
                            vertPos[1] = new VertexPositionTexture(new Vector3(verts[1], zCoord), new Vector2(point2UV, 0.0f));
                            vertPos[2] = new VertexPositionTexture(new Vector3(verts[2], zCoord), new Vector2(point2UV, 1.0f));
                        }
                        vertices.AddRange(vertPos);
                    }
                }
            }

            return(vertices);
        }
Ejemplo n.º 3
0
        public static List <VoronoiCell> CarveCave(List <VoronoiCell> cells, Vector2 startPoint, out List <VoronoiCell> newCells)
        {
            Voronoi voronoi = new Voronoi(1.0);

            List <Vector2> sites = new List <Vector2>();

            float siteInterval = 400.0f;
            float siteVariance = siteInterval * 0.4f;

            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 -= siteInterval * 2;
            edges.Y -= siteInterval * 2;
            edges.Z += siteInterval * 2;
            edges.W += siteInterval * 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 + siteInterval; x < edges.Z - siteInterval; x += siteInterval)
            {
                for (float y = edges.Y + siteInterval; y < edges.W - siteInterval; y += siteInterval)
                {
                    if (Rand.Int(5, false) == 0)
                    {
                        continue;                          //skip some positions to make the cells more irregular
                    }
                    sites.Add(new Vector2(x, y) + Rand.Vector(siteVariance, false));
                }
            }

            List <GraphEdge> graphEdges = voronoi.MakeVoronoiGraph(sites, edges.X, edges.Y, edges.Z, edges.W);

            List <VoronoiCell>[,] cellGrid;
            newCells = GraphEdgesToCells(graphEdges, borders, 1000, out cellGrid);

            foreach (VoronoiCell cell in newCells)
            {
                //if the cell is at the edge of the graph, remove it
                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;
                }

                //remove cells that aren't inside any of the original "base cells"
                if (cells.Any(c => c.IsPointInside(cell.Center)))
                {
                    continue;
                }
                foreach (GraphEdge edge in cell.edges)
                {
                    //mark all the cells adjacent to the removed cell as edges of the cave
                    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);

            //start carving from the edge cell closest to the startPoint
            VoronoiCell startCell   = null;
            float       closestDist = 0.0f;

            foreach (VoronoiCell cell in newCells)
            {
                if (cell.CellType != CellType.Edge)
                {
                    continue;
                }

                float dist = Vector2.Distance(startPoint, cell.Center);
                if (dist < closestDist || startCell == null)
                {
                    startCell   = cell;
                    closestDist = dist;
                }
            }

            startCell.CellType = CellType.Path;

            List <VoronoiCell> path = new List <VoronoiCell>()
            {
                startCell
            };
            VoronoiCell pathCell = startCell;

            for (int i = 0; i < newCells.Count / 2; i++)
            {
                var allowedNextCells = new List <VoronoiCell>();
                foreach (GraphEdge edge in pathCell.edges)
                {
                    var adjacent = edge.AdjacentCell(pathCell);
                    if (adjacent == null ||
                        adjacent.CellType == CellType.Removed ||
                        adjacent.CellType == CellType.Edge)
                    {
                        continue;
                    }

                    allowedNextCells.Add(adjacent);
                }

                if (allowedNextCells.Count == 0)
                {
                    if (i > 5)
                    {
                        break;
                    }

                    foreach (GraphEdge edge in pathCell.edges)
                    {
                        var adjacent = edge.AdjacentCell(pathCell);
                        if (adjacent == null ||
                            adjacent.CellType == CellType.Removed)
                        {
                            continue;
                        }

                        allowedNextCells.Add(adjacent);
                    }

                    if (allowedNextCells.Count == 0)
                    {
                        break;
                    }
                }

                //randomly pick one of the adjacent cells as the next cell
                pathCell = allowedNextCells[Rand.Int(allowedNextCells.Count, false)];

                //randomly take steps further away from the startpoint to make the cave expand further
                if (Rand.Int(4, false) == 0)
                {
                    float furthestDist = 0.0f;
                    foreach (VoronoiCell nextCell in allowedNextCells)
                    {
                        float dist = Vector2.Distance(startCell.Center, nextCell.Center);
                        if (dist > furthestDist || furthestDist == 0.0f)
                        {
                            furthestDist = dist;
                            pathCell     = nextCell;
                        }
                    }
                }

                pathCell.CellType = CellType.Path;
                path.Add(pathCell);
            }

            //make sure the tunnel is always wider than minPathWidth
            float minPathWidth = 100.0f;

            for (int i = 0; i < path.Count; i++)
            {
                var cell = path[i];
                foreach (GraphEdge edge in cell.edges)
                {
                    if (edge.point1 == edge.point2)
                    {
                        continue;
                    }
                    if (Vector2.Distance(edge.point1, edge.point2) > minPathWidth)
                    {
                        continue;
                    }

                    GraphEdge adjacentEdge = cell.edges.Find(e => e != edge && (e.point1 == edge.point1 || e.point2 == edge.point1));

                    var adjacentCell = adjacentEdge.AdjacentCell(cell);
                    if (i > 0 && (adjacentCell.CellType == CellType.Path || adjacentCell.CellType == CellType.Edge))
                    {
                        continue;
                    }

                    adjacentCell.CellType = CellType.Path;
                    path.Add(adjacentCell);
                }
            }

            return(path);
        }