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); }
public static void CreateHexagon(VegetationMaskArea mask) { float radius = GetRadius(mask); Vector3[] hexagon = ShapeCreator.CreateHexagon(mask.transform.position, radius); mask.Nodes.Clear(); mask.AddNodesToEnd(hexagon); // center main handle, implicitly updates the mask CenterMainHandle(mask); }
/// <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); }