Example #1
0
        public static ClipperLib.IntPoint[] ScaleUpPaths(SvgPoint[] points, double scale = 1)
        {
            var result = new ClipperLib.IntPoint[points.Length];

            Parallel.For(0, points.Length, i => result[i] = new ClipperLib.IntPoint((long)Math.Round((decimal)points[i].x * (decimal)scale), (long)Math.Round((decimal)points[i].y * (decimal)scale)));

            return(result.ToArray());
        } // 2 secs
        private XElement CreateCollisionElementForLayer(TmxLayer layer)
        {
            // Collision elements look like this
            // (Can also have EdgeCollider2Ds)
            //      <GameOject name="Collision">
            //        <PolygonCollider2D>
            //          <Path>list of points</Path>
            //          <Path>another list of points</Path>
            //        </PolygonCollider2D>
            //      </GameOject>

            LayerClipper.TransformPointFunc xfFunc =
                delegate(float x, float y)
            {
                // Transform point to Unity space
                PointF pointUnity3d       = PointFToUnityVector_NoScale(new PointF(x, y));
                ClipperLib.IntPoint point = new ClipperLib.IntPoint(pointUnity3d.X, pointUnity3d.Y);
                return(point);
            };

            LayerClipper.ProgressFunc progFunc =
                delegate(string prog)
            {
                Program.WriteLine(prog);
            };

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            // Add our polygon and edge colliders
            List <XElement> polyColliderElements = new List <XElement>();

            if (layer.IsExportingConvexPolygons())
            {
                AddPolygonCollider2DElements_Convex(solution, polyColliderElements);
            }
            else
            {
                AddPolygonCollider2DElements_Complex(solution, polyColliderElements);
            }

            AddEdgeCollider2DElements(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), polyColliderElements);

            if (polyColliderElements.Count() == 0)
            {
                // No collisions on this layer
                return(null);
            }

            XElement gameObjectCollision =
                new XElement("GameObject",
                             new XAttribute("name", "Collision"),
                             polyColliderElements);

            return(gameObjectCollision);
        }
        private XElement CreateCollisionElementForLayer(TmxLayer layer)
        {
            // Collision elements look like this
            // (Can also have EdgeCollider2Ds)
            //      <GameOject name="Collision">
            //        <PolygonCollider2D>
            //          <Path>list of points</Path>
            //          <Path>another list of points</Path>
            //        </PolygonCollider2D>
            //      </GameOject>

            LayerClipper.TransformPointFunc xfFunc =
                delegate(float x, float y)
                {
                    // Transform point to Unity space
                    PointF pointUnity3d = PointFToUnityVector_NoScale(new PointF(x, y));
                    ClipperLib.IntPoint point = new ClipperLib.IntPoint(pointUnity3d.X, pointUnity3d.Y);
                    return point;
                };

            LayerClipper.ProgressFunc progFunc =
                delegate(string prog)
                {
                    Program.WriteLine(prog);
                };

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            // Add our polygon and edge colliders
            List<XElement> polyColliderElements = new List<XElement>();
            AddPolygonCollider2DElements(ClipperLib.Clipper.ClosedPathsFromPolyTree(solution), polyColliderElements);
            AddEdgeCollider2DElements(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), polyColliderElements);

            if (polyColliderElements.Count() == 0)
            {
                // No collisions on this layer
                return null;
            }

            XElement gameObjectCollision =
                new XElement("GameObject",
                    new XAttribute("name", "Collision"),
                    polyColliderElements);

            return gameObjectCollision;
        }
Example #4
0
        public static ClipperLib.PolyTree ExecuteClipper(TmxMap tmxMap, TmxLayer tmxLayer, TransformPointFunc xfFunc, ProgressFunc progFunc)
        {
            // The "fullClipper" combines the clipper results from the smaller pieces
            ClipperLib.Clipper fullClipper = new ClipperLib.Clipper();

            // From the perspective of Clipper lines are polygons too
            // Closed paths == polygons
            // Open paths == lines
            var polygonGroups = from y in Enumerable.Range(0, tmxLayer.Height)
                                from x in Enumerable.Range(0, tmxLayer.Width)
                                let rawTileId = tmxLayer.GetRawTileIdAt(x, y)
                                                let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
                                                             where tileId != 0
                                                             let tile = tmxMap.Tiles[tileId]
                                                                        from polygon in tile.ObjectGroup.Objects
                                                                        where (polygon as TmxHasPoints) != null
                                                                        let groupX = x / LayerClipper.GroupBySize
                                                                                     let groupY = y / LayerClipper.GroupBySize
                                                                                                  group new
            {
                PositionOnMap         = tmxMap.GetMapPositionAt(x, y, tile),
                HasPointsInterface    = polygon as TmxHasPoints,
                TmxObjectInterface    = polygon,
                IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId),
                IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId),
                TileCenter            = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f),
            }
            by Tuple.Create(groupX, groupY);

            int groupIndex = 0;
            int groupCount = polygonGroups.Count();

            foreach (var polyGroup in polygonGroups)
            {
                if (groupIndex % 5 == 0)
                {
                    progFunc(String.Format("Clipping '{0}' polygons: {1}%", tmxLayer.Name, (groupIndex / (float)groupCount) * 100));
                }
                groupIndex++;

                // The "groupClipper" clips the polygons in a smaller part of the world
                ClipperLib.Clipper groupClipper = new ClipperLib.Clipper();

                // Add all our polygons to the Clipper library so it can reduce all the polygons to a (hopefully small) number of paths
                foreach (var poly in polyGroup)
                {
                    // Create a clipper library polygon out of each and add it to our collection
                    ClipperPolygon clipperPolygon = new ClipperPolygon();

                    // Our points may be transformed due to tile flipping/rotation
                    // Before we transform them we put all the points into local space relative to the tile
                    SizeF    offset            = new SizeF(poly.TmxObjectInterface.Position);
                    PointF[] transformedPoints = poly.HasPointsInterface.Points.Select(pt => PointF.Add(pt, offset)).ToArray();

                    // Now transform the points relative to the tile
                    TmxMath.TransformPoints(transformedPoints, poly.TileCenter, poly.IsFlippedDiagnoally, poly.IsFlippedHorizontally, poly.IsFlippedVertically);

                    foreach (var pt in transformedPoints)
                    {
                        float x = poly.PositionOnMap.X + pt.X;
                        float y = poly.PositionOnMap.Y + pt.Y;

                        ClipperLib.IntPoint point = xfFunc(x, y);
                        clipperPolygon.Add(point);
                    }

                    // Because of Unity's cooridnate system, the winding order of the polygons must be reversed
                    clipperPolygon.Reverse();

                    // Add the "subject"
                    groupClipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed());
                }

                // Get a solution for this group
                ClipperLib.PolyTree solution = new ClipperLib.PolyTree();
                groupClipper.Execute(ClipperLib.ClipType.ctUnion, solution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

                // Combine the solutions into the full clipper
                fullClipper.AddPaths(ClipperLib.Clipper.ClosedPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, true);
                fullClipper.AddPaths(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, false);
            }
            progFunc(String.Format("Clipping '{0}' polygons: 100%", tmxLayer.Name));

            ClipperLib.PolyTree fullSolution = new ClipperLib.PolyTree();
            fullClipper.Execute(ClipperLib.ClipType.ctUnion, fullSolution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

            return(fullSolution);
        }
        private XElement CreateCollisionElementForLayer(TmxLayer layer)
        {
            // Collision elements look like this
            // (Can also have EdgeCollider2Ds)
            //      <GameOject name="Collision">
            //        <PolygonCollider2D>
            //          <Path>list of points</Path>
            //          <Path>another list of points</Path>
            //        </PolygonCollider2D>
            //      </GameOject>

            LayerClipper.TransformPointFunc xfFunc =
                delegate(float x, float y)
            {
                // Transform point to Unity space
                PointF pointUnity3d       = PointFToUnityVector_NoScale(new PointF(x, y));
                ClipperLib.IntPoint point = new ClipperLib.IntPoint(pointUnity3d.X, pointUnity3d.Y);
                return(point);
            };

            LayerClipper.ProgressFunc progFunc =
                delegate(string prog)
            {
                Logger.WriteLine(prog);
            };

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            var paths = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution);

            if (paths.Count >= MaxNumberOfSafePaths)
            {
                StringBuilder warning = new StringBuilder();
                warning.AppendFormat("Layer '{0}' has a large number of polygon paths ({1}).", layer.Name, paths.Count);
                warning.AppendLine("  Importing this layer may be slow in Unity. (Can take an hour or more for +1000 paths.)");
                warning.AppendLine("  Check polygon/rectangle objects in Tile Collision Editor in Tiled and use 'Snap to Grid' or 'Snap to Fine Grid'.");
                warning.AppendLine("  You want colliders to be set up so they can be merged with colliders on neighboring tiles, reducing path count considerably.");
                warning.AppendLine("  In some cases the size of the map may need to be reduced.");
                Logger.WriteWarning(warning.ToString());
            }

            // Add our polygon and edge colliders
            List <XElement> polyColliderElements = new List <XElement>();

            if (layer.IsExportingConvexPolygons())
            {
                AddPolygonCollider2DElements_Convex(solution, polyColliderElements);
            }
            else
            {
                AddPolygonCollider2DElements_Complex(solution, polyColliderElements);
            }

            AddEdgeCollider2DElements(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), polyColliderElements);

            if (polyColliderElements.Count() == 0)
            {
                // No collisions on this layer
                return(null);
            }

            XElement gameObjectCollision =
                new XElement("GameObject",
                             new XAttribute("name", "Collision"),
                             polyColliderElements);

            // Collision layer may have a name and "unity physics layer" to go with it
            // (But not if we're using unity:layer override)
            if (String.IsNullOrEmpty(layer.UnityLayerOverrideName) && !String.IsNullOrEmpty(layer.Name))
            {
                gameObjectCollision.SetAttributeValue("name", "Collision_" + layer.Name);
                gameObjectCollision.SetAttributeValue("layer", layer.Name);
            }

            return(gameObjectCollision);
        }
        private XElement CreateCollisionElementForLayer(TmxLayer layer)
        {
            // Collision elements look like this
            // (Can also have EdgeCollider2Ds)
            //      <GameOject name="Collision">
            //        <PolygonCollider2D>
            //          <Path>list of points</Path>
            //          <Path>another list of points</Path>
            //        </PolygonCollider2D>
            //      </GameOject>

            LayerClipper.TransformPointFunc xfFunc =
                delegate(float x, float y)
                {
                    // Transform point to Unity space
                    PointF pointUnity3d = PointFToUnityVector_NoScale(new PointF(x, y));
                    ClipperLib.IntPoint point = new ClipperLib.IntPoint(pointUnity3d.X, pointUnity3d.Y);
                    return point;
                };

            LayerClipper.ProgressFunc progFunc =
                delegate(string prog)
                {
                    Program.WriteLine(prog);
                };

            ClipperLib.PolyTree solution = LayerClipper.ExecuteClipper(this.tmxMap, layer, xfFunc, progFunc);

            var paths = ClipperLib.Clipper.ClosedPathsFromPolyTree(solution);
            if (paths.Count >= MaxNumberOfSafePaths)
            {
                StringBuilder warning = new StringBuilder();
                warning.AppendFormat("Layer '{0}' has a large number of polygon paths ({1}).", layer.Name, paths.Count);
                warning.AppendLine("  Importing this layer may be slow in Unity. (Can take an hour or more for +1000 paths.)");
                warning.AppendLine("  Check polygon/rectangle objects in Tile Collision Editor in Tiled and use 'Snap to Grid' or 'Snap to Fine Grid'.");
                warning.AppendLine("  You want colliders to be set up so they can be merged with colliders on neighboring tiles, reducing path count considerably.");
                warning.AppendLine("  In some cases the size of the map may need to be reduced.");
                Program.WriteWarning(warning.ToString());
            }

            // Add our polygon and edge colliders
            List<XElement> polyColliderElements = new List<XElement>();

            if (layer.IsExportingConvexPolygons())
            {
                AddPolygonCollider2DElements_Convex(solution, polyColliderElements);
            }
            else
            {
                AddPolygonCollider2DElements_Complex(solution, polyColliderElements);
            }

            AddEdgeCollider2DElements(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), polyColliderElements);

            if (polyColliderElements.Count() == 0)
            {
                // No collisions on this layer
                return null;
            }

            XElement gameObjectCollision =
                new XElement("GameObject",
                    new XAttribute("name", "Collision"),
                    polyColliderElements);

            // Collision layer may have a name and "unity physics layer" to go with it
            // (But not if we're using unity:layer override)
            if (String.IsNullOrEmpty(layer.UnityLayerOverrideName) && !String.IsNullOrEmpty(layer.Name))
            {
                gameObjectCollision.SetAttributeValue("name", "Collision_" + layer.Name);
                gameObjectCollision.SetAttributeValue("layer", layer.Name);
            }

            return gameObjectCollision;
        }
 public static Vector2 clipperToUnity(ClipperLib.IntPoint pt)
 {
     return(new Vector2(pt.X / (float)scalar, pt.Y / (float)scalar));
 }
        public const long scalar = 10000000; //Value by which to scale integers to represent their floating point counterparts, aiming to preserve 7 decimal points of precision (Unity's float precision)

        public static Poly2Tri.Point2D clipperToPoly2tri(ClipperLib.IntPoint pt)
        {
            return(new Poly2Tri.Point2D(pt.X / (double)scalar, pt.Y / (double)scalar));
        }
Example #9
0
 public static LibTessDotNet.Vec3 ToVectorTess(this ClipperLib.IntPoint point)
 {
     return(new LibTessDotNet.Vec3 {
         X = point.X, Y = point.Y, Z = 0
     });
 }
Example #10
0
 public static Vector2 ToVector2D(this ClipperLib.IntPoint point)
 {
     return(new Vector2(point.X / INT_SCALE, point.Y / INT_SCALE));
 }
Example #11
0
        public static ClipperLib.PolyTree ExecuteClipper(TmxMap tmxMap, TmxLayer tmxLayer, TransformPointFunc xfFunc, ProgressFunc progFunc)
        {
            // The "fullClipper" combines the clipper results from the smaller pieces
            ClipperLib.Clipper fullClipper = new ClipperLib.Clipper();

            // Limit to polygon "type" that matches the collision layer name (unless we are overriding the whole layer to a specific Unity Layer Name)
            bool usingUnityLayerOverride = !String.IsNullOrEmpty(tmxLayer.UnityLayerOverrideName);

            // From the perspective of Clipper lines are polygons too
            // Closed paths == polygons
            // Open paths == lines
            Dictionary <TupleInt2, List <PolygonGroup> > polygonGroups = new Dictionary <TupleInt2, List <PolygonGroup> >();

            foreach (int y in Enumerable.Range(0, tmxLayer.Height))
            {
                foreach (int x in Enumerable.Range(0, tmxLayer.Width))
                {
                    uint rawTileId = tmxLayer.GetRawTileIdAt(x, y);
                    if (rawTileId == 0)
                    {
                        continue;
                    }

                    uint    tileId = TmxMath.GetTileIdWithoutFlags(rawTileId);
                    TmxTile tile   = tmxMap.Tiles[tileId];

                    foreach (TmxObject polygon in tile.ObjectGroup.Objects)
                    {
                        if (typeof(TmxHasPoints).IsAssignableFrom(polygon.GetType()) &&
                            (usingUnityLayerOverride || String.Compare(polygon.Type, tmxLayer.Name, true) == 0))
                        {
                            int groupX = x / LayerClipper.GroupBySize;
                            int groupY = y / LayerClipper.GroupBySize;

                            PolygonGroup poly = new PolygonGroup();
                            poly.PositionOnMap         = tmxMap.GetMapPositionAt(x, y, tile);
                            poly.HasPointsInterface    = polygon as TmxHasPoints;
                            poly.TmxObjectInterface    = polygon;
                            poly.IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId);
                            poly.IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId);
                            poly.IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId);
                            poly.TileCenter            = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f);

                            TupleInt2 key = new TupleInt2(groupX, groupY);
                            if (!polygonGroups.ContainsKey(key))
                            {
                                polygonGroups[key] = new List <PolygonGroup>();
                            }
                            polygonGroups[key].Add(poly);
                        }
                    }
                }
            }
            // Tuple not supported in Mono 2.0 so doing this the old fashioned way, sorry
            //var polygonGroups = from y in Enumerable.Range(0, tmxLayer.Height)
            //                    from x in Enumerable.Range(0, tmxLayer.Width)
            //                    let rawTileId = tmxLayer.GetRawTileIdAt(x, y)
            //                    where rawTileId != 0
            //                    let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
            //                    let tile = tmxMap.Tiles[tileId]
            //                    from polygon in tile.ObjectGroup.Objects
            //                    where (polygon as TmxHasPoints) != null
            //                    where  usingUnityLayerOverride || String.Compare(polygon.Type, tmxLayer.Name, true) == 0
            //                    let groupX = x / LayerClipper.GroupBySize
            //                    let groupY = y / LayerClipper.GroupBySize
            //                    group new
            //                    {
            //                        PositionOnMap = tmxMap.GetMapPositionAt(x, y, tile),
            //                        HasPointsInterface = polygon as TmxHasPoints,
            //                        TmxObjectInterface = polygon,
            //                        IsFlippedDiagnoally = TmxMath.IsTileFlippedDiagonally(rawTileId),
            //                        IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
            //                        IsFlippedVertically = TmxMath.IsTileFlippedVertically(rawTileId),
            //                        TileCenter = new PointF(tile.TileSize.Width * 0.5f, tile.TileSize.Height * 0.5f),
            //                    }
            //                    by Tuple.Create(groupX, groupY);

            int groupIndex = 0;
            int groupCount = polygonGroups.Count();

            foreach (TupleInt2 key in polygonGroups.Keys)
            {
                if (groupIndex % 5 == 0)
                {
                    progFunc(String.Format("Clipping '{0}' polygons: {1}%", tmxLayer.Name, (groupIndex / (float)groupCount) * 100));
                }
                groupIndex++;

                // The "groupClipper" clips the polygons in a smaller part of the world
                ClipperLib.Clipper groupClipper = new ClipperLib.Clipper();

                // Add all our polygons to the Clipper library so it can reduce all the polygons to a (hopefully small) number of paths
                foreach (PolygonGroup poly in polygonGroups[key])
                {
                    // Create a clipper library polygon out of each and add it to our collection
                    ClipperPolygon clipperPolygon = new ClipperPolygon();

                    // Our points may be transformed due to tile flipping/rotation
                    // Before we transform them we put all the points into local space relative to the tile
                    SizeF    offset            = new SizeF(poly.TmxObjectInterface.Position);
                    PointF[] transformedPoints = poly.HasPointsInterface.Points.Select(pt => PointF.Add(pt, offset)).ToArray();

                    // Now transform the points relative to the tile
                    TmxMath.TransformPoints(transformedPoints, poly.TileCenter, poly.IsFlippedDiagnoally, poly.IsFlippedHorizontally, poly.IsFlippedVertically);

                    foreach (var pt in transformedPoints)
                    {
                        float x = poly.PositionOnMap.X + pt.X;
                        float y = poly.PositionOnMap.Y + pt.Y;

                        ClipperLib.IntPoint point = xfFunc(x, y);
                        clipperPolygon.Add(point);
                    }

                    // Because of Unity's cooridnate system, the winding order of the polygons must be reversed
                    clipperPolygon.Reverse();

                    // Add the "subject"
                    groupClipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed());
                }

                // Get a solution for this group
                ClipperLib.PolyTree solution = new ClipperLib.PolyTree();
                groupClipper.Execute(ClipperLib.ClipType.ctUnion, solution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

                // Combine the solutions into the full clipper
                fullClipper.AddPaths(ClipperLib.Clipper.ClosedPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, true);
                fullClipper.AddPaths(ClipperLib.Clipper.OpenPathsFromPolyTree(solution), ClipperLib.PolyType.ptSubject, false);
            }
            progFunc(String.Format("Clipping '{0}' polygons: 100%", tmxLayer.Name));

            ClipperLib.PolyTree fullSolution = new ClipperLib.PolyTree();
            fullClipper.Execute(ClipperLib.ClipType.ctUnion, fullSolution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);

            return(fullSolution);
        }