protected override void InternalFromXml(System.Xml.Linq.XElement xml, TmxMap tmxMap) { // Get the tile uint gid = TmxHelper.GetAttributeAsUInt(xml, "gid"); this.FlippedHorizontal = TmxMath.IsTileFlippedHorizontally(gid); this.FlippedVertical = TmxMath.IsTileFlippedVertically(gid); uint rawTileId = TmxMath.GetTileIdWithoutFlags(gid); this.Tile = tmxMap.Tiles[rawTileId]; // The tile needs to have a mesh on it. // Note: The tile may already be referenced by another TmxObjectTile instance, and as such will have its mesh data already made if (this.Tile.Meshes.Count() == 0) { this.Tile.Meshes = TmxMesh.FromTmxTile(this.Tile, tmxMap); } // Check properties for layer placement if (this.Properties.PropertyMap.ContainsKey("unity:sortingLayerName")) { this.SortingLayerName = this.Properties.GetPropertyValueAsString("unity:sortingLayerName"); } if (this.Properties.PropertyMap.ContainsKey("unity:sortingOrder")) { this.SortingOrder = this.Properties.GetPropertyValueAsInt("unity:sortingOrder"); } }
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)); }
protected override void InternalFromXml(System.Xml.Linq.XElement xml, TmxMap tmxMap) { // Get the tile uint gid = TmxHelper.GetAttributeAsUInt(xml, "gid"); this.FlippedHorizontal = TmxMath.IsTileFlippedHorizontally(gid); this.FlippedVertical = TmxMath.IsTileFlippedVertically(gid); uint rawTileId = TmxMath.GetTileIdWithoutFlags(gid); this.Tile = tmxMap.Tiles[rawTileId]; // The tile needs to have a mesh on it. // Note: The tile may already be referenced by another TmxObjectTile instance, and as such will have its mesh data already made if (this.Tile.Meshes.Count() == 0) { this.Tile.Meshes = TmxMesh.FromTmxTile(this.Tile, tmxMap); } }
protected override void InternalFromXml(XElement xml, TmxMap tmxMap) { uint attributeAsUInt = TmxHelper.GetAttributeAsUInt(xml, "gid"); FlippedHorizontal = TmxMath.IsTileFlippedHorizontally(attributeAsUInt); FlippedVertical = TmxMath.IsTileFlippedVertically(attributeAsUInt); uint tileIdWithoutFlags = TmxMath.GetTileIdWithoutFlags(attributeAsUInt); Tile = tmxMap.Tiles[tileIdWithoutFlags]; if (Tile.Meshes.Count() == 0) { Tile.Meshes = TmxMesh.FromTmxTile(Tile, tmxMap); } ExplicitSortingLayerName = base.Properties.GetPropertyValueAsString("unity:sortingLayerName", ""); if (base.Properties.PropertyMap.ContainsKey("unity:sortingOrder")) { ExplicitSortingOrder = base.Properties.GetPropertyValueAsInt("unity:sortingOrder"); } }
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(); }
// 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); }
// 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); }
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); } }
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); }