/// <summary>
        /// Insert a new node between every node segment of the area
        /// </summary>
        public static void Subdivide(VegetationMaskArea mask)
        {
            List <Node> originalNodes = new List <Node>();

            originalNodes.AddRange(mask.Nodes);

            for (var i = 0; i < originalNodes.Count; i++)
            {
                Node curr = originalNodes[i];
                Node next = mask.GetNextNode(curr);

                Vector3[] segment = new Vector3[] { curr.Position, next.Position };

                Vector3 meanVector = PolygonUtils.GetMeanVector(segment);

                int index = mask.GetNodeIndex(curr);

                Node newNode = new Node()
                {
                    Position = meanVector
                };

                mask.Nodes.Insert(index + 1, newNode);
            }

            UpdateMask(mask);
        }
Beispiel #2
0
        /// <summary>
        /// Create a biome mask.
        /// The blend distance is simply calculated using the mean vector.
        /// </summary>
        /// <param name="gameObjectName"></param>
        /// <param name="maskName"></param>
        /// <param name="position"></param>
        /// <param name="nodes"></param>
        private void CreateBiomeMaskArea(string gameObjectName, string maskName, Vector3 position, List <Vector3> nodes)
        {
            float blendDistanceMin = editor.extension.biomeSettings.biomeBlendDistanceMin;
            float blendDistanceMax = editor.extension.biomeSettings.biomeBlendDistanceMax;

            float biomeBlendDistance = UnityEngine.Random.Range(blendDistanceMin, blendDistanceMax);

            // mean vector is just a simple measure. please adapt if you need more accuracy
            Vector3 meanVector = PolygonUtils.GetMeanVector(nodes);

            float blendDistance = meanVector.magnitude / 2f * biomeBlendDistance;

            // create the mask using the provided parameters
            editor.CreateBiomeMaskArea(gameObjectName, maskName, position, nodes, blendDistance);
        }
Beispiel #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);
        }
        /// <summary>
        /// Get the center of the mask polygon
        /// </summary>
        /// <param name="mask"></param>
        /// <returns></returns>
        public static Vector3 GetMaskCenter(VegetationMaskArea mask)
        {
            List <Vector3> worldPositions = mask.GetWorldSpaceNodePositions();

            return(PolygonUtils.GetMeanVector(worldPositions.ToArray()));
        }
Beispiel #5
0
 /// <summary>
 ///     Initializes a new instance of the ClockwiseComparer class and sets the origin to the mean vector, depending on the positions.
 /// </summary>
 /// <param name="origin">Origin.</param>
 public ClockwiseComparer(List <Vector3> positions)
 {
     m_Origin = PolygonUtils.GetMeanVector(positions);
 }