Beispiel #1
0
        /// <summary>
        /// Finds the closest indices between two contours. Useful for merging contours.
        /// </summary>
        /// <param name="a">A contour.</param>
        /// <param name="b">Another contour.</param>
        /// <param name="indexA">The nearest index on contour A.</param>
        /// <param name="indexB">The nearest index on contour B.</param>
        private static void GetClosestIndices(Contour a, Contour b, out int indexA, out int indexB)
        {
            int closestDistance = int.MaxValue;
            int lengthA         = a.vertices.Length;
            int lengthB         = b.vertices.Length;

            indexA = -1;
            indexB = -1;

            for (int i = 0; i < lengthA; i++)
            {
                int vertA     = i;
                int vertANext = (i + 1) % lengthA;
                int vertAPrev = (i + lengthA - 1) % lengthA;

                for (int j = 0; j < lengthB; j++)
                {
                    int vertB = j;

                    //vertB must be infront of vertA
                    if (ContourVertex.IsLeft(ref a.vertices[vertAPrev], ref a.vertices[vertA], ref b.vertices[vertB]) &&
                        ContourVertex.IsLeft(ref a.vertices[vertA], ref a.vertices[vertANext], ref b.vertices[vertB]))
                    {
                        int dx       = b.vertices[vertB].X - a.vertices[vertA].X;
                        int dz       = b.vertices[vertB].Z - a.vertices[vertA].Z;
                        int tempDist = dx * dx + dz * dz;
                        if (tempDist < closestDistance)
                        {
                            indexA          = i;
                            indexB          = j;
                            closestDistance = tempDist;
                        }
                    }
                }
            }
        }
Beispiel #2
0
		/// <summary>
		/// Finds the closest indices between two contours. Useful for merging contours.
		/// </summary>
		/// <param name="a">A contour.</param>
		/// <param name="b">Another contour.</param>
		/// <param name="indexA">The nearest index on contour A.</param>
		/// <param name="indexB">The nearest index on contour B.</param>
		private static void GetClosestIndices(Contour a, Contour b, out int indexA, out int indexB)
		{
			int closestDistance = int.MaxValue;
			int lengthA = a.vertices.Length;
			int lengthB = b.vertices.Length;

			indexA = -1;
			indexB = -1;

			for (int i = 0; i < lengthA; i++)
			{
				int vertA = i;
				int vertANext = (i + 1) % lengthA;
				int vertAPrev = (i + lengthA - 1) % lengthA;

				for (int j = 0; j < lengthB; j++)
				{
					int vertB = j;

					//vertB must be infront of vertA
					if (ContourVertex.IsLeft(ref a.vertices[vertAPrev], ref a.vertices[vertA], ref b.vertices[vertB]) &&
						ContourVertex.IsLeft(ref a.vertices[vertA], ref a.vertices[vertANext], ref b.vertices[vertB]))
					{
						int dx = b.vertices[vertB].X - a.vertices[vertA].X;
						int dz = b.vertices[vertB].Z - a.vertices[vertA].Z;
						int tempDist = dx * dx + dz * dz;
						if (tempDist < closestDistance)
						{
							indexA = i;
							indexB = j;
							closestDistance = tempDist;
						}
					}
				}
			}
		}
        /// <summary>
        /// Builds a set of <see cref="Contour"/>s around the generated regions. Must be called after regions are generated.
        /// </summary>
        /// <param name="maxError">The maximum allowed deviation in a simplified contour from a raw one.</param>
        /// <param name="maxEdgeLength">The maximum edge length.</param>
        /// <param name="buildFlags">Flags that change settings for the build process.</param>
        /// <returns>A <see cref="ContourSet"/> containing one contour per region.</returns>
        public ContourSet BuildContourSet(float maxError, int maxEdgeLength, ContourBuildFlags buildFlags)
        {
            BBox3 contourSetBounds = bounds;
            if (borderSize > 0)
            {
                //remove offset
                float pad = borderSize * cellSize;
                contourSetBounds.Min.X += pad;
                contourSetBounds.Min.Z += pad;
                contourSetBounds.Max.X -= pad;
                contourSetBounds.Max.Z -= pad;
            }

            int contourSetWidth = width - borderSize * 2;
            int contourSetLength = length - borderSize * 2;

            int maxContours = Math.Max(maxRegions, 8);
            var contours = new List<Contour>(maxContours);

            EdgeFlags[] flags = new EdgeFlags[spans.Length];

            //Modify flags array by using the CompactHeightfield data
            //mark boundaries
            for (int z = 0; z < length; z++)
            {
                for (int x = 0; x < width; x++)
                {
                    //loop through all the spans in the cell
                    CompactCell c = cells[x + z * width];
                    for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++)
                    {
                        CompactSpan s = spans[i];

                        //set the flag to 0 if the region is a border region or null.
                        if (s.Region.IsNull || RegionId.HasFlags(s.Region, RegionFlags.Border))
                        {
                            flags[i] = 0;
                            continue;
                        }

                        //go through all the neighboring cells
                        for (var dir = Direction.West; dir <= Direction.South; dir++)
                        {
                            //obtain region id
                            RegionId r = RegionId.Null;
                            if (s.IsConnected(dir))
                            {
                                int dx = x + dir.GetHorizontalOffset();
                                int dz = z + dir.GetVerticalOffset();
                                int di = cells[dx + dz * width].StartIndex + CompactSpan.GetConnection(ref s, dir);
                                r = spans[di].Region;
                            }

                            //region ids are equal
                            if (r == s.Region)
                            {
                                //res marks all the internal edges
                                EdgeFlagsHelper.AddEdge(ref flags[i], dir);
                            }
                        }

                        //flags represents all the nonconnected edges, edges that are only internal
                        //the edges need to be between different regions
                        EdgeFlagsHelper.FlipEdges(ref flags[i]);
                    }
                }
            }

            var verts = new List<ContourVertex>();
            var simplified = new List<ContourVertex>();

            for (int z = 0; z < length; z++)
            {
                for (int x = 0; x < width; x++)
                {
                    CompactCell c = cells[x + z * width];
                    for (int i = c.StartIndex, end = c.StartIndex + c.Count; i < end; i++)
                    {
                        //flags is either 0000 or 1111
                        //in other words, not connected at all
                        //or has all connections, which means span is in the middle and thus not an edge.
                        if (flags[i] == EdgeFlags.None || flags[i] == EdgeFlags.All)
                        {
                            flags[i] = EdgeFlags.None;
                            continue;
                        }

                        var spanRef = new CompactSpanReference(x, z, i);
                        RegionId reg = this[spanRef].Region;
                        if (reg.IsNull || RegionId.HasFlags(reg, RegionFlags.Border))
                            continue;

                        //reset each iteration
                        verts.Clear();
                        simplified.Clear();

                        //Walk along a contour, then build it
                        WalkContour(spanRef, flags, verts);
                        Contour.Simplify(verts, simplified, maxError, maxEdgeLength, buildFlags);
                        Contour.RemoveDegenerateSegments(simplified);
                        Contour contour = new Contour(simplified, reg, areas[i], borderSize);

                        if (!contour.IsNull)
                            contours.Add(contour);
                    }
                }
            }

            //Check and merge bad contours
            for (int i = 0; i < contours.Count; i++)
            {
                Contour cont = contours[i];

                //Check if contour is backwards
                if (cont.Area2D < 0)
                {
                    //Find another contour to merge with
                    int mergeIndex = -1;
                    for (int j = 0; j < contours.Count; j++)
                    {
                        if (i == j)
                            continue;

                        //Must have at least one vertex, the same region ID, and be going forwards.
                        Contour contj = contours[j];
                        if (contj.Vertices.Length != 0 && contj.RegionId == cont.RegionId && contj.Area2D > 0)
                        {
                            mergeIndex = j;
                            break;
                        }
                    }

                    //Merge if found.
                    if (mergeIndex != -1)
                    {
                        contours[mergeIndex].MergeWith(cont);
                        contours.RemoveAt(i);
                        i--;
                    }
                }
            }

            return new ContourSet(contours, contourSetBounds, contourSetWidth, contourSetLength);
        }
Beispiel #4
0
		/// <summary>
		/// Merges another contour into this instance.
		/// </summary>
		/// <param name="contour">The contour to merge.</param>
		public void MergeWith(Contour contour)
		{
			int lengthA = vertices.Length;
			int lengthB = contour.vertices.Length;

			int ia, ib;
			GetClosestIndices(this, contour, out ia, out ib);

			//create a list with the capacity set to the max number of possible verts to avoid expanding the list.
			var newVerts = new List<ContourVertex>(vertices.Length + contour.vertices.Length + 2);

			//copy contour A
			for (int i = 0; i <= lengthA; i++)
				newVerts.Add(vertices[(ia + i) % lengthA]);

			//add contour B (other contour) to contour A (this contour)
			for (int i = 0; i <= lengthB; i++)
				newVerts.Add(contour.vertices[(ib + i) % lengthB]);

			vertices = newVerts.ToArray();
			
			//delete the other contour
			contour.vertices = null;
		}