private StringWriter BuildWavefrontStringForLayerMesh(TmxLayer layer, TmxMesh mesh, IEnumerable <int> horizontalRange, IEnumerable <int> verticalRange)
        {
            Logger.WriteVerbose("Building mesh obj file for '{0}'", mesh.UniqueMeshName);
            GenericListDatabase <Vertex3> vertexDatabase = new GenericListDatabase <Vertex3>();
            HashIndexOf <PointF>          uvDatabase     = new HashIndexOf <PointF>();
            StringBuilder faces = new StringBuilder();

            foreach (int y in verticalRange)
            {
                foreach (int x in horizontalRange)
                {
                    int  tileIndex = layer.GetTileIndex(x, y);
                    uint tileId    = mesh.GetTileIdAt(tileIndex);

                    // Skip blank tiles
                    if (tileId == 0)
                    {
                        continue;
                    }

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

                    // What are the vertex and texture coorindates of this face on the mesh?
                    var position = this.tmxMap.GetMapPositionAt(x, y, tile);
                    var vertices = CalculateFaceVertices(position, tile.TileSize);

                    // If we're using depth shaders then we'll need to set a depth value of this face
                    float depth_z = 0.0f;
                    if (Tiled2Unity.Settings.DepthBufferEnabled)
                    {
                        depth_z = CalculateFaceDepth(position.Y + tmxMap.TileHeight, tmxMap.MapSizeInPixels.Height);
                    }

                    FaceVertices faceVertices = new FaceVertices {
                        Vertices = vertices, Depth_z = depth_z
                    };

                    // Is the tile being flipped or rotated (needed for texture cooridinates)
                    bool flipDiagonal   = TmxMath.IsTileFlippedDiagonally(tileId);
                    bool flipHorizontal = TmxMath.IsTileFlippedHorizontally(tileId);
                    bool flipVertical   = TmxMath.IsTileFlippedVertically(tileId);
                    var  uvs            = CalculateFaceTextureCoordinates(tile, flipDiagonal, flipHorizontal, flipVertical);

                    // Adds vertices and uvs to the database as we build the face strings
                    string v0 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V0) + 1, uvDatabase.Add(uvs[0]) + 1);
                    string v1 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V1) + 1, uvDatabase.Add(uvs[1]) + 1);
                    string v2 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V2) + 1, uvDatabase.Add(uvs[2]) + 1);
                    string v3 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V3) + 1, uvDatabase.Add(uvs[3]) + 1);
                    faces.AppendFormat("f {0} {1} {2} {3}\n", v0, v1, v2, v3);
                }
            }

            // We have all the data we need to build the wavefront file format
            return(CreateWavefrontWriter(mesh, vertexDatabase, uvDatabase, faces));
        }
        private void DrawTiles(Graphics g)
        {
            // Load all our tiled images
            var images = from layer in this.tmxMap.Layers
                         where layer.Properties.GetPropertyValueAsBoolean("unity:collisionOnly", false) == false
                         where layer.Visible == true
                         where IsLayerEnabled(layer.DefaultName)
                         from y in Enumerable.Range(0, layer.Height)
                         from x in Enumerable.Range(0, layer.Width)
                         let tileId = layer.GetTileIdAt(x, y)
                                      where tileId != 0
                                      let tile = this.tmxMap.Tiles[tileId]
                                                 select new
            {
                Path  = tile.TmxImage.Path,
                Trans = tile.TmxImage.TransparentColor,
            };

            images = images.Distinct();

            Dictionary <string, Bitmap> tileSetBitmaps = new Dictionary <string, Bitmap>();

            foreach (var img in images)
            {
                Bitmap bmp = (Bitmap)Bitmap.FromFile(img.Path);

                if (!String.IsNullOrEmpty(img.Trans))
                {
                    System.Drawing.Color transColor = System.Drawing.ColorTranslator.FromHtml(img.Trans);
                    bmp.MakeTransparent(transColor);
                }

                tileSetBitmaps.Add(img.Path, bmp);
            }

            foreach (TmxLayer layer in this.tmxMap.Layers)
            {
                if (layer.Visible == false)
                {
                    continue;
                }

                if (IsLayerEnabled(layer.DefaultName) == false)
                {
                    continue;
                }

                if (layer.Properties.GetPropertyValueAsBoolean("unity:collisionOnly", false) == true)
                {
                    continue;
                }

                // The range of x and y depends on the render order of the tiles
                // By default we draw right and down but may reverse the tiles we visit
                var range_x = Enumerable.Range(0, layer.Width);
                var range_y = Enumerable.Range(0, layer.Height);

                if (this.tmxMap.DrawOrderHorizontal == -1)
                {
                    range_x = range_x.Reverse();
                }

                if (this.tmxMap.DrawOrderVertical == -1)
                {
                    range_y = range_y.Reverse();
                }

                // Visit the tiles we are going to draw
                var tiles = from y in range_y
                            from x in range_x
                            let rawTileId = layer.GetRawTileIdAt(x, y)
                                            let tileId = layer.GetTileIdAt(x, y)
                                                         where tileId != 0

                                                         let tile = this.tmxMap.Tiles[tileId]

                                                                    // Support for animated tiles. Just show the first frame of the animation.
                                                                    let frame = (tile.Animation == null) ? tile : this.tmxMap.Tiles[tile.Animation.Frames[0].GlobalTileId]

                                                                                select new
                {
                    Tile                  = frame,
                    Position              = TmxMath.TileCornerInScreenCoordinates(this.tmxMap, x, y),
                    Bitmap                = tileSetBitmaps[frame.TmxImage.Path],
                    IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId),
                    IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                    IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId),
                };

                PointF[] destPoints  = new PointF[4];
                PointF[] destPoints3 = new PointF[3];
                foreach (var t in tiles)
                {
                    PointF location = t.Position;

                    // Individual tiles may be larger than the given tile size of the overall map
                    location.Y = (t.Position.Y - t.Tile.TileSize.Height) + this.tmxMap.TileHeight;

                    // Make up the 'quad' of texture points and transform them
                    PointF center = new PointF(t.Tile.TileSize.Width * 0.5f, t.Tile.TileSize.Height * 0.5f);
                    destPoints[0] = new Point(0, 0);
                    destPoints[1] = new Point(t.Tile.TileSize.Width, 0);
                    destPoints[2] = new Point(t.Tile.TileSize.Width, t.Tile.TileSize.Height);
                    destPoints[3] = new Point(0, t.Tile.TileSize.Height);

                    // Transform the points based on our flipped flags
                    TmxMath.TransformPoints(destPoints, center, t.IsFlippedDiagnoally, t.IsFlippedHorizontally, t.IsFlippedVertically);

                    // Put the destination points back into world space
                    TmxMath.TranslatePoints(destPoints, location);

                    // Stupid DrawImage function only takes 3 destination points otherwise it throws an exception
                    destPoints3[0] = destPoints[0];
                    destPoints3[1] = destPoints[1];
                    destPoints3[2] = destPoints[3];

                    // Draw the tile
                    Rectangle source = new Rectangle(t.Tile.LocationOnSource, t.Tile.TileSize);
                    g.DrawImage(t.Bitmap, destPoints3, source, GraphicsUnit.Pixel);
                }
            }

            tileSetBitmaps.Clear();
        }
Exemple #3
0
        // Creates the text for a Wavefront OBJ file for the TmxMap
        private StringWriter BuildObjString()
        {
            HashIndexOf <PointF> vertexDatabase = new HashIndexOf <PointF>();
            HashIndexOf <PointF> uvDatabase     = new HashIndexOf <PointF>();

            // Go through every face of every mesh of every visible layer and collect vertex and texture coordinate indices as you go
            int           groupCount  = 0;
            StringBuilder faceBuilder = new StringBuilder();

            foreach (var layer in this.tmxMap.Layers)
            {
                if (layer.Visible != true)
                {
                    continue;
                }

                if (layer.Ignore == TmxLayer.IgnoreSettings.Visual)
                {
                    continue;
                }

                // We're going to use this layer
                ++groupCount;

                // Enumerate over the tiles in the direction given by the draw order of the map
                var verticalRange   = (this.tmxMap.DrawOrderVertical == 1) ? Enumerable.Range(0, layer.Height) : Enumerable.Range(0, layer.Height).Reverse();
                var horizontalRange = (this.tmxMap.DrawOrderHorizontal == 1) ? Enumerable.Range(0, layer.Width) : Enumerable.Range(0, layer.Width).Reverse();

                foreach (TmxMesh mesh in layer.Meshes)
                {
                    Program.WriteLine("Writing '{0}' mesh group", mesh.UniqueMeshName);
                    faceBuilder.AppendFormat("\ng {0}\n", mesh.UniqueMeshName);

                    foreach (int y in verticalRange)
                    {
                        foreach (int x in horizontalRange)
                        {
                            int  tileIndex = layer.GetTileIndex(x, y);
                            uint tileId    = mesh.GetTileIdAt(tileIndex);

                            // Skip blank tiles
                            if (tileId == 0)
                            {
                                continue;
                            }

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

                            // What are the vertex and texture coorindates of this face on the mesh?
                            var position = this.tmxMap.GetMapPositionAt(x, y);
                            var vertices = CalculateFaceVertices(position, tile.TileSize, this.tmxMap.TileHeight, tile.Offset);

                            // Is the tile being flipped or rotated (needed for texture cooridinates)
                            bool flipDiagonal   = TmxMath.IsTileFlippedDiagonally(tileId);
                            bool flipHorizontal = TmxMath.IsTileFlippedHorizontally(tileId);
                            bool flipVertical   = TmxMath.IsTileFlippedVertically(tileId);
                            var  uvs            = CalculateFaceTextureCoordinates(tile, flipDiagonal, flipHorizontal, flipVertical);

                            // Adds vertices and uvs to the database as we build the face strings
                            string v0 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[0]) + 1, uvDatabase.Add(uvs[0]) + 1);
                            string v1 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[1]) + 1, uvDatabase.Add(uvs[1]) + 1);
                            string v2 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[2]) + 1, uvDatabase.Add(uvs[2]) + 1);
                            string v3 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[3]) + 1, uvDatabase.Add(uvs[3]) + 1);
                            faceBuilder.AppendFormat("f {0} {1} {2} {3}\n", v0, v1, v2, v3);
                        }
                    }
                }
            }

            // Now go through any tile objects we may have and write them out as face groups as well
            foreach (var tmxMesh in this.tmxMap.GetUniqueListOfVisibleObjectTileMeshes())
            {
                // We're going to use this tile object
                groupCount++;

                Program.WriteLine("Writing '{0}' tile group", tmxMesh.UniqueMeshName);
                faceBuilder.AppendFormat("\ng {0}\n", tmxMesh.UniqueMeshName);

                // Get the single tile associated with this mesh
                TmxTile tmxTile = this.tmxMap.Tiles[tmxMesh.TileIds[0]];

                var vertices = CalculateFaceVertices_TileObject(tmxTile.TileSize, tmxTile.Offset);
                var uvs      = CalculateFaceTextureCoordinates(tmxTile, false, false, false);

                // Adds vertices and uvs to the database as we build the face strings
                string v0 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[0]) + 1, uvDatabase.Add(uvs[0]) + 1);
                string v1 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[1]) + 1, uvDatabase.Add(uvs[1]) + 1);
                string v2 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[2]) + 1, uvDatabase.Add(uvs[2]) + 1);
                string v3 = String.Format("{0}/{1}/1", vertexDatabase.Add(vertices[3]) + 1, uvDatabase.Add(uvs[3]) + 1);
                faceBuilder.AppendFormat("f {0} {1} {2} {3}\n", v0, v1, v2, v3);
            }

            // All of our faces have been built and vertex and uv databases have been filled.
            // Start building out the obj file
            StringWriter objWriter = new StringWriter();

            objWriter.WriteLine("# Wavefront OBJ file automatically generated by Tiled2Unity");
            objWriter.WriteLine();

            Program.WriteLine("Writing face vertices");
            objWriter.WriteLine("# Vertices (Count = {0})", vertexDatabase.List.Count());
            foreach (var v in vertexDatabase.List)
            {
                objWriter.WriteLine("v {0} {1} 0", v.X, v.Y);
            }
            objWriter.WriteLine();

            Program.WriteLine("Writing face uv coordinates");
            objWriter.WriteLine("# Texture cooridinates (Count = {0})", uvDatabase.List.Count());
            foreach (var uv in uvDatabase.List)
            {
                objWriter.WriteLine("vt {0} {1}", uv.X, uv.Y);
            }
            objWriter.WriteLine();

            // Write the one indexed normal
            objWriter.WriteLine("# Normal");
            objWriter.WriteLine("vn 0 0 -1");
            objWriter.WriteLine();

            // Now we can copy over the string used to build the databases
            objWriter.WriteLine("# Groups (Count = {0})", groupCount);
            objWriter.WriteLine(faceBuilder.ToString());

            return(objWriter);
        }
        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);
        }
Exemple #5
0
        // Creates the text for a Wavefront OBJ file for the TmxMap
        private StringWriter BuildObjString()
        {
            IGenericDatabase <Vertex3> vertexDatabase = new HashIndexOf <Vertex3>();
            HashIndexOf <PointF>       uvDatabase     = new HashIndexOf <PointF>();

            // Are we allowing vertices to be written too (advanced option)
            if (Tiled2Unity.Settings.WriteableVertices)
            {
                // Replace vertex database with class that ensure each vertex (even ones with similar values) are unique
                Logger.WriteLine("Using writeable-vertices. This will increase the size of the mesh but will allow you mutate vertices through scripting. This is an advanced feature.");
                vertexDatabase = new GenericListDatabase <Vertex3>();
            }

            float mapLogicalHeight = this.tmxMap.MapSizeInPixels().Height;

            // Go through every face of every mesh of every visible layer and collect vertex and texture coordinate indices as you go
            int           groupCount  = 0;
            StringBuilder faceBuilder = new StringBuilder();

            foreach (var layer in this.tmxMap.Layers)
            {
                if (layer.Visible != true)
                {
                    continue;
                }

                if (layer.Ignore == TmxLayer.IgnoreSettings.Visual)
                {
                    continue;
                }

                // We're going to use this layer
                ++groupCount;

                // Enumerate over the tiles in the direction given by the draw order of the map
                var verticalRange   = (this.tmxMap.DrawOrderVertical == 1) ? Enumerable.Range(0, layer.Height) : Enumerable.Range(0, layer.Height).Reverse();
                var horizontalRange = (this.tmxMap.DrawOrderHorizontal == 1) ? Enumerable.Range(0, layer.Width) : Enumerable.Range(0, layer.Width).Reverse();

                foreach (TmxMesh mesh in layer.Meshes)
                {
                    Logger.WriteLine("Writing '{0}' mesh group", mesh.UniqueMeshName);
                    faceBuilder.AppendFormat("\ng {0}\n", mesh.UniqueMeshName);

                    foreach (int y in verticalRange)
                    {
                        foreach (int x in horizontalRange)
                        {
                            int  tileIndex = layer.GetTileIndex(x, y);
                            uint tileId    = mesh.GetTileIdAt(tileIndex);

                            // Skip blank tiles
                            if (tileId == 0)
                            {
                                continue;
                            }

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

                            // What are the vertex and texture coorindates of this face on the mesh?
                            var position = this.tmxMap.GetMapPositionAt(x, y);
                            var vertices = CalculateFaceVertices(position, tile.TileSize, this.tmxMap.TileHeight, tile.Offset);

                            // If we're using depth shaders then we'll need to set a depth value of this face
                            float depth_z = 0.0f;
                            if (Tiled2Unity.Settings.DepthBufferEnabled)
                            {
                                depth_z = CalculateFaceDepth(position.Y, mapLogicalHeight);
                            }

                            FaceVertices faceVertices = new FaceVertices {
                                Vertices = vertices, Depth_z = depth_z
                            };

                            // Is the tile being flipped or rotated (needed for texture cooridinates)
                            bool flipDiagonal   = TmxMath.IsTileFlippedDiagonally(tileId);
                            bool flipHorizontal = TmxMath.IsTileFlippedHorizontally(tileId);
                            bool flipVertical   = TmxMath.IsTileFlippedVertically(tileId);
                            var  uvs            = CalculateFaceTextureCoordinates(tile, flipDiagonal, flipHorizontal, flipVertical);

                            // Adds vertices and uvs to the database as we build the face strings
                            string v0 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V0) + 1, uvDatabase.Add(uvs[0]) + 1);
                            string v1 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V1) + 1, uvDatabase.Add(uvs[1]) + 1);
                            string v2 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V2) + 1, uvDatabase.Add(uvs[2]) + 1);
                            string v3 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V3) + 1, uvDatabase.Add(uvs[3]) + 1);
                            faceBuilder.AppendFormat("f {0} {1} {2} {3}\n", v0, v1, v2, v3);
                        }
                    }
                }
            }

            // Now go through any tile objects we may have and write them out as face groups as well
            foreach (var tmxMesh in this.tmxMap.GetUniqueListOfVisibleObjectTileMeshes())
            {
                // We're going to use this tile object
                groupCount++;

                Logger.WriteLine("Writing '{0}' tile group", tmxMesh.UniqueMeshName);
                faceBuilder.AppendFormat("\ng {0}\n", tmxMesh.UniqueMeshName);

                // Get the single tile associated with this mesh
                TmxTile tmxTile = this.tmxMap.Tiles[tmxMesh.TileIds[0]];

                var vertices = CalculateFaceVertices_TileObject(tmxTile.TileSize, tmxTile.Offset);
                var uvs      = CalculateFaceTextureCoordinates(tmxTile, false, false, false);

                // TileObjects have zero depth on their vertices. Their GameObject parent will set depth.
                FaceVertices faceVertices = new FaceVertices {
                    Vertices = vertices, Depth_z = 0.0f
                };

                // Adds vertices and uvs to the database as we build the face strings
                string v0 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V0) + 1, uvDatabase.Add(uvs[0]) + 1);
                string v1 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V1) + 1, uvDatabase.Add(uvs[1]) + 1);
                string v2 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V2) + 1, uvDatabase.Add(uvs[2]) + 1);
                string v3 = String.Format("{0}/{1}/1", vertexDatabase.AddToDatabase(faceVertices.V3) + 1, uvDatabase.Add(uvs[3]) + 1);
                faceBuilder.AppendFormat("f {0} {1} {2} {3}\n", v0, v1, v2, v3);
            }

            // All of our faces have been built and vertex and uv databases have been filled.
            // Start building out the obj file
            StringWriter objWriter = new StringWriter();

            objWriter.WriteLine("# Wavefront OBJ file automatically generated by Tiled2Unity");
            objWriter.WriteLine();

            Logger.WriteLine("Writing face vertices");
            objWriter.WriteLine("# Vertices (Count = {0})", vertexDatabase.List.Count());
            foreach (var v in vertexDatabase.List)
            {
                objWriter.WriteLine("v {0} {1} {2}", v.X, v.Y, v.Z);
            }
            objWriter.WriteLine();

            Logger.WriteLine("Writing face uv coordinates");
            objWriter.WriteLine("# Texture cooridinates (Count = {0})", uvDatabase.List.Count());
            foreach (var uv in uvDatabase.List)
            {
                objWriter.WriteLine("vt {0} {1}", uv.X, uv.Y);
            }
            objWriter.WriteLine();

            // Write the one indexed normal
            objWriter.WriteLine("# Normal");
            objWriter.WriteLine("vn 0 0 -1");
            objWriter.WriteLine();

            // Now we can copy over the string used to build the databases
            objWriter.WriteLine("# Groups (Count = {0})", groupCount);
            objWriter.WriteLine(faceBuilder.ToString());

            return(objWriter);
        }
Exemple #6
0
        private void DrawTiles(Graphics g)
        {
            foreach (TmxLayer layer in this.tmxMap.Layers)
            {
                if (layer.Visible == false)
                {
                    continue;
                }

                if (layer.Ignore == TmxLayer.IgnoreSettings.Visual)
                {
                    continue;
                }

                // Set the opacity for the layer
                ColorMatrix colorMatrix = new ColorMatrix();
                colorMatrix.Matrix33 = layer.Opacity;

                ImageAttributes imageAttributes = new ImageAttributes();
                imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);

                // Translate by the offset
                GraphicsState state = g.Save();
                g.TranslateTransform(layer.Offset.X, layer.Offset.Y);

                // The range of x and y depends on the render order of the tiles
                // By default we draw right and down but may reverse the tiles we visit
                var range_x = Enumerable.Range(0, GetMaxTilesWide(layer));
                var range_y = Enumerable.Range(0, GetMaxTilesHigh(layer));

                if (this.tmxMap.DrawOrderHorizontal == -1)
                {
                    range_x = range_x.Reverse();
                }

                if (this.tmxMap.DrawOrderVertical == -1)
                {
                    range_y = range_y.Reverse();
                }

                // Visit the tiles we are going to draw
                var tiles = from y in range_y
                            from x in range_x
                            let rawTileId = layer.GetRawTileIdAt(x, y)
                                            let tileId = layer.GetTileIdAt(x, y)
                                                         where tileId != 0

                                                         let tile = this.tmxMap.Tiles[tileId]

                                                                    // Support for animated tiles. Just show the first frame of the animation.
                                                                    let frame = this.tmxMap.Tiles[tile.Animation.Frames[0].GlobalTileId]

                                                                                select new
                {
                    Tile                  = frame,
                    Position              = TmxMath.TileCornerInScreenCoordinates(this.tmxMap, x, y),
                    Bitmap                = frame.TmxImage.ImageBitmap,
                    IsFlippedDiagnoally   = TmxMath.IsTileFlippedDiagonally(rawTileId),
                    IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(rawTileId),
                    IsFlippedVertically   = TmxMath.IsTileFlippedVertically(rawTileId),
                };

                PointF[] destPoints  = new PointF[4];
                PointF[] destPoints3 = new PointF[3];
                foreach (var t in tiles)
                {
                    PointF location = t.Position;

                    // Individual tiles may be larger than the given tile size of the overall map
                    location.Y = (t.Position.Y - t.Tile.TileSize.Height) + this.tmxMap.TileHeight;

                    // Take tile offset into account
                    location.X += t.Tile.Offset.X;
                    location.Y += t.Tile.Offset.Y;

                    // Make up the 'quad' of texture points and transform them
                    PointF center = new PointF(t.Tile.TileSize.Width * 0.5f, t.Tile.TileSize.Height * 0.5f);
                    destPoints[0] = new Point(0, 0);
                    destPoints[1] = new Point(t.Tile.TileSize.Width, 0);
                    destPoints[2] = new Point(t.Tile.TileSize.Width, t.Tile.TileSize.Height);
                    destPoints[3] = new Point(0, t.Tile.TileSize.Height);

                    // Transform the points based on our flipped flags
                    TmxMath.TransformPoints(destPoints, center, t.IsFlippedDiagnoally, t.IsFlippedHorizontally, t.IsFlippedVertically);

                    // Put the destination points back into world space
                    TmxMath.TranslatePoints(destPoints, location);

                    // Stupid DrawImage function only takes 3 destination points otherwise it throws an exception
                    destPoints3[0] = destPoints[0];
                    destPoints3[1] = destPoints[1];
                    destPoints3[2] = destPoints[3];

                    // Draw the tile
                    Rectangle source = new Rectangle(t.Tile.LocationOnSource, t.Tile.TileSize);
                    g.DrawImage(t.Bitmap, destPoints3, source, GraphicsUnit.Pixel, imageAttributes);
                }

                g.Restore(state);
            }
        }
Exemple #7
0
        public static PolyTree ExecuteClipper(TmxMap map, TmxChunk chunk, TransformPointFunc xfFunc)
        {
            ////for(int i=0;i<chunk.Height;i++)
            //// {
            ////     for(int j=0; j<chunk.Width;j++)
            ////     {
            ////         var raw = chunk.GetRawTileIdAt(j, i);
            ////      if(raw!=0)
            ////         {
            ////             var tid = TmxMath.GetTileIdWithoutFlags(raw);
            ////             var tile = map.Tiles[tid];
            ////             foreach(var p in tile.ObjectGroup.Objects)
            ////             {
            ////                 if(p is TmxHasPoints)
            ////                 {
            ////                     p.ToEnumerable().Where((x) =>
            ////                     {
            ////                         if (!usingUnityLayerOverride)
            ////                         {


            ////                             return string.Compare(tuple.Item1.Type, chunk.ParentData.ParentLayer.Name, true) == 0;
            ////                         }

            ////                         return true;
            ////                     });
            ////                 }

            ////             }
            ////         }
            ////     }
            //// }

            //     Clipper clipper = new Clipper(0);
            //     Tuple<TmxObject, TmxTile, uint> tuple = new Tuple<TmxObject, TmxTile, uint>(null, null, 0);
            //     bool usingUnityLayerOverride = !string.IsNullOrEmpty(chunk.ParentData.ParentLayer.UnityLayerOverrideName);
            //     foreach (var item2 in from h__TransparentIdentifier4 in (from y in Enumerable.Range(0, chunk.Height)
            //                                                              from x in Enumerable.Range(0, chunk.Width)
            //                                                              let rawTileId = chunk.GetRawTileIdAt(x, y)
            //                                                              where rawTileId != 0
            //                                                              let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
            //                                                              let tile = map.Tiles[tileId]

            //                                                              from polygon in tile.ObjectGroup.Objects
            //                                                              where polygon is TmxHasPoints

            //                                                              select polygon.ToEnumerable().ToList().TrueForAll
            //                                                              (h__TransparentIdentifier4 =>

            //                    {
            //                        UnityEngine.Debug.Log("liudaodelh");
            //                        tuple = new Tuple<TmxObject, TmxTile, uint>(polygon, tile, rawTileId);
            //                        if (!usingUnityLayerOverride)
            //                        {


            //                            return string.Compare(tuple.Item1.Type, chunk.ParentData.ParentLayer.Name, true) == 0;
            //                        }

            //                        return true;
            //                    }))
            //                           select new
            //                           {

            //                               PositionOnMap = map.GetMapPositionAt((int)tuple.Item1.Position.X + chunk.X, (int)tuple.Item1.Position.Y + chunk.Y, tuple.Item2),
            //                               HasPointsInterface = (tuple.Item1 as TmxHasPoints),
            //                               TmxObjectInterface = tuple.Item1,
            //                               IsFlippedDiagnoally = TmxMath.IsTileFlippedDiagonally(tuple.Item3),
            //                               IsFlippedHorizontally = TmxMath.IsTileFlippedHorizontally(tuple.Item3),
            //                               IsFlippedVertically = TmxMath.IsTileFlippedVertically(tuple.Item3),
            //                               TileCenter = new PointF((float)tuple.Item2.TileSize.Width * 0.5f, (float)tuple.Item2.TileSize.Height * 0.5f)
            //                           })
            //     {
            //         List<IntPoint> list = new List<IntPoint>();
            //         SizeF offset = new SizeF(item2.TmxObjectInterface.Position);
            //         PointF[] array = item2.HasPointsInterface.Points.Select((PointF pt) => PointF.Add(pt, offset)).ToArray();
            //         TmxMath.TransformPoints(array, item2.TileCenter, item2.IsFlippedDiagnoally, item2.IsFlippedHorizontally, item2.IsFlippedVertically);
            //         PointF[] array2 = array;
            //         for (int i = 0; i < array2.Length; i++)
            //         {
            //             PointF pointF = array2[i];
            //             float x2 = (float)item2.PositionOnMap.X + pointF.X;
            //             float y2 = (float)item2.PositionOnMap.Y + pointF.Y;
            //             IntPoint item = xfFunc(x2, y2);
            //             list.Add(item);
            //         }
            //         list.Reverse();
            //         clipper.AddPath(list, PolyType.ptSubject, item2.HasPointsInterface.ArePointsClosed());
            //     }
            //     PolyTree polyTree = new PolyTree();
            //     clipper.Execute(ClipType.ctUnion, polyTree, SubjectFillRule, ClipFillRule);

            //     return polyTree;


            ClipperLib.Clipper clipper = 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(chunk.ParentData.ParentLayer.UnityLayerOverrideName);

            var polygons = from y in Enumerable.Range(0, chunk.Height)
                           from x in Enumerable.Range(0, chunk.Width)
                           let rawTileId = chunk.GetRawTileIdAt(x, y)
                                           where rawTileId != 0
                                           let tileId                       = TmxMath.GetTileIdWithoutFlags(rawTileId)
                                                                   let tile = map.Tiles[tileId]
                                                                              from polygon in tile.ObjectGroup.Objects
                                                                              where (polygon as TmxHasPoints) != null
                                                                              where usingUnityLayerOverride || String.Compare(polygon.Type, chunk.ParentData.ParentLayer.Name, true) == 0
                                                                              select new
            {
                PositionOnMap         = map.GetMapPositionAt(x + chunk.X, y + chunk.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),
            };

            // 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 polygons)
            {
                // 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"
                clipper.AddPath(clipperPolygon, ClipperLib.PolyType.ptSubject, poly.HasPointsInterface.ArePointsClosed());
            }

            ClipperLib.PolyTree solution = new ClipperLib.PolyTree();
            clipper.Execute(ClipperLib.ClipType.ctUnion, solution, LayerClipper.SubjectFillRule, LayerClipper.ClipFillRule);
            return(solution);
        }
        private StringWriter BuildObjString()
        {
            // Creates the text for a Wavefront OBJ file for the TmxMap
            StringWriter objWriter = new StringWriter();

            // Gather the information for every face
            var faces = from layer in this.tmxMap.Layers
                        where layer.Visible == true
                        where layer.Properties.GetPropertyValueAsBoolean("unity:collisionOnly", false) == false

                        // Draw order forces us to visit tiles in a particular order
                        from y in (this.tmxMap.DrawOrderVertical == 1) ? Enumerable.Range(0, layer.Height) : Enumerable.Range(0, layer.Height).Reverse()
                        from x in (this.tmxMap.DrawOrderHorizontal == 1) ? Enumerable.Range(0, layer.Width) : Enumerable.Range(0, layer.Width).Reverse()

                        let rawTileId = layer.GetRawTileIdAt(x, y)
                                        let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId)
                                                     where tileId != 0
                                                     let fd                                              = TmxMath.IsTileFlippedDiagonally(rawTileId)
                                                                                let fh                   = TmxMath.IsTileFlippedHorizontally(rawTileId)
                                                                                                  let fv = TmxMath.IsTileFlippedVertically(rawTileId)
                                                                                                           let animTile = this.tmxMap.Tiles[tileId]

                                                                                                                          // Enumerate through all frames of a tile. (Tiles without animation are treated as a single frame)
                                                                                                                          from frame in TileFrame.EnumerateFramesFromTile(animTile, this.tmxMap)
                                                                                                                          select new
            {
                LayerName          = layer.UniqueName,
                Vertices           = CalculateFaceVertices(this.tmxMap.GetMapPositionAt(x, y), frame.Tile.TileSize, this.tmxMap.TileHeight, frame.Position_z),
                TextureCoordinates = CalculateFaceTextureCoordinates(frame.Tile, fd, fh, fv),
                ImagePath          = frame.Tile.TmxImage.Path,
                ImageName          = Path.GetFileNameWithoutExtension(frame.Tile.TmxImage.Path),
            };

            // We have all the information we need now to build our list of vertices, texture coords, and grouped faces
            // (Faces are grouped by LayerName.TextureName combination because Wavefront Obj only supports one texture per face)
            objWriter.WriteLine("# Wavefront OBJ file automatically generated by Tiled2Unity");
            objWriter.WriteLine();

            // We may have a ton of vertices so use a set right now
            HashSet <Vector3D> vertexSet = new HashSet <Vector3D>();

            Program.WriteLine("Building face vertices");
            foreach (var face in faces)
            {
                // Index the vertices
                foreach (var v in face.Vertices)
                {
                    vertexSet.Add(v);
                }
            }

            HashSet <PointF> textureCoordinateSet = new HashSet <PointF>();

            Program.WriteLine("Building face texture coordinates");
            foreach (var face in faces)
            {
                // Index the texture coordinates
                foreach (var tc in face.TextureCoordinates)
                {
                    textureCoordinateSet.Add(tc);
                }
            }

            // Write the indexed vertices
            Program.WriteLine("Writing indexed vertices");
            IList <Vector3D> vertices = vertexSet.ToList();

            objWriter.WriteLine("# Vertices (Count = {0})", vertices.Count);
            foreach (var v in vertices)
            {
                objWriter.WriteLine("v {0} {1} {2}", v.X, v.Y, v.Z);
            }
            objWriter.WriteLine();

            // Write the indexed texture coordinates
            Program.WriteLine("Writing indexed texture coordinates");
            IList <PointF> textureCoordinates = textureCoordinateSet.ToList();

            objWriter.WriteLine("# Texture Coorindates (Count = {0})", textureCoordinates.Count);
            foreach (var vt in textureCoordinates)
            {
                objWriter.WriteLine("vt {0} {1}", vt.X, vt.Y);
            }
            objWriter.WriteLine();

            // Write the one indexed normal
            objWriter.WriteLine("# Normal");
            objWriter.WriteLine("vn 0 0 -1");
            objWriter.WriteLine();

            // Group faces by Layer+TileSet
            var groups = from f in faces
                         group f by TiledMapExpoterUtils.UnityFriendlyMeshName(tmxMap, f.LayerName, f.ImageName);

            // Write out the faces
            objWriter.WriteLine("# Groups (Count = {0})", groups.Count());

            // Need dictionaries with index as value.
            var vertexDict   = Enumerable.Range(0, vertices.Count()).ToDictionary(i => vertices[i], i => i);
            var texCoordDict = Enumerable.Range(0, textureCoordinates.Count()).ToDictionary(i => textureCoordinates[i], i => i);

            foreach (var g in groups)
            {
                Program.WriteLine("Writing '{0}' mesh group", g.Key);

                objWriter.WriteLine("g {0}", g.Key);
                foreach (var f in g)
                {
                    objWriter.Write("f ");
                    for (int i = 0; i < 4; ++i)
                    {
                        int vertexIndex            = vertexDict[f.Vertices[i]] + 1;
                        int textureCoordinateIndex = texCoordDict[f.TextureCoordinates[i]] + 1;

                        objWriter.Write(" {0}/{1}/1 ", vertexIndex, textureCoordinateIndex);
                    }
                    objWriter.WriteLine();
                }
            }

            Program.WriteLine("Done writing Wavefront Obj data for '{0}'", tmxMap.Name);

            return(objWriter);
        }
        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);
        }