Пример #1
0
        /// <summary>
        /// Get a position and angle along the biome mask's edge.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="angle"></param>
        private void GetRandomBiomeEdgePosition(out Vector3 position, out float angle)
        {
            BiomeMaskArea mask = editor.extension.lineSettings.biomeMaskArea;

            // parameter consistency check
            if (mask == null)
            {
                Debug.LogError("No mask defined");

                position = Vector3.zero;
                angle    = 0;
                return;
            }

            List <Vector3> positions = BiomeMaskUtils.GetPositions(mask);

            // sort clockwise, so that the pick algorithm works
            // if this were counterclockwise, then the angle would make the lines face inwards
            PolygonUtils.SortClockWise(positions);

            // get from node index
            int nodeIndexFrom = Random.Range(0, positions.Count); // note: int is exclusive last

            // get to node index, consider overlap
            int nodeIndexTo = nodeIndexFrom + 1;

            if (nodeIndexTo >= mask.Nodes.Count)
            {
                nodeIndexTo = 0;
            }

            // get nodes
            Vector3 positionFrom = mask.transform.position + positions[nodeIndexFrom];
            Vector3 positionTo   = mask.transform.position + positions[nodeIndexTo];

            // having the lines flip inwards into the biome is just a matter of changing the access order of the nodes
            // leaving this here, maybe we find a use case later
            bool flipAngle = editor.extension.lineSettings.attachedAngleFlip;

            if (flipAngle)
            {
                Vector3 tmp = positionFrom;
                positionFrom = positionTo;
                positionTo   = tmp;
            }

            float   distance  = (positionTo - positionFrom).magnitude;
            Vector3 direction = (positionTo - positionFrom).normalized;

            // the position along the edge. 0=from, 0.5=center, 1=to
            float relativePosition = Random.Range(0f, 1f);

            // calculate the position
            position = positionFrom + direction * distance * relativePosition;

            // calculate the angle 90 degrees to the from-to points and convert to degrees
            angle = Mathf.Atan2(positionTo.z - positionFrom.z, positionTo.x - positionFrom.x) * Mathf.Rad2Deg;
        }
        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);
        }
Пример #3
0
        /// <summary>
        /// Modifies a polygon and creates a random shape.
        ///
        /// Algorithm:
        ///
        /// * create a new polygon, subdivided this way:
        ///   + get center of polygon
        ///   + create a list of angles for iteration
        ///   + iterate through the angles, create a line using the angle and find the intersection with a line of the polygon
        /// * iterate through the subdivided polygon
        ///   + move the vertex towards the center
        ///   + consider previous vertex in order to not move in too large steps
        /// </summary>
        public static List <Vector3> CreateRandomShape(List <Vector3> polygon, float ellipseRelaxationFactor, int angleStepCount, bool randomAngleMovement, bool keepOriginalShape, float douglasPeuckerReductionTolerance)
        {
            Vector3 meanVector = PolygonUtils.GetMeanVector(polygon);
            float   length     = meanVector.magnitude * 2;


            #region create angles

            // get the list of angles to step through
            List <float> angleRadList = CreateAngleList(angleStepCount, randomAngleMovement);

            #endregion create angles

            #region create new polygon using angles

            List <Vector3> subdividedPolygon = new List <Vector3>();

            // add existing points in order to keep the general voronoi shape
            if (keepOriginalShape)
            {
                subdividedPolygon.AddRange(polygon);
            }

            for (int i = 0; i < angleRadList.Count; i++)
            {
                // convert angle from deg to rad
                float angle = angleRadList[i];

                float x = meanVector.x + length * Mathf.Cos(angle);
                float z = meanVector.z + length * Mathf.Sin(angle);

                // position on the ellipse
                Vector3 line1PositionA = meanVector;
                Vector3 line1PositionB = new Vector3(x, 0, z);

                for (var j = 0; j < polygon.Count; j++)
                {
                    int currIndex = j;
                    int nextIndex = j + 1;
                    if (nextIndex == polygon.Count)
                    {
                        nextIndex = 0;
                    }

                    Vector3 line2PositionA = new Vector3(polygon[currIndex].x, 0, polygon[currIndex].z);
                    Vector3 line2PositionB = new Vector3(polygon[nextIndex].x, 0, polygon[nextIndex].z);


                    Vector3 intersection = Vector3.zero;

                    // try and find an intersection. if one is found, add the point to the list
                    if (PolygonUtils.LineSegmentsIntersection(line1PositionA, line1PositionB, line2PositionA, line2PositionB, out intersection))
                    {
                        subdividedPolygon.Add(intersection);
                        break;
                    }
                }
            }

            // sort again
            PolygonUtils.SortClockWise(subdividedPolygon);

            #endregion create new polygon using angles


            #region create new polygon using the intersections
            List <Vector3> newPolygon = new List <Vector3>();

            float prevDistance = 0;

            for (int i = 0; i < subdividedPolygon.Count; i++)
            {
                Vector3 curr = subdividedPolygon[i];

                // position on the ellipse
                Vector3 position = curr;

                // from center to position
                float   distance  = (position - meanVector).magnitude;
                Vector3 direction = (position - meanVector).normalized;

                // move from center towards new position. but not too much, let it depend on the previous distance
                {
                    // move initially from 0 to max distance. otherwise use the previous value
                    float min = i == 0 ? distance * ellipseRelaxationFactor : prevDistance * ellipseRelaxationFactor;
                    float max = distance;

                    // the radius must be smaller during the next iteration, we are navigating around an ellipse => clamp the values
                    if (min > max)
                    {
                        min = max;
                    }

                    float moveDistance = UnityEngine.Random.Range(min, max);

                    distance = moveDistance;
                }

                position = meanVector + distance * direction;

                newPolygon.Add(position);

                prevDistance = distance;
            }
            #endregion create new polygon using the intersections

            if (douglasPeuckerReductionTolerance > 0)
            {
                // convert to vector2
                List <Vector2> vector2List = newPolygon.ConvertAll <Vector2>(item => new Vector2(item.x, item.z));

                // use vspro's DouglasPeuckerReduction algorithm
                vector2List = PolygonUtility.DouglasPeuckerReduction(vector2List, douglasPeuckerReductionTolerance);

                // convert back to vector3
                if (vector2List.Count >= 3)
                {
                    newPolygon = vector2List.ConvertAll <Vector3>(item => new Vector3(item.x, 0, item.y));
                }
            }

            return(newPolygon);
        }