public static void renderTree(ClipperLib.PolyNode node, List <Vector3> verts, List <Vector3> norms, List <Vector2> uvs, List <int> faces, ref int idx) { Poly2Tri.Polygon p = new Poly2Tri.Polygon(node.Contour.ConvertAll <Poly2Tri.PolygonPoint>(PP)); //p.RemoveDuplicateNeighborPoints(); //p.MergeParallelEdges(1e-3); foreach (ClipperLib.PolyNode child in node.Childs) { if (child.IsHole) { Poly2Tri.Polygon h = new Poly2Tri.Polygon(child.Contour.ConvertAll <Poly2Tri.PolygonPoint>(PP)); h.RemoveDuplicateNeighborPoints(); h.MergeParallelEdges(1e-3); p.AddHole(h); } else { renderTree(child, verts, norms, uvs, faces, ref idx); } } Poly2Tri.DTSweepContext ctx = new Poly2Tri.DTSweepContext(); ctx.PrepareTriangulation(p); Poly2Tri.DTSweep.Triangulate(ctx); foreach (Poly2Tri.DelaunayTriangle dt in p.Triangles) { faces.Add(idx); uvs.Add(UV(dt.Points[2])); norms.Add(Vector3.back); verts.Add(V3(dt.Points[2])); idx++; faces.Add(idx); uvs.Add(UV(dt.Points[1])); norms.Add(Vector3.back); verts.Add(V3(dt.Points[1])); idx++; faces.Add(idx); uvs.Add(UV(dt.Points[0])); norms.Add(Vector3.back); verts.Add(V3(dt.Points[0])); idx++; } }
/// <summary> /// Creates a triangulation of the vertices given, and gives you the indices of it. /// </summary> /// <param name="aPoints">A list of points to triangulate.</param> /// <param name="aTreatAsPath">Should we discard any triangles at all? Use this if you want to get rid of triangles that are outside the path.</param> /// <param name="aInvert">if we're treating it as a path, should we instead sicard triangles inside the path?</param> /// <returns>A magical list of indices describing the triangulation!</returns> public static List<int> GetIndices (ref List<Vector2> aPoints, bool aTreatAsPath, bool aInvert, float aVertGridSpacing = 0) { Vector4 bounds = GetBounds(aPoints); if (aVertGridSpacing > 0) { SplitEdges(ref aPoints, aVertGridSpacing); } List<PolygonPoint> verts = new List<PolygonPoint>(aPoints.Count); for (int i = 0; i < aPoints.Count; i++) { verts.Add(new PolygonPoint( aPoints[i].x, aPoints[i].y)); } Polygon poly; if (aInvert) { aPoints.Add(new Vector2(bounds.x - (bounds.z - bounds.x) * 1, bounds.w - (bounds.y - bounds.w) * 1)); // 4 aPoints.Add(new Vector2(bounds.z + (bounds.z - bounds.x) * 1, bounds.w - (bounds.y - bounds.w) * 1)); // 3 aPoints.Add(new Vector2(bounds.z + (bounds.z - bounds.x) * 1, bounds.y + (bounds.y - bounds.w) * 1)); // 2 aPoints.Add(new Vector2(bounds.x - (bounds.z - bounds.x) * 1, bounds.y + (bounds.y - bounds.w) * 1)); // 1 List<PolygonPoint> outer = new List<PolygonPoint>(4); for (int i = 0; i < 4; i++) { outer.Add( new PolygonPoint( aPoints[(aPoints.Count - 4) + i].x, aPoints[(aPoints.Count - 4) + i].y) ); } poly = new Polygon(outer); poly.AddHole(new Polygon(verts)); } else { poly = new Polygon(verts); } if (aVertGridSpacing > 0) { if (aInvert) bounds = GetBounds(aPoints); for (float y = bounds.w + aVertGridSpacing; y <= bounds.y; y+=aVertGridSpacing) { for (float x = bounds.x + aVertGridSpacing; x <= bounds.z; x+=aVertGridSpacing) { TriangulationPoint pt = new TriangulationPoint(x, y); bool inside = poly.IsPointInside(pt); if (inside) poly.AddSteinerPoint(pt); } } } P2T.Triangulate(poly); aPoints.Clear(); List<int> result= new List<int>(poly.Triangles.Count * 3); int ind = 0; foreach (DelaunayTriangle triangle in poly.Triangles) { TriangulationPoint p1 = triangle.Points[0]; TriangulationPoint p2 = triangle.PointCWFrom(p1); TriangulationPoint p3 = triangle.PointCWFrom(p2); aPoints.Add(new Vector2(p1.Xf, p1.Yf)); aPoints.Add(new Vector2(p2.Xf, p2.Yf)); aPoints.Add(new Vector2(p3.Xf, p3.Yf)); result.Add(ind++); result.Add(ind++); result.Add(ind++); } return result; }
private void DrawFill(List <Vector> fillVertices) { if (m_mesh.Vertices.Count <= 2 || m_mesh.FillMode == FillMode.None || m_mesh.UvMapping.FillTexture == null) { return; } if (!m_mesh.IsClosed) { fillVertices.Add(m_mesh.FillMode != FillMode.Inverted ? m_mesh.Vertices.Last().Position : m_mesh.Vertices.First().Position); } var polygon = new Polygon(fillVertices.Select(v => new PolygonPoint(v.X, v.Y))); if (IsInverted) { var center = polygon.GetCentroid(); var size = new Size(polygon.BoundingBox.Width, polygon.BoundingBox.Height); var topLeft = new Point(center.X - size.Width, center.Y + size.Height); var topRight = new Point(center.X + size.Width, center.Y + size.Height); var bottomLeft = new Point(center.X - size.Width, center.Y - size.Height); var bottomRight = new Point(center.X + size.Width, center.Y - size.Height); var invertedPolygon = new Polygon( new PolygonPoint(bottomLeft.X, bottomLeft.Y), new PolygonPoint(topLeft.X, topLeft.Y), new PolygonPoint(topRight.X, topRight.Y), new PolygonPoint(bottomRight.X, bottomRight.Y)); invertedPolygon.AddHole(polygon); polygon = invertedPolygon; } try { P2T.Triangulate(polygon); } catch (Exception) { return; } var unitsPerFill = CalculateUnitsPerFillUv(); foreach (var triangle in polygon.Triangles) { MeshFillData.AddTriangle( new Point3D(triangle.Points._0.X, triangle.Points._0.Y, 0.0), new Point3D(triangle.Points._1.X, triangle.Points._1.Y, 0.0), new Point3D(triangle.Points._2.X, triangle.Points._2.Y, 0.0), new Point(triangle.Points._0.X / unitsPerFill.X, triangle.Points._0.Y / unitsPerFill.Y), new Point(triangle.Points._1.X / unitsPerFill.X, triangle.Points._1.Y / unitsPerFill.Y), new Point(triangle.Points._2.X / unitsPerFill.X, triangle.Points._2.Y / unitsPerFill.Y)); } }
static Polygon CleanClone(Polygon polygon) { var n = new Polygon(polygon.Points.Select(p => new PolygonPoint(p.X, p.Y))); if (polygon.Holes != null) { foreach (var hole in polygon.Holes) { n.AddHole(CleanClone(hole)); } } return n; }
static Polygon CleanClone(Polygon polygon) { var n = new Polygon(polygon.Points.Select(p => new PolygonPoint(p.X, p.Y))); if (polygon.Holes != null) { foreach (var hole in polygon.Holes) { n.AddHole(CleanClone(hole)); } } return(n); }
public List<DelaunayTriangle> TriangulateComplex( PolyTree polyTree, bool ignoreFills = false, bool ignoreHoles = true) { PolyNode rootNode; if (polyTree.Total == 0) { Console.WriteLine(0); rootNode = new PolyNode(); } else { rootNode = polyTree.GetFirst().Parent; } // Equivalent to rootNode.Contour = bounds; var contourField = rootNode.GetType().GetField("m_polygon", BindingFlags.Instance | BindingFlags.NonPublic); if (contourField == null) { throw new Exception("Could not find field contour backing field."); } contourField.SetValue(rootNode, kTriangulatorBounds); var result = new List<DelaunayTriangle>(); int i = 0; for (var currentNode = rootNode; currentNode != null; currentNode = currentNode.GetNext()) { if ((ignoreHoles && currentNode.IsHole) || (ignoreFills && !currentNode.IsHole)) continue; var polyline = DownscalePolygon(currentNode.Contour); var finalPolygon = new Polygon(polyline); foreach (var child in currentNode.Childs) { var shrunkContour = EdgeShrink(child.Contour); var holePoints = DownscalePolygon(shrunkContour); var holePoly = new Polygon(holePoints); finalPolygon.AddHole(holePoly); } P2T.Triangulate(finalPolygon); result.AddRange(finalPolygon.Triangles); } return result; }
protected override Polygon GetPolygon() { Polygon poly = new Polygon(GetPoints()); if (thickness > 1) { List<PolygonPoint> points = GetPoints(); Vector2 center = GetCentroid(points); float[] angles = { AngleA(a, b, c), AngleB(a, b, c), AngleC(a, b, c) }; int count = points.Count; for (int i = count; --i >= 0;) { PolygonPoint point = points[i]; double vecX = center.X - point.X; double vecY = center.Y - point.Y; double invLen = 1d / Math.Sqrt((vecX * vecX) + (vecY * vecY)); vecX = vecX * invLen; vecY = vecY * invLen; float ratio = 1 - (angles[i] / 180); float angleThickness = ratio * thickness; point.X += vecX * angleThickness; point.Y += vecY * angleThickness; } Polygon hole = new Polygon(points); poly.AddHole(hole); } return poly; }
/// <summary> /// Create a Mesh from a given Polygon. /// </summary> /// <returns>The freshly minted mesh.</returns> /// <param name="polygon">Polygon you want to triangulate.</param> public static Mesh CreateMesh(Polygon polygon) { // Ensure we have the rotation properly calculated if (polygon.rotation == Quaternion.identity) polygon.CalcRotation(); // Rotate 1 point and note where it ends up in Z float z = (polygon.rotation * polygon.outside[0]).z; // Convert the outside points (throwing out Z at this point) Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ConvertPoints(polygon.outside, polygon.rotation)); // Convert each of the holes foreach (List<Vector3> hole in polygon.holes) { poly.AddHole(new Poly2Tri.Polygon(ConvertPoints(hole, polygon.rotation))); } // Triangulate it! Note that this may throw an exception if the data is bogus. DTSweepContext tcx = new DTSweepContext(); tcx.PrepareTriangulation(poly); DTSweep.Triangulate(tcx); tcx = null; // Create the Vector3 vertices (undoing the rotation), // and also build a map of vertex codes to indices Quaternion invRot = Quaternion.Inverse(polygon.rotation); Dictionary<uint, int> codeToIndex = new Dictionary<uint, int>(); List<Vector3> vertexList = new List<Vector3>(); foreach (DelaunayTriangle t in poly.Triangles) { foreach (var p in t.Points) { if (codeToIndex.ContainsKey(p.VertexCode)) continue; codeToIndex[p.VertexCode] = vertexList.Count; Vector3 pos = new Vector3(p.Xf, p.Yf, z); // (restore the Z we saved earlier) vertexList.Add(invRot * pos); } } // Create the indices array int[] indices = new int[poly.Triangles.Count * 3]; { int i = 0; foreach (DelaunayTriangle t in poly.Triangles) { indices[i++] = codeToIndex[t.Points[0].VertexCode]; indices[i++] = codeToIndex[t.Points[1].VertexCode]; indices[i++] = codeToIndex[t.Points[2].VertexCode]; } } // Create the UV list, by looking up the closest point for each in our poly Mesh msh = new Mesh(); msh.vertices = vertexList.ToArray(); Vector2[] uvs = new Vector2[msh.vertices.Length]; for (int i=0; i < uvs.Length; i++) { uvs[i] = new Vector2(msh.vertices[i].x, msh.vertices[i].y); } msh.uv = uvs; /*if (polygon.OutsideUVs != null) { uv = new Vector2[vertexList.Count]; for (int i=0; i<vertexList.Count; i++) { uv[i] = polygon.ClosestUV(vertexList[i]); } }*/ // Create the mesh msh.triangles = indices; msh.RecalculateNormals(); msh.RecalculateBounds(); return msh; }
static bool TryInsertHole(Polygon parent, Polygon hole) { //Can't go in. if (!parent.IsPointInside(hole[0])) return false; if (parent.Holes != null) foreach (var item in parent.Holes) { if (TryInsertHole(item, hole)) return true; } //it doesn't fit into any of the daughter holes. parent.AddHole(hole); return true; }
protected override Polygon GetPolygon() { Polygon poly = new Polygon( GetPoints() ); if( thickness > 0 ) { List<PolygonPoint> holePoints = new List<PolygonPoint>(); float x; float y; float radAngle; float degreeStep = 360f / sides; float r = radius - thickness; for( int i = 0; i < sides; i++ ) { radAngle = MathUtil.DegreesToRadians( degreeStep * i ); x = (float)( Math.Cos( radAngle ) * r ); y = (float)( Math.Sin( radAngle ) * r ); holePoints.Add( new PolygonPoint( x, y ) ); } Polygon hole = new Polygon( holePoints ); poly.AddHole( hole ); } return poly; }
/// <summary> /// triangulate polygon, algorithm from wiki is fast enough /// http://wiki.unity3d.com/index.php?title=Triangulator /// </summary> /// <returns></returns> public List<int> Triangulate() { // no holes supported if (holes.Count == 0) { var indices = new List<int>(Points.Length); int n = Points.Length; if (n < 3) return indices; var V = new int[n]; if (Area > 0) { for (int v = 0; v < n; v++) V[v] = v; } else { for (int v = 0; v < n; v++) V[v] = (n - 1) - v; } int nv = n; int count = 2*nv; for (int m = 0, v = nv - 1; nv > 2;) { if ((count--) <= 0) return indices; int u = v; if (nv <= u) u = 0; v = u + 1; if (nv <= v) v = 0; int w = v + 1; if (nv <= w) w = 0; if (Snip(u, v, w, nv, V)) { int a, b, c, s, t; a = V[u]; b = V[v]; c = V[w]; indices.Add(a); indices.Add(b); indices.Add(c); m++; for (s = v, t = v + 1; t < nv; s++, t++) V[s] = V[t]; nv--; count = 2*nv; } } indices.Reverse(); return indices; } else { // use poly2tri library to triangulate mesh with holes var p2tPoints = new List<Poly2Tri.PolygonPoint>(Points.Length); foreach (var point in Points) { p2tPoints.Add(new Poly2Tri.PolygonPoint(point.x, point.y)); } // create p2t polygon var p2tPolygon = new Poly2Tri.Polygon(p2tPoints); // add holes foreach (var polygonHole in holes) { var p2tHolePoints = new List<Poly2Tri.PolygonPoint>(polygonHole.Points.Length); foreach (var polygonPoint in polygonHole.Points) { p2tHolePoints.Add(new Poly2Tri.PolygonPoint(polygonPoint.x, polygonPoint.y)); } p2tPolygon.AddHole(new Poly2Tri.Polygon(p2tHolePoints)); } try { Poly2Tri.P2T.Triangulate(p2tPolygon); } catch (Exception ex) { ExploderUtils.Log("P2T Exception: " + ex); return null; } var triangles = p2tPolygon.Triangles.Count; var indices = new List<int>(triangles*3); Points = new Vector2[triangles*3]; var j = 0; // recalc min max Min.x = float.MaxValue; Min.y = float.MaxValue; Max.x = float.MinValue; Max.y = float.MinValue; for (int i = 0; i < triangles; i++) { indices.Add((j + 0)); indices.Add((j + 1)); indices.Add((j + 2)); Points[j + 2].x = (float)p2tPolygon.Triangles[i].Points._0.X; Points[j + 2].y = (float)p2tPolygon.Triangles[i].Points._0.Y; Points[j + 1].x = (float)p2tPolygon.Triangles[i].Points._1.X; Points[j + 1].y = (float)p2tPolygon.Triangles[i].Points._1.Y; Points[j + 0].x = (float)p2tPolygon.Triangles[i].Points._2.X; Points[j + 0].y = (float)p2tPolygon.Triangles[i].Points._2.Y; // recalc min max for (int k = 0; k < 3; k++) { if (Points[j + k].x < Min.x) { Min.x = Points[j + k].x; } if (Points[j + k].y < Min.y) { Min.y = Points[j + k].y; } if (Points[j + k].x > Max.x) { Max.x = Points[j + k].x; } if (Points[j + k].y > Max.y) { Max.y = Points[j + k].y; } } j += 3; } return indices; } }
public static Mesh CreateXYMesh(Polygon polygon) { //AML Maybe pass this as arg and reduce coords to XY float z = polygon.outside[0].z; // Check for the easy case (a triangle) if (polygon.holes.Count == 0 && (polygon.outside.Count == 3 || (polygon.outside.Count == 4 && polygon.outside[3] == polygon.outside[0]))) { return(CreateTriangle(polygon)); } // Convert the outside points (throwing out Z at this point) Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ConvertPoints(polygon.outside)); // Convert each of the holes foreach (List <Vector3> hole in polygon.holes) { poly.AddHole(new Poly2Tri.Polygon(ConvertPoints(hole))); } // Triangulate it! Note that this may throw an exception if the data is bogus. try { DTSweepContext tcx = new DTSweepContext(); tcx.PrepareTriangulation(poly); DTSweep.Triangulate(tcx); tcx = null; } catch (System.Exception e) { //Profiler.Exit(profileID); throw e; } // Create the indices array Dictionary <uint, int> codeToIndex = new Dictionary <uint, int>(); List <Vector3> vertexList = new List <Vector3>(); foreach (DelaunayTriangle t in poly.Triangles) { foreach (var p in t.Points) { if (codeToIndex.ContainsKey(p.VertexCode)) { continue; } codeToIndex[p.VertexCode] = vertexList.Count; vertexList.Add(new Vector3(p.Xf, p.Yf, z)); } } int[] indices = new int[poly.Triangles.Count * 3]; { int i = 0; foreach (DelaunayTriangle t in poly.Triangles) { indices[i++] = codeToIndex[t.Points[0].VertexCode]; indices[i++] = codeToIndex[t.Points[1].VertexCode]; indices[i++] = codeToIndex[t.Points[2].VertexCode]; } } // Create the UV list, by looking up the closest point for each in our poly Vector2[] uv = null; if (polygon.outsideUVs != null) { uv = new Vector2[vertexList.Count]; for (int i = 0; i < vertexList.Count; i++) { uv[i] = polygon.ClosestUV(vertexList[i]); } } // Create the mesh Mesh msh = new Mesh(); msh.vertices = vertexList.ToArray(); msh.triangles = indices; msh.uv = uv; msh.RecalculateNormals(); msh.RecalculateBounds(); return(msh); }
private List<Polygon> TriangulatePolyTree(PolyTree tree) { List<Polygon> polygonsGenerated = new List<Polygon>(); List<PolygonPoint> AllPoints = new List<PolygonPoint>(); foreach (PolyNode islands in tree.Childs) { List<PolygonPoint> floorPoints = new List<PolygonPoint>(); foreach (IntPoint point in SubdividePolygon(islands.Contour)) { floorPoints.Add(new PolygonPoint(point.X / GenerationInformation.CalculationScaleFactor, point.Y / GenerationInformation.CalculationScaleFactor)); } AllPoints.AddRange(floorPoints); Polygon floorPolygon = new Polygon(floorPoints); foreach (PolyNode nextNode in islands.Childs) { if (nextNode.IsHole) { List<PolygonPoint> holePoints = new List<PolygonPoint>(); foreach (IntPoint point in SubdividePolygon(nextNode.Contour)) { holePoints.Add(new PolygonPoint(point.X / GenerationInformation.CalculationScaleFactor, point.Y / GenerationInformation.CalculationScaleFactor)); } AllPoints.AddRange(holePoints); floorPolygon.AddHole(new Polygon(holePoints)); } } if (GenerationInformation.UseGrid) { List<TriangulationPoint> steinerPoints = new List<TriangulationPoint>(); for (float x = (float)floorPolygon.BoundingBox.MinX - ((float)floorPolygon.BoundingBox.MinX % GenerationInformation.GridSize.x); x < floorPolygon.BoundingBox.MaxX; x += GenerationInformation.GridSize.x) { for (float y = (float)floorPolygon.BoundingBox.MinY - ((float)floorPolygon.BoundingBox.MinY % GenerationInformation.GridSize.y); y < floorPolygon.BoundingBox.MaxY; y += GenerationInformation.GridSize.y) { TriangulationPoint p = new TriangulationPoint(x, y); if (floorPolygon.IsPointInside(p)) steinerPoints.Add(p); } } CullSteinerPoints(ref steinerPoints, AllPoints, 0.1f); floorPolygon.AddSteinerPoints(steinerPoints); } floorPolygon.Prepare(P2T.CreateContext(TriangulationAlgorithm.DTSweep)); P2T.Triangulate(floorPolygon); polygonsGenerated.Add(floorPolygon); } return polygonsGenerated; }
protected virtual Polygon GetPolygon() { Polygon poly = new Polygon(GetPoints()); if (thickness > 0) { Polygon hole = new Polygon(GetPoints(thickness)); poly.AddHole(hole); } return poly; }
/// <summary> /// Create a Mesh from a given Polygon. /// </summary> /// <returns>The freshly minted mesh.</returns> /// <param name="polygon">Polygon you want to triangulate.</param> public static Mesh CreateMesh(Polygon polygon) { // Ensure we have the rotation properly calculated if (polygon.rotation == Quaternion.identity) { polygon.CalcRotation(); } // Rotate 1 point and note where it ends up in Z float z = (polygon.rotation * polygon.outside[0]).z; // Convert the outside points (throwing out Z at this point) Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ConvertPoints(polygon.outside, polygon.rotation)); // Convert each of the holes foreach (List <Vector3> hole in polygon.holes) { poly.AddHole(new Poly2Tri.Polygon(ConvertPoints(hole, polygon.rotation))); } // Triangulate it! Note that this may throw an exception if the data is bogus. DTSweepContext tcx = new DTSweepContext(); tcx.PrepareTriangulation(poly); DTSweep.Triangulate(tcx); tcx = null; // Create the Vector3 vertices (undoing the rotation), // and also build a map of vertex codes to indices Quaternion invRot = Quaternion.Inverse(polygon.rotation); Dictionary <uint, int> codeToIndex = new Dictionary <uint, int>(); List <Vector3> vertexList = new List <Vector3>(); foreach (DelaunayTriangle t in poly.Triangles) { foreach (var p in t.Points) { if (codeToIndex.ContainsKey(p.VertexCode)) { continue; } codeToIndex[p.VertexCode] = vertexList.Count; Vector3 pos = new Vector3(p.Xf, p.Yf, z); // (restore the Z we saved earlier) vertexList.Add(invRot * pos); } } // Create the indices array int[] indices = new int[poly.Triangles.Count * 3]; { int i = 0; foreach (DelaunayTriangle t in poly.Triangles) { indices[i++] = codeToIndex[t.Points[0].VertexCode]; indices[i++] = codeToIndex[t.Points[1].VertexCode]; indices[i++] = codeToIndex[t.Points[2].VertexCode]; } } // Create the UV list, by looking up the closest point for each in our poly Vector2[] uv = null; // if (polygon.outsideUVs != null) { // uv = new Vector2[vertexList.Count]; // for (int i=0; i<vertexList.Count; i++) { // uv[i] = polygon.ClosestUV(vertexList[i]); // } // } // Create the mesh Mesh msh = new Mesh(); msh.vertices = vertexList.ToArray(); msh.triangles = indices; msh.uv = uv; msh.RecalculateNormals(); msh.RecalculateBounds(); return(msh); }
public void UpdateNavigationMesh() { var visitedBlockedCells = new HashSet<NavigationGridletCell>(); var blobs = new List<List<NavigationGridletCell>>(); foreach (var cell in Cells) { if (!cell.Flags.HasFlag(CellFlags.Blocked)) { continue; } if (visitedBlockedCells.Contains(cell)) { continue; } var s = new Stack<NavigationGridletCell>(); s.Push(cell); var blob = new List<NavigationGridletCell>(); while (s.Any()) { var node = s.Pop(); blob.Add(node); visitedBlockedCells.Add(node); var derps = new List<NavigationGridletCell>(); if (node.X > 0) { derps.Add(node.Gridlet.Cells[node.Index - 1]); } if (node.X < node.Gridlet.XLength - 1) { derps.Add(node.Gridlet.Cells[node.Index + 1]); } if (node.Y > 0) { derps.Add(node.Gridlet.Cells[node.Index - node.Gridlet.XLength]); } if (node.Y < node.Gridlet.YLength - 1) { derps.Add(node.Gridlet.Cells[node.Index + node.Gridlet.XLength]); } var blockedNeighbors = derps.Where(n => n.Flags.HasFlag(CellFlags.Blocked)).ToArray(); blockedNeighbors.Where(n => !visitedBlockedCells.Contains(n)).ForEach(s.Push); } blobs.Add(blob); } // derp var cps = new Polygon(new List<PolygonPoint> { new PolygonPoint(-XLength / 2.0f - 5, -YLength / 2.0f - 5), new PolygonPoint(XLength / 2.0f + 5, -YLength / 2.0f - 5), new PolygonPoint(XLength / 2.0f + 5, YLength / 2.0f + 5), new PolygonPoint(-XLength / 2.0f - 5, YLength / 2.0f + 5) }); foreach (var blob in blobs) { var chull = GeometryUtilities.ConvexHull(blob.SelectMany(x => new[] { new ItzWarty.Geometry.Point2D(x.X, x.Y), new ItzWarty.Geometry.Point2D(x.X + 1, x.Y), new ItzWarty.Geometry.Point2D(x.X + 1, x.Y + 1), new ItzWarty.Geometry.Point2D(x.X, x.Y + 1) }).ToArray()); cps.AddHole( new Polygon( chull.Select(p => new PolygonPoint(p.X - XLength / 2.0f, p.Y - YLength / 2.0f)).ToArray() ) ); } var handledNeighborCells = new HashSet<NavigationGridletCell>(); foreach (var edgeCell in EdgeCells.Where(x => x.Flags.HasFlag(CellFlags.Connector))) { var thisObb = this.OrientedBoundingBox; foreach (var neighbor in edgeCell.Neighbors) { if (handledNeighborCells.Contains(neighbor)) { continue; } else { handledNeighborCells.Add(neighbor); } // var neighborObb = neighbor.OrientedBoundingBox; // var neighborToLocal = OrientedBoundingBox.GetBoxToBoxMatrix(ref thisObb, ref neighborObb); // Vector3 zero = new Vector3(0, 0, 0); // Vector3 neighborRelativePosition; // Vector3.Transform(ref zero, ref neighborToLocal, out neighborRelativePosition); // var p = new PolygonPoint((neighborRelativePosition.X), (neighborRelativePosition.Y)); // var q = new PolygonPoint((neighborRelativePosition.X + 0.05), (neighborRelativePosition.Y + 0.05)); // cps.AddConstraint(new TriangulationConstraint(p, q)); } // var p = new PolygonPoint((edgeCell.X + 0.45) * 0.998f - XLength / 2.0f, (edgeCell.Y + 0.45f) * 0.98f - YLength / 2.0f); // var q = new PolygonPoint((edgeCell.X + 0.55) * 0.999f - XLength / 2.0f, (edgeCell.Y + 0.55f) * 0.99f - YLength / 2.0f); // cps.AddConstraint(new TriangulationConstraint(p, q)); } // cps.AddHole( // new List<TriangulationPoint> { // new TriangulationPoint(-XLength / 2 + 2, -YLength / 2 + 2), // new TriangulationPoint(XLength / 2 - 2, -YLength / 2 + 2), // new TriangulationPoint(XLength / 2 - 2, YLength / 2 - 2), // new TriangulationPoint(-XLength / 2 + 2, YLength / 2 - 2) // }, "hole"); P2T.Triangulate(cps); Console.WriteLine(cps.Triangles.Count); Mesh = cps.Triangles; // for (var x = 0; x < XLength; x++) { // for (var y = 0; y < YLength; y++) { // var cellIndex = x + y * XLength; // if (Cells[cellIndex].Flags.HasFlag(CellFlags.Connector)) { // // } // } // } }
public IEnumerable <MapObject> Create(IDGenerator generator, Box box, ITexture texture, int roundDecimals) { var width = box.Width; var length = Math.Max(1, Math.Abs((int)box.Length)); var height = box.Height; var flatten = (float)_flattenFactor.Value; var text = _text.GetValue(); var family = _fontChooser.GetFontFamily(); var style = Enum.GetValues(typeof(FontStyle)).OfType <FontStyle>().FirstOrDefault(fs => family.IsStyleAvailable(fs)); if (!family.IsStyleAvailable(style)) { family = FontFamily.GenericSansSerif; } var set = new PolygonSet(); var sizes = new List <RectangleF>(); using (var bmp = new Bitmap(1, 1)) { using (var g = System.Drawing.Graphics.FromImage(bmp)) { using (var font = new Font(family, length, style, GraphicsUnit.Pixel)) { for (var i = 0; i < text.Length; i += 32) { using (var sf = new StringFormat(StringFormat.GenericTypographic)) { var rem = Math.Min(text.Length, i + 32) - i; var range = Enumerable.Range(0, rem).Select(x => new CharacterRange(x, 1)).ToArray(); sf.SetMeasurableCharacterRanges(range); var reg = g.MeasureCharacterRanges(text.Substring(i, rem), font, new RectangleF(0, 0, float.MaxValue, float.MaxValue), sf); sizes.AddRange(reg.Select(x => x.GetBounds(g))); } } } } } var xOffset = box.Start.DX; var yOffset = box.End.DY; for (var ci = 0; ci < text.Length; ci++) { var c = text[ci]; var size = sizes[ci]; var gp = new GraphicsPath(); gp.AddString(c.ToString(CultureInfo.InvariantCulture), family, (int)style, length, new PointF(0, 0), StringFormat.GenericTypographic); gp.Flatten(new System.Drawing.Drawing2D.Matrix(), flatten); var polygons = new List <Polygon>(); var poly = new List <PolygonPoint>(); for (var i = 0; i < gp.PointCount; i++) { var type = gp.PathTypes[i]; var point = gp.PathPoints[i]; poly.Add(new PolygonPoint(point.X + xOffset, -point.Y + yOffset)); if ((type & 0x80) == 0x80) { polygons.Add(new Polygon(poly)); poly.Clear(); } } var tri = new List <Polygon>(); Polygon polygon = null; foreach (var p in polygons) { if (polygon == null) { polygon = p; tri.Add(p); } else if (p.CalculateWindingOrder() != polygon.CalculateWindingOrder()) { polygon.AddHole(p); } else { polygon = null; tri.Add(p); } } foreach (var pp in tri) { try { P2T.Triangulate(pp); set.Add(pp); } catch { // Ignore } } xOffset += size.Width; } var zOffset = box.Start.Z; foreach (var polygon in set.Polygons) { foreach (var t in polygon.Triangles) { var points = t.Points.Select(x => new Coordinate((decimal)x.X, (decimal)x.Y, zOffset).Round(roundDecimals)).ToList(); var faces = new List <Coordinate[]>(); // Add the vertical faces var z = new Coordinate(0, 0, height).Round(roundDecimals); for (var j = 0; j < points.Count; j++) { var next = (j + 1) % points.Count; faces.Add(new[] { points[j], points[j] + z, points[next] + z, points[next] }); } // Add the top and bottom faces faces.Add(points.ToArray()); faces.Add(points.Select(x => x + z).Reverse().ToArray()); // Nothing new here, move along var solid = new Solid(generator.GetNextObjectID()) { Colour = Colour.GetRandomBrushColour() }; foreach (var arr in faces) { var face = new Face(generator.GetNextFaceID()) { Parent = solid, Plane = new Plane(arr[0], arr[1], arr[2]), Colour = solid.Colour, Texture = { Texture = texture } }; face.Vertices.AddRange(arr.Select(x => new Vertex(x, face))); face.UpdateBoundingBox(); face.AlignTextureToFace(); solid.Faces.Add(face); } solid.UpdateBoundingBox(); yield return(solid); } } }
/// <summary> /// triangulate polygon, algorithm from wiki is fast enough /// http://wiki.unity3d.com/index.php?title=Triangulator /// </summary> /// <returns></returns> public List <int> Triangulate() { // no holes supported if (holes.Count == 0) { var indices = new List <int>(Points.Length); int n = Points.Length; if (n < 3) { return(indices); } var V = new int[n]; if (Area > 0) { for (int v = 0; v < n; v++) { V[v] = v; } } else { for (int v = 0; v < n; v++) { V[v] = (n - 1) - v; } } int nv = n; int count = 2 * nv; for (int m = 0, v = nv - 1; nv > 2;) { if ((count--) <= 0) { return(indices); } int u = v; if (nv <= u) { u = 0; } v = u + 1; if (nv <= v) { v = 0; } int w = v + 1; if (nv <= w) { w = 0; } if (Snip(u, v, w, nv, V)) { int a, b, c, s, t; a = V[u]; b = V[v]; c = V[w]; indices.Add(a); indices.Add(b); indices.Add(c); m++; for (s = v, t = v + 1; t < nv; s++, t++) { V[s] = V[t]; } nv--; count = 2 * nv; } } indices.Reverse(); return(indices); } else { // use poly2tri library to triangulate mesh with holes var p2tPoints = new List <Poly2Tri.PolygonPoint>(Points.Length); foreach (var point in Points) { p2tPoints.Add(new Poly2Tri.PolygonPoint(point.x, point.y)); } // create p2t polygon var p2tPolygon = new Poly2Tri.Polygon(p2tPoints); // add holes foreach (var polygonHole in holes) { var p2tHolePoints = new List <Poly2Tri.PolygonPoint>(polygonHole.Points.Length); foreach (var polygonPoint in polygonHole.Points) { p2tHolePoints.Add(new Poly2Tri.PolygonPoint(polygonPoint.x, polygonPoint.y)); } p2tPolygon.AddHole(new Poly2Tri.Polygon(p2tHolePoints)); } try { Poly2Tri.P2T.Triangulate(p2tPolygon); } catch (Exception ex) { ExploderUtils.Log("P2T Exception: " + ex); return(null); } var triangles = p2tPolygon.Triangles.Count; var indices = new List <int>(triangles * 3); Points = new Vector2[triangles * 3]; var j = 0; // recalc min max Min.x = float.MaxValue; Min.y = float.MaxValue; Max.x = float.MinValue; Max.y = float.MinValue; for (int i = 0; i < triangles; i++) { indices.Add((j + 0)); indices.Add((j + 1)); indices.Add((j + 2)); Points[j + 2].x = (float)p2tPolygon.Triangles[i].Points._0.X; Points[j + 2].y = (float)p2tPolygon.Triangles[i].Points._0.Y; Points[j + 1].x = (float)p2tPolygon.Triangles[i].Points._1.X; Points[j + 1].y = (float)p2tPolygon.Triangles[i].Points._1.Y; Points[j + 0].x = (float)p2tPolygon.Triangles[i].Points._2.X; Points[j + 0].y = (float)p2tPolygon.Triangles[i].Points._2.Y; // recalc min max for (int k = 0; k < 3; k++) { if (Points[j + k].x < Min.x) { Min.x = Points[j + k].x; } if (Points[j + k].y < Min.y) { Min.y = Points[j + k].y; } if (Points[j + k].x > Max.x) { Max.x = Points[j + k].x; } if (Points[j + k].y > Max.y) { Max.y = Points[j + k].y; } } j += 3; } return(indices); } }
/// <summary> /// Create a Mesh from a given Polygon. /// </summary> /// <returns>The freshly minted mesh.</returns> /// <param name="polygon">Polygon you want to triangulate.</param> public static Mesh CreateMesh(Polygon polygon) { // Check for the easy case (a triangle) if (polygon.holes.Count == 0 && (polygon.outside.Count == 3 || (polygon.outside.Count == 4 && polygon.outside[3] == polygon.outside[0]))) { return(CreateTriangle(polygon)); } // Ensure we have the rotation properly calculated, and have a valid normal if (polygon.rotation == Quaternion.identity) { polygon.CalcRotation(); } if (polygon.planeNormal == Vector3.zero) { return(null); // bad data } // Rotate 1 point and note where it ends up in Z float z = (polygon.rotation * polygon.outside[0]).z; // Prepare a map from vertex codes to 3D positions. Dictionary <uint, Vector3> codeToPosition = new Dictionary <uint, Vector3>(); // Convert the outside points (throwing out Z at this point) Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ConvertPoints(polygon.outside, polygon.rotation, codeToPosition)); // Convert each of the holes foreach (List <Vector3> hole in polygon.holes) { poly.AddHole(new Poly2Tri.Polygon(ConvertPoints(hole, polygon.rotation, codeToPosition))); } // Triangulate it! Note that this may throw an exception if the data is bogus. try { DTSweepContext tcx = new DTSweepContext(); tcx.PrepareTriangulation(poly); DTSweep.Triangulate(tcx); tcx = null; } catch (System.Exception e) { throw e; } // Now, to get back to our original positions, use our code-to-position map. We do // this instead of un-rotating to be a little more robust about noncoplanar polygons. // Create the Vector3 vertices (undoing the rotation), // and also build a map of vertex codes to indices Quaternion? invRot = null; Dictionary <uint, int> codeToIndex = new Dictionary <uint, int>(); List <Vector3> vertexList = new List <Vector3>(); foreach (DelaunayTriangle t in poly.Triangles) { foreach (var p in t.Points) { if (codeToIndex.ContainsKey(p.VertexCode)) { continue; } codeToIndex[p.VertexCode] = vertexList.Count; Vector3 pos; if (!codeToPosition.TryGetValue(p.VertexCode, out pos)) { // This can happen in rare cases when we're hitting limits of floating-point precision. // Rather than fail, let's just do the inverse rotation. if (!invRot.HasValue) { invRot = Quaternion.Inverse(polygon.rotation); } pos = invRot.Value * new Vector3(p.Xf, p.Yf, z); } vertexList.Add(pos); } } // Create the indices array int[] indices = new int[poly.Triangles.Count * 3]; { int i = 0; foreach (DelaunayTriangle t in poly.Triangles) { indices[i++] = codeToIndex[t.Points[0].VertexCode]; indices[i++] = codeToIndex[t.Points[1].VertexCode]; indices[i++] = codeToIndex[t.Points[2].VertexCode]; } } // Create the UV list, by looking up the closest point for each in our poly Vector2[] uv = null; if (polygon.outsideUVs != null) { uv = new Vector2[vertexList.Count]; for (int i = 0; i < vertexList.Count; i++) { uv[i] = polygon.ClosestUV(vertexList[i]); } } // Create the mesh Mesh msh = new Mesh(); msh.vertices = vertexList.ToArray(); msh.triangles = indices; msh.uv = uv; msh.RecalculateNormals(); msh.RecalculateBounds(); return(msh); }
/// <summary> /// Create a Mesh from a given Polygon. /// </summary> /// <returns>The freshly minted mesh.</returns> /// <param name="polygon">Polygon you want to triangulate.</param> public static bool CreateMesh(List <Poly2Mesh.Polygon> polygons, ref Mesh dstMesh) { // TODO: use vertex indices instead of actual vertices to find original vertices var poly2TriPolygons = new List <Poly2Tri.Polygon>(); var codeToPositions = new List <Dictionary <uint, Vector3> >(); var zs = new List <float>(); // Ensure we have the rotation properly calculated, and have a valid normal for (int p = 0; p < polygons.Count; p++) { if (polygons[p].rotation == Quaternion.identity) { polygons[p].CalcRotation(); } if (polygons[p].planeNormal == Vector3.zero) { Debug.Log("polygons[p].planeNormal == Vector3.zero"); return(false); // bad data } // Rotate 1 point and note where it ends up in Z float z = (polygons[p].rotation * polygons[p].outside[0]).z; // Prepare a map from vertex codes to 3D positions. Dictionary <uint, Vector3> codeToPosition = new Dictionary <uint, Vector3>(); // Convert the outside points (throwing out Z at this point) Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ConvertPoints(polygons[p].outside, polygons[p].rotation, codeToPosition)); // Convert each of the holes if (polygons[p].holes != null) { foreach (List <Vector3> hole in polygons[p].holes) { poly.AddHole(new Poly2Tri.Polygon(ConvertPoints(hole, polygons[p].rotation, codeToPosition))); } } codeToPositions.Add(codeToPosition); poly2TriPolygons.Add(poly); zs.Add(z); } // Triangulate it! Note that this may throw an exception if the data is bogus. for (int p = 0; p < poly2TriPolygons.Count; p++) { try { DTSweepContext tcx = new DTSweepContext(); tcx.PrepareTriangulation(poly2TriPolygons[p]); DTSweep.Triangulate(tcx); tcx = null; } catch (System.Exception e) { //Profiler.Exit(profileID); Debug.LogException(e); //throw e; } } // Now, to get back to our original positions, use our code-to-position map. We do // this instead of un-rotating to be a little more robust about noncoplanar polygons. // Create the Vector3 vertices (undoing the rotation), // and also build a map of vertex codes to indices Quaternion? invRot = null; Dictionary <uint, int> codeToIndex = new Dictionary <uint, int>(); List <Vector3> vertexList = new List <Vector3>(); List <int> indexList = new List <int>(); int triangleCount = 0; for (int p = 0; p < polygons.Count; p++) { var poly = poly2TriPolygons[p]; var polygon = polygons[p]; var z = zs[p]; var codeToPosition = codeToPositions[p]; triangleCount += poly.Triangles.Count; codeToIndex.Clear(); foreach (DelaunayTriangle t in poly.Triangles) { foreach (var point in t.Points) { if (codeToIndex.ContainsKey(point.VertexCode)) { continue; } codeToIndex[point.VertexCode] = vertexList.Count; Vector3 pos; if (!codeToPosition.TryGetValue(point.VertexCode, out pos)) { // This can happen in rare cases when we're hitting limits of floating-point precision. // Rather than fail, let's just do the inverse rotation. Debug.LogWarning("Vertex code lookup failed; using inverse rotation."); if (!invRot.HasValue) { invRot = Quaternion.Inverse(polygon.rotation); } pos = invRot.Value * new Vector3(point.Xf, point.Yf, z); } vertexList.Add(pos); } } if (polygon.inverse) { foreach (DelaunayTriangle t in poly.Triangles) { indexList.Add(codeToIndex[t.Points[2].VertexCode]); indexList.Add(codeToIndex[t.Points[1].VertexCode]); indexList.Add(codeToIndex[t.Points[0].VertexCode]); } } else { foreach (DelaunayTriangle t in poly.Triangles) { indexList.Add(codeToIndex[t.Points[0].VertexCode]); indexList.Add(codeToIndex[t.Points[1].VertexCode]); indexList.Add(codeToIndex[t.Points[2].VertexCode]); } } } // Create the indices array var indices = indexList.ToArray(); // Create the mesh dstMesh.vertices = vertexList.ToArray(); dstMesh.triangles = indices; dstMesh.RecalculateNormals(); return(true); }