private List <Vector3> CreateRoad(Bounds bounds, int pointCount, Vector2[] clipPolygon)
        {
            // get offset, eg when biome mask is used for clipping
            float xmin = bounds.center.x - bounds.extents.x;
            float zmin = bounds.center.z - bounds.extents.z;

            // get 0/0-based bounds for graph processing
            Bounds graphBounds = new Bounds(bounds.size / 2, bounds.size);

            DelaunayVoronoiGraph graph = new DelaunayVoronoiGraph();

            // algorithm is 0-based
            graph.GeneratePoints(0, bounds.size.x, bounds.size.z); // initialize with the dimensions which are used in the algorithm

            // add random points
            for (int i = 0; i < pointCount; i++)
            {
                // get random point starting at [0/0]
                Vector2 vector = PolygonUtils.GetRandomPointXZ(graphBounds);

                graph.AddPoint(vector.x, vector.y);
            }

            // create the graph using the points
            graph.CreateGraph();

            int cellIndex = Random.Range(0, pointCount - 1);

            // normalize clip polygon, shift to [0/0]
            Vector2[] offsetClipPolygon = clipPolygon.Select(item => new Vector2(item.x - xmin, item.y - zmin)).ToArray();

            // get cell, clip it at the clip polygon
            Cell cell = graph.GetVoronoiCell(cellIndex, offsetClipPolygon);

            if (cell == null)
            {
                return(null);
            }

            // consider biome mask shift: shift points away from 0/0 if necessary
            Vector3 position = new Vector3(cell.Centroid.x + xmin, 0, cell.Centroid.y + zmin); // TODO: recalculate, might have changed because of clipping

            // consider biome mask shift: shift points away from 0/0 if necessary
            List <Vector3> nodes = cell.Vertices.Select(item => new Vector3(item.x + xmin, 0, item.y + zmin)).ToList();

            // apply random shape if requested
            if (editor.extension.shapeSettings.randomShape)
            {
                nodes = ShapeCreator.CreateRandomShape(nodes,                                             //
                                                       editor.extension.shapeSettings.RandomConvexity,    //
                                                       editor.extension.shapeSettings.keepOriginalPoints, //
                                                       editor.extension.shapeSettings.RandomPointsCount,  //
                                                       editor.extension.shapeSettings.randomAngle,        //
                                                       editor.extension.shapeSettings.douglasPeuckerReductionTolerance);
            }

            return(nodes);
        }
Example #2
0
        /// <summary>
        /// Create Biome masks for the specified bounds
        /// </summary>
        /// <param name="bounds"></param>
        private void CreateMasks(Bounds bounds)
        {
            float outerRadius = GetOuterRadius(bounds);

            // bounds for clipping
            Vector2[] clipPolygon = editor.GetBiomeClipPolygon(bounds);

            float density = editor.extension.biomeSettings.density;

            List <Vector3> positions = GetPositions(bounds);

            foreach (Vector3 position in positions)
            {
                // skip randomly
                if (density != 1 && UnityEngine.Random.Range(0f, 1f) >= density)
                {
                    continue;
                }

                Vector3[] hexagon = ShapeCreator.CreateHexagon(position, outerRadius);

                // clip, convert to vector2
                Vector2[] polygonXY     = hexagon.Select(item => new Vector2(item.x, item.z)).ToArray();
                Vector2[] clippedPoints = SutherlandHodgman.GetIntersectedPolygon(polygonXY, clipPolygon);

                if (clippedPoints == null || clippedPoints.Length < 3)
                {
                    continue;
                }

                // convert back to vector3
                hexagon = clippedPoints.Select(item => new Vector3(item.x, 0, item.y)).ToArray();

                int maskId = editor.GetNextMaskId();

                List <Vector3> nodes = hexagon.OfType <Vector3>().ToList();

                // apply random shape if requested
                if (editor.extension.shapeSettings.randomShape)
                {
                    nodes = ShapeCreator.CreateRandomShape(nodes,                                             //
                                                           editor.extension.shapeSettings.RandomConvexity,    //
                                                           editor.extension.shapeSettings.keepOriginalPoints, //
                                                           editor.extension.shapeSettings.RandomPointsCount,  //
                                                           editor.extension.shapeSettings.randomAngle,        //
                                                           editor.extension.shapeSettings.douglasPeuckerReductionTolerance);
                }

                CreateBiomeMaskArea("Biome Mask " + maskId, "Mask " + maskId, position, nodes);
            }
        }
        private void CreateBiomeMaskArea(string gameObjectName, string maskName, Bounds bounds)
        {
            // modify bounds, reduce its rectangular distribution
            float minFactor = editor.extension.rectangularPartitionSettings.boundsShiftFactorMin;
            float maxFactor = editor.extension.rectangularPartitionSettings.boundsShiftFactorMax;

            bounds = PolygonUtils.ShiftResizeBounds(bounds, minFactor, maxFactor);

            List <Vector3> nodes;

            if (editor.extension.shapeSettings.randomShape)
            {
                // old mechanism: convex hull
                // nodes = ShapeCreator.CreateRandomShapeUsingConvexHull(bounds, randomPointCount);

                // new mechanism: random shape with parameters like convexity
                nodes = ShapeCreator.CreateRandomShape(
                    ShapeCreator.CreateRectangularBoundsShape(bounds),
                    editor.extension.shapeSettings.RandomConvexity,     //
                    editor.extension.shapeSettings.keepOriginalPoints,  //
                    editor.extension.shapeSettings.RandomPointsCount,   //
                    editor.extension.shapeSettings.randomAngle,         //
                    editor.extension.shapeSettings.douglasPeuckerReductionTolerance);
            }
            // exact bounds
            else
            {
                nodes = ShapeCreator.CreateRectangularBoundsShape(bounds);
            }


            // bounds for clipping
            Vector2[] clipPolygon = editor.GetBiomeClipPolygon(bounds);

            // clip, convert to vector2
            Vector2[] polygonXY     = nodes.Select(item => new Vector2(item.x, item.z)).ToArray();
            Vector2[] clippedPoints = SutherlandHodgman.GetIntersectedPolygon(polygonXY, clipPolygon);

            if (clippedPoints == null || clippedPoints.Length < 3)
            {
                return;
            }

            // convert back to vector3
            nodes = clippedPoints.Select(item => new Vector3(item.x, 0, item.y)).ToList();

            // create the biome mask using the nodes
            CreateBiomeMaskArea(gameObjectName, maskName, bounds, nodes);
        }
        private void CreateRoads(Bounds bounds)
        {
            // not too close to the bounds; the easy roads spline might move the road outside
            bounds.size *= 0.9f;

            Vector2[] clipBounds = new Vector2[] {
                new Vector2(bounds.center.x - bounds.extents.x, bounds.center.z - bounds.extents.z),
                new Vector2(bounds.center.x + bounds.extents.x, bounds.center.z - bounds.extents.z),
                new Vector2(bounds.center.x + bounds.extents.x, bounds.center.z + bounds.extents.z),
                new Vector2(bounds.center.x - bounds.extents.x, bounds.center.z + bounds.extents.z),
            };

            int pointCount = editor.extension.voronoiSettings.pointCount;

            List <Vector3> maskNodes = CreateRoad(bounds, pointCount, clipBounds);

            List <Vector3> nodes = ShapeCreator.CreateRandomShape(maskNodes,                                         //
                                                                  editor.extension.shapeSettings.RandomConvexity,    //
                                                                  editor.extension.shapeSettings.keepOriginalPoints, //
                                                                  editor.extension.shapeSettings.RandomPointsCount,  //
                                                                  editor.extension.shapeSettings.randomAngle,        //
                                                                  editor.extension.shapeSettings.douglasPeuckerReductionTolerance);

            PolygonUtils.SortClockWise(nodes);

            if (smoothEnabled.boolValue)
            {
                List <Vector2> positionsXY = nodes.ConvertAll(item => new Vector2(item.x, item.z));
                positionsXY = getCurveSmoothingChaikin(positionsXY, 0.5f, 0);

                nodes = positionsXY.ConvertAll(item => new Vector3(item.x, 0, item.y));
            }

            nodes = AlignToTerrainHeight(nodes);

            // remove nodes that are too close to each other
            RemoveNodes(nodes, minDistance.floatValue);

            // set ER markers
            erRoad.AddMarkers(nodes.ToArray());

            // set closed track
            erRoad.ClosedTrack(closedTrack.boolValue);
        }