private void ReadTileIds(XElement xml) { TileIds.Clear(); if (ParentData.Encoding == DataEncoding.Xml) { ReadTileIds_Xml(xml); } else if (ParentData.Encoding == DataEncoding.Csv) { ReadTiledIds_Csv(xml.Value); } else if (ParentData.Encoding == DataEncoding.Base64) { ReadTileIds_Base64(xml.Value); } else { TmxException.ThrowFormat("Unsupported encoding for chunk data in layer '{0}'", ParentData.ParentLayer); } for (int i = 0; i < TileIds.Count; i++) { uint tileId = TileIds[i]; tileId = TmxMath.GetTileIdWithoutFlags(tileId); if (!ParentData.ParentLayer.ParentMap.Tiles.ContainsKey(tileId)) { TileIds[i] = 0u; } } }
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 void BuildCollisionLayers_ByObjectType() { for (int i = 0; i < Data.Chunks.Count; i++) { TmxChunk tmxChunk = Data.Chunks[i]; for (int j = 0; j < tmxChunk.TileIds.Count; j++) { uint num = tmxChunk.TileIds[j]; if (num != 0) { uint tileIdWithoutFlags = TmxMath.GetTileIdWithoutFlags(num); foreach (TmxObject @object in base.ParentMap.Tiles[tileIdWithoutFlags].ObjectGroup.Objects) { if (@object is TmxHasPoints) { TmxLayer tmxLayer = CollisionLayers.Find((TmxLayer l) => string.Compare(l.Name, @object.Type, true) == 0); if (tmxLayer == null) { tmxLayer = new TmxLayer(null, base.ParentMap); CollisionLayers.Add(tmxLayer); tmxLayer.Name = @object.Type; tmxLayer.Offset = base.Offset; tmxLayer.Width = Width; tmxLayer.Height = Height; tmxLayer.Ignore = base.Ignore; tmxLayer.Properties = base.Properties; tmxLayer.Data = Data.MakeEmptyCopy(tmxLayer); } tmxLayer.Data.Chunks[i].TileIds[j] = num; } } } } } }
public static TmxObject FromXml(XElement xml, TmxObjectGroup tmxObjectGroup, TmxMap tmxMap) { Logger.WriteVerbose("Parsing object ..."); uint attributeAsUInt = TmxHelper.GetAttributeAsUInt(xml, "tid", 0u); if (attributeAsUInt != 0 && tmxMap.Templates.TryGetValue(attributeAsUInt, out TgxTemplate value)) { xml = value.Templatize(xml); tmxMap = value.TemplateGroupMap; } TmxObject tmxObject = null; if (xml.Element("ellipse") != null) { tmxObject = new TmxObjectEllipse(); } else if (xml.Element("polygon") != null) { tmxObject = new TmxObjectPolygon(); } else if (xml.Element("polyline") != null) { tmxObject = new TmxObjectPolyline(); } else if (xml.Attribute("gid") != null) { uint attributeAsUInt2 = TmxHelper.GetAttributeAsUInt(xml, "gid"); attributeAsUInt2 = TmxMath.GetTileIdWithoutFlags(attributeAsUInt2); if (tmxMap.Tiles.ContainsKey(attributeAsUInt2)) { tmxObject = new TmxObjectTile(); } else { Logger.WriteWarning("Tile Id {0} not found in tilesets. Using a rectangle instead.\n{1}", attributeAsUInt2, xml.ToString()); tmxObject = new TmxObjectRectangle(); } } else { tmxObject = new TmxObjectRectangle(); } tmxObject.Id = TmxHelper.GetAttributeAsInt(xml, "id", 0); tmxObject.Name = TmxHelper.GetAttributeAsString(xml, "name", ""); tmxObject.Type = TmxHelper.GetAttributeAsString(xml, "type", ""); tmxObject.Visible = (TmxHelper.GetAttributeAsInt(xml, "visible", 1) == 1); tmxObject.ParentObjectGroup = tmxObjectGroup; float attributeAsFloat = TmxHelper.GetAttributeAsFloat(xml, "x"); float attributeAsFloat2 = TmxHelper.GetAttributeAsFloat(xml, "y"); float attributeAsFloat3 = TmxHelper.GetAttributeAsFloat(xml, "width", 0f); float attributeAsFloat4 = TmxHelper.GetAttributeAsFloat(xml, "height", 0f); float attributeAsFloat5 = TmxHelper.GetAttributeAsFloat(xml, "rotation", 0f); tmxObject.Position = new PointF(attributeAsFloat, attributeAsFloat2); tmxObject.Size = new SizeF(attributeAsFloat3, attributeAsFloat4); tmxObject.Rotation = attributeAsFloat5; tmxObject.Properties = TmxProperties.FromXml(xml); tmxObject.InternalFromXml(xml, tmxMap); return(tmxObject); }
public TmxTile GetTileFromTileId(uint tileId) { if (tileId == 0) { return(null); } tileId = TmxMath.GetTileIdWithoutFlags(tileId); return(Tiles[tileId]); }
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 BuildCollisionLayers_ByObjectType() { // Find all tiles with collisions on them and put them into a "Collision Layer" of the same type for (int t = 0; t < this.TileIds.Length; ++t) { uint rawTileId = this.TileIds[t]; if (rawTileId == 0) { continue; } uint tileId = TmxMath.GetTileIdWithoutFlags(rawTileId); TmxTile tmxTile = this.TmxMap.Tiles[tileId]; foreach (TmxObject colliderObject in tmxTile.ObjectGroup.Objects) { if ((colliderObject is TmxHasPoints) == false) { continue; } // We have a collider object on the tile // Add the tile to the Collision Layer of the matching type // Or, create a new Collision Layer of this type to add this tile to TmxLayer collisionLayer = this.CollisionLayers.Find(l => String.Compare(l.Name, colliderObject.Type, true) == 0); if (collisionLayer == null) { // Create a new Collision Layer collisionLayer = new TmxLayer(null, this.TmxMap); this.CollisionLayers.Add(collisionLayer); // The new Collision Layer has the name of the collider object and empty tiles (they will be filled with tiles that have matching collider objects) collisionLayer.Name = colliderObject.Type; collisionLayer.TileIds = new uint[this.TileIds.Length]; // Copy over some stuff from parent layer that we need for creating collisions collisionLayer.Offset = this.Offset; collisionLayer.Width = this.Width; collisionLayer.Height = this.Height; collisionLayer.Ignore = this.Ignore; collisionLayer.Properties = this.Properties; } // Add the tile to this collision layer collisionLayer.TileIds[t] = rawTileId; } } }
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"); } }
// Creates a TmxMesh from a tile (for tile objects) public static List <TmxMesh> FromTmxTile(TmxTile tmxTile, TmxMap tmxMap) { List <TmxMesh> meshes = new List <TmxMesh>(); int timeMs = 0; foreach (var frame in tmxTile.Animation.Frames) { uint frameTileId = frame.GlobalTileId; TmxTile frameTile = tmxMap.Tiles[frameTileId]; TmxMesh mesh = new TmxMesh(); mesh.TileIds = new uint[1]; mesh.TileIds[0] = frameTileId; mesh.UniqueMeshName = String.Format("mesh_tile_{0}", TmxMath.GetTileIdWithoutFlags(frameTileId).ToString("D4")); mesh.TmxImage = frameTile.TmxImage; mesh.ObjectName = "tile_obj"; // Keep track of the timing for this mesh (non-animating meshes will have a start time and duration of 0) mesh.StartTimeMs = timeMs; mesh.DurationMs = frame.DurationMs; mesh.FullAnimationDurationMs = tmxTile.Animation.TotalTimeMs; if (mesh.DurationMs != 0) { // Decorate the name a bit with some animation details for the frame mesh.ObjectName += string.Format("[{0}-{1}]", timeMs, timeMs + mesh.DurationMs); } // Advance time timeMs += frame.DurationMs; // Add the animation frame to our list of meshes meshes.Add(mesh); } return(meshes); }
// 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); }
public uint GetTileIdAt(int x, int y) { return(TmxMath.GetTileIdWithoutFlags(GetRawTileIdAt(x, y))); }
public static TmxLayer FromXml(XElement elem, TmxLayerNode parent, TmxMap tmxMap) { TmxLayer tmxLayer = new TmxLayer(parent, tmxMap); tmxLayer.FromXmlInternal(elem); // We can build a layer from a "tile layer" (default) or an "image layer" if (elem.Name == "layer") { tmxLayer.Width = TmxHelper.GetAttributeAsInt(elem, "width"); tmxLayer.Height = TmxHelper.GetAttributeAsInt(elem, "height"); tmxLayer.ParseData(elem.Element("data")); } else if (elem.Name == "imagelayer") { XElement xmlImage = elem.Element("image"); if (xmlImage == null) { Logger.WriteWarning("Image Layer '{0}' is being ignored since it has no image.", tmxLayer.Name); tmxLayer.Ignore = IgnoreSettings.True; return(tmxLayer); } // An image layer is sort of like an tile layer but with just one tile tmxLayer.Width = 1; tmxLayer.Height = 1; // Find the "tile" that matches our image string imagePath = TmxHelper.GetAttributeAsFullPath(elem.Element("image"), "source"); TmxTile tile = tmxMap.Tiles.First(t => t.Value.TmxImage.AbsolutePath == imagePath).Value; tmxLayer.TileIds = new uint[1] { tile.GlobalId }; // The image layer needs to be tranlated in an interesting way when expressed as a tile layer PointF translated = tmxLayer.Offset; // Make up for height of a regular tile in the map translated.Y -= (float)tmxMap.TileHeight; // Make up for the height of this image translated.Y += (float)tile.TmxImage.Size.Height; // Correct for any orientation effects on the map (like isometric) // (We essentially undo the translation via orientation here) PointF orientation = TmxMath.TileCornerInScreenCoordinates(tmxMap, 0, 0); translated.X -= orientation.X; translated.Y -= orientation.Y; // Translate by the x and y coordiantes translated.X += TmxHelper.GetAttributeAsFloat(elem, "x", 0); translated.Y += TmxHelper.GetAttributeAsFloat(elem, "y", 0); tmxLayer.Offset = translated; } // Sometimes TMX files have "dead" tiles in them (tiles that were removed but are still referenced) // Remove these tiles from the layer by replacing them with zero for (int t = 0; t < tmxLayer.TileIds.Length; ++t) { uint tileId = tmxLayer.TileIds[t]; tileId = TmxMath.GetTileIdWithoutFlags(tileId); if (!tmxMap.Tiles.ContainsKey(tileId)) { tmxLayer.TileIds[t] = 0; } } // Each layer will be broken down into "meshes" which are collections of tiles matching the same texture or animation tmxLayer.Meshes = TmxMesh.ListFromTmxLayer(tmxLayer); // Each layer may contain different collision types which are themselves put into "Collison Layers" to be processed later tmxLayer.BuildCollisionLayers(); return(tmxLayer); }
// 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); }
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); }
public static TmxObject FromXml(XElement xml, TmxObjectGroup tmxObjectGroup, TmxMap tmxMap) { Logger.WriteLine("Parsing object ..."); // What kind of TmxObject are we creating? TmxObject tmxObject = null; if (xml.Element("ellipse") != null) { tmxObject = new TmxObjectEllipse(); } else if (xml.Element("polygon") != null) { tmxObject = new TmxObjectPolygon(); } else if (xml.Element("polyline") != null) { tmxObject = new TmxObjectPolyline(); } else if (xml.Attribute("gid") != null) { uint gid = TmxHelper.GetAttributeAsUInt(xml, "gid"); gid = TmxMath.GetTileIdWithoutFlags(gid); if (tmxMap.Tiles.ContainsKey(gid)) { tmxObject = new TmxObjectTile(); } else { // For some reason, the tile is not in any of our tilesets // Warn the user and use a rectangle Logger.WriteWarning("Tile Id {0} not found in tilesets. Using a rectangle instead.\n{1}", gid, xml.ToString()); tmxObject = new TmxObjectRectangle(); } } else { // Just a rectangle tmxObject = new TmxObjectRectangle(); } // Data found on every object type tmxObject.Name = TmxHelper.GetAttributeAsString(xml, "name", ""); tmxObject.Type = TmxHelper.GetAttributeAsString(xml, "type", ""); tmxObject.Visible = TmxHelper.GetAttributeAsInt(xml, "visible", 1) == 1; tmxObject.ParentObjectGroup = tmxObjectGroup; float x = TmxHelper.GetAttributeAsFloat(xml, "x"); float y = TmxHelper.GetAttributeAsFloat(xml, "y"); float w = TmxHelper.GetAttributeAsFloat(xml, "width", 0); float h = TmxHelper.GetAttributeAsFloat(xml, "height", 0); float r = TmxHelper.GetAttributeAsFloat(xml, "rotation", 0); tmxObject.Position = new System.Drawing.PointF(x, y); tmxObject.Size = new System.Drawing.SizeF(w, h); tmxObject.Rotation = r; tmxObject.Properties = TmxProperties.FromXml(xml); tmxObject.InternalFromXml(xml, tmxMap); return(tmxObject); }
private List <XElement> CreateMeshElementsForLayer(TmxLayer layer) { // Mesh elements look like this: // <GameObject copy="LayerName+TilesetName" /> // (The importer in Unity will look a submesh of that name and copy it to our prefab) // (This is complicated by potential tile animations now) var meshes = from rawTileId in layer.TileIds where rawTileId != 0 let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId) let tile = this.tmxMap.Tiles[tileId] let name = TiledMapExpoterUtils.UnityFriendlyMeshName(tmxMap, layer.UniqueName, Path.GetFileNameWithoutExtension(tile.TmxImage.Path)) group tile.Animation by name into meshGroup select meshGroup; List <XElement> xmlMeshes = new List <XElement>(); foreach (var m in meshes) { XElement xmlMesh = new XElement("GameObject", new XAttribute("copy", m.Key)); // Do we have any animations? var animations = m.Distinct(); foreach (var anim in animations) { if (anim != null) { XElement xmlAnim = new XElement("TileAnimator"); foreach (var frame in anim.Frames) { xmlAnim.Add(new XElement("Frame", new XAttribute("vertex_z", frame.UniqueFrameId * Program.Vertex_ZScale), new XAttribute("duration", frame.DurationMs))); } xmlMesh.Add(xmlAnim); } } xmlMeshes.Add(xmlMesh); } return(xmlMeshes); }
private List <XElement> CreateImportFilesElements(string exportToUnityProjectPath) { List <XElement> elements = new List <XElement>(); // Add the mesh file as raw text { StringWriter objBuilder = BuildObjString(); XElement mesh = new XElement("ImportMesh", new XAttribute("filename", this.tmxMap.Name + ".obj"), StringToBase64String(objBuilder.ToString())); elements.Add(mesh); } { // Add all image files as compressed base64 strings var layerImages = from layer in this.tmxMap.Layers where layer.Visible == true from rawTileId in layer.TileIds where rawTileId != 0 let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId) let tile = this.tmxMap.Tiles[tileId] select tile.TmxImage; // Tile Objects may have images not yet references by a layer var objectImages = from objectGroup in this.tmxMap.ObjectGroups where objectGroup.Visible == true from tmxObject in objectGroup.Objects where tmxObject.Visible == true where tmxObject is TmxObjectTile let tmxTileObject = tmxObject as TmxObjectTile from mesh in tmxTileObject.Tile.Meshes select mesh.TmxImage; // Combine image paths from tile layers and object layers List <TmxImage> images = new List <TmxImage>(); images.AddRange(layerImages); images.AddRange(objectImages); // Get rid of duplicate images TmxImageComparer imageComparer = new TmxImageComparer(); images = images.Distinct(imageComparer).ToList(); // Do not import files if they are already in the project (in the /Assets/ directory of where we're exporting too) string unityAssetsDir = Path.Combine(exportToUnityProjectPath, "Assets"); foreach (TmxImage image in images) { // If the copy from location comes from within the project we want to copy to, then don't do it. // This allows us to have tileset images that are alreday in use by the Unity project string saveToAssetsDir = unityAssetsDir.ToLower(); string copyFromDir = image.AbsolutePath.ToLower(); if (copyFromDir.StartsWith(saveToAssetsDir)) { XElement xmlInternalTexture = new XElement("InternalTexture"); // The path to the texture will be WRT to the Unity project root string assetPath = image.AbsolutePath.Remove(0, exportToUnityProjectPath.Length); assetPath = assetPath.TrimStart('\\'); assetPath = assetPath.TrimStart('/'); Program.WriteLine("InternalTexture : {0}", assetPath); // Path to texture in the asset directory xmlInternalTexture.SetAttributeValue("assetPath", assetPath); // Transparent color key? if (!String.IsNullOrEmpty(image.TransparentColor)) { xmlInternalTexture.SetAttributeValue("alphaColorKey", image.TransparentColor); } // Are we using depth shaders on our materials? if (Program.DepthBufferEnabled) { xmlInternalTexture.SetAttributeValue("usesDepthShaders", true); } elements.Add(xmlInternalTexture); } else { XElement xmlImportTexture = new XElement("ImportTexture"); // Note that compression is not available in Unity. Go with Base64 string. Blerg. Program.WriteLine("ImportTexture : will import '{0}' to {1}", image.AbsolutePath, Path.Combine(unityAssetsDir, "Tiled2Unity\\Textures\\")); // Is there a color key for transparency? if (!String.IsNullOrEmpty(image.TransparentColor)) { xmlImportTexture.SetAttributeValue("alphaColorKey", image.TransparentColor); } // Are we using depth shaders on our materials? if (Program.DepthBufferEnabled) { xmlImportTexture.SetAttributeValue("usesDepthShaders", true); } // Bake the image file into the xml xmlImportTexture.Add(new XAttribute("filename", Path.GetFileName(image.AbsolutePath)), FileToBase64String(image.AbsolutePath)); elements.Add(xmlImportTexture); } } } return(elements); }
private List <XElement> CreateImportFilesElements(string exportToUnityProjectPath) { List <XElement> elements = new List <XElement>(); // Add the mesh file as raw text { StringWriter objBuilder = BuildObjString(); XElement mesh = new XElement("ImportMesh", new XAttribute("filename", this.tmxMap.Name + ".obj"), StringToBase64String(objBuilder.ToString())); elements.Add(mesh); } { // Add all image files as compressed base64 strings var layerImages = from layer in this.tmxMap.Layers where layer.Visible == true from rawTileId in layer.TileIds where rawTileId != 0 let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId) let tile = this.tmxMap.Tiles[tileId] select tile.TmxImage; // Find the images from the frames as well var frameImages = from layer in this.tmxMap.Layers where layer.Visible == true from rawTileId in layer.TileIds where rawTileId != 0 let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId) let tile = this.tmxMap.Tiles[tileId] from rawFrame in tile.Animation.Frames let frameId = TmxMath.GetTileIdWithoutFlags(rawFrame.GlobalTileId) let frame = this.tmxMap.Tiles[frameId] select frame.TmxImage; // Tile Objects may have images not yet references by a layer var objectImages = from objectGroup in this.tmxMap.ObjectGroups where objectGroup.Visible == true from tmxObject in objectGroup.Objects where tmxObject.Visible == true where tmxObject is TmxObjectTile let tmxTileObject = tmxObject as TmxObjectTile from mesh in tmxTileObject.Tile.Meshes select mesh.TmxImage; // Combine image paths from tile layers and object layers List <TmxImage> images = new List <TmxImage>(); images.AddRange(layerImages); images.AddRange(frameImages); images.AddRange(objectImages); // Get rid of duplicate images TmxImageComparer imageComparer = new TmxImageComparer(); images = images.Distinct(imageComparer).ToList(); foreach (TmxImage image in images) { // The source texture is internal if it has a sibling *.meta file // We don't want to copy internal textures into Unity because they are already there. bool isInternal = File.Exists(image.AbsolutePath + ".meta"); if (isInternal) { // The texture is already in the Unity project so don't import XElement xmlInternalTexture = new XElement("InternalTexture"); // The path to the texture will be WRT to the Unity project root string assetsFolder = GetUnityAssetsPath(image.AbsolutePath); string assetPath = image.AbsolutePath.Remove(0, assetsFolder.Length); assetPath = "Assets" + assetPath; assetPath = assetPath.Replace("\\", "/"); Logger.WriteLine("InternalTexture : {0}", assetPath); // Path to texture in the asset directory xmlInternalTexture.SetAttributeValue("assetPath", assetPath); // Transparent color key? if (!String.IsNullOrEmpty(image.TransparentColor)) { xmlInternalTexture.SetAttributeValue("alphaColorKey", image.TransparentColor); } // Are we using depth shaders on our materials? if (Tiled2Unity.Settings.DepthBufferEnabled) { xmlInternalTexture.SetAttributeValue("usesDepthShaders", true); } // Will the material be loaded as a resource? if (this.tmxMap.IsResource) { xmlInternalTexture.SetAttributeValue("isResource", true); } elements.Add(xmlInternalTexture); } else { // The texture needs to be imported into the Unity project (under Tiled2Unity's care) XElement xmlImportTexture = new XElement("ImportTexture"); // Note that compression is not available in Unity. Go with Base64 string. Blerg. Logger.WriteLine("ImportTexture : will import '{0}' to {1}", image.AbsolutePath, Path.Combine(exportToUnityProjectPath, "Textures")); // Is there a color key for transparency? if (!String.IsNullOrEmpty(image.TransparentColor)) { xmlImportTexture.SetAttributeValue("alphaColorKey", image.TransparentColor); } // Are we using depth shaders on our materials? if (Tiled2Unity.Settings.DepthBufferEnabled) { xmlImportTexture.SetAttributeValue("usesDepthShaders", true); } // Will the material be loaded as a resource? if (this.tmxMap.IsResource) { xmlImportTexture.SetAttributeValue("isResource", true); } // Bake the image file into the xml xmlImportTexture.Add(new XAttribute("filename", Path.GetFileName(image.AbsolutePath)), FileToBase64String(image.AbsolutePath)); elements.Add(xmlImportTexture); } } } return(elements); }
public static List <TmxMesh> FromTmxTile(TmxTile tmxTile, TmxMap tmxMap) { List <TmxMesh> list = new List <TmxMesh>(); int num = 0; foreach (TmxFrame frame in tmxTile.Animation.Frames) { uint globalTileId = frame.GlobalTileId; TmxTile tmxTile2 = tmxMap.Tiles[globalTileId]; TmxMesh tmxMesh = new TmxMesh(); tmxMesh.TileIds = new uint[1]; tmxMesh.TileIds[0] = globalTileId; tmxMesh.UniqueMeshName = string.Format("{0}_mesh_tile_{1}", tmxMap.Name, TmxMath.GetTileIdWithoutFlags(globalTileId).ToString("D4")); tmxMesh.TmxImage = tmxTile2.TmxImage; tmxMesh.ObjectName = "tile_obj"; tmxMesh.StartTimeMs = num; tmxMesh.DurationMs = frame.DurationMs; tmxMesh.FullAnimationDurationMs = tmxTile.Animation.TotalTimeMs; if (tmxMesh.DurationMs != 0) { TmxMesh tmxMesh2 = tmxMesh; tmxMesh2.ObjectName += $"[{num}-{num + tmxMesh.DurationMs}][{tmxMesh.FullAnimationDurationMs}]"; } num += frame.DurationMs; list.Add(tmxMesh); } return(list); }
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 List <XElement> CreateImportFilesElements(string exportToUnityProjectPath) { List <XElement> elements = new List <XElement>(); // Add the mesh file as raw text { StringWriter objBuilder = BuildObjString(); XElement mesh = new XElement("ImportMesh", new XAttribute("filename", this.tmxMap.Name + ".obj"), StringToBase64String(objBuilder.ToString())); elements.Add(mesh); } { // Add all image files as compressed base64 strings var imagePaths = from layer in this.tmxMap.Layers where layer.Visible == true from rawTileId in layer.TileIds where rawTileId != 0 let tileId = TmxMath.GetTileIdWithoutFlags(rawTileId) let tile = this.tmxMap.Tiles[tileId] select tile.TmxImage.Path; imagePaths = imagePaths.Distinct(); // Do not import files if they are already in the project (in the /Assets/ directory of where we're exporting too) string unityAssetsDir = Path.Combine(exportToUnityProjectPath, "Assets"); foreach (string path in imagePaths) { // If the copy from location comes from within the project we want to copy to, then don't do it. // This allows us to have tileset images that are alreday in use by the Unity project string saveToAssetsDir = unityAssetsDir.ToLower(); string copyFromDir = path.ToLower(); if (copyFromDir.StartsWith(saveToAssetsDir)) { // The path to the texture will be WRT to the Unity project root string assetPath = path.Remove(0, exportToUnityProjectPath.Length); assetPath = assetPath.TrimStart('\\'); assetPath = assetPath.TrimStart('/'); Program.WriteLine("InternalTexture : {0}", assetPath); XElement texture = new XElement("InternalTexture", new XAttribute("assetPath", assetPath)); elements.Add(texture); } else { // Note that compression is not available in Unity. Go with Base64 string. Blerg. Program.WriteLine("ImportTexture : will import '{0}' to {1}", path, Path.Combine(unityAssetsDir, "Tiled2Unity\\Textures\\")); XElement texture = new XElement("ImportTexture", new XAttribute("filename", Path.GetFileName(path)), FileToBase64String(path)); //FileToCompressedBase64String(path)); elements.Add(texture); } } } return(elements); }
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 uint GetTileIdAt(int x, int y) { uint tileId = GetRawTileIdAt(x, y); return(TmxMath.GetTileIdWithoutFlags(tileId)); }
public static TmxLayer FromXml(XElement elem, TmxMap tmxMap) { Program.WriteVerbose(elem.ToString()); TmxLayer tmxLayer = new TmxLayer(tmxMap); // Order within Xml file is import for layer types tmxLayer.XmlElementIndex = elem.NodesBeforeSelf().Count(); // Have to decorate layer names in order to force them into being unique // Also, can't have whitespace in the name because Unity will add underscores tmxLayer.Name = TmxHelper.GetAttributeAsString(elem, "name"); tmxLayer.Visible = TmxHelper.GetAttributeAsInt(elem, "visible", 1) == 1; tmxLayer.Opacity = TmxHelper.GetAttributeAsFloat(elem, "opacity", 1); PointF offset = new PointF(0, 0); offset.X = TmxHelper.GetAttributeAsFloat(elem, "offsetx", 0); offset.Y = TmxHelper.GetAttributeAsFloat(elem, "offsety", 0); tmxLayer.Offset = offset; // Set our properties tmxLayer.Properties = TmxProperties.FromXml(elem); // Set the "ignore" setting on this layer tmxLayer.Ignore = tmxLayer.Properties.GetPropertyValueAsEnum <IgnoreSettings>("unity:ignore", IgnoreSettings.False); // We can build a layer from a "tile layer" (default) or an "image layer" if (elem.Name == "layer") { tmxLayer.Width = TmxHelper.GetAttributeAsInt(elem, "width"); tmxLayer.Height = TmxHelper.GetAttributeAsInt(elem, "height"); tmxLayer.ParseData(elem.Element("data")); } else if (elem.Name == "imagelayer") { XElement xmlImage = elem.Element("image"); if (xmlImage == null) { Program.WriteWarning("Image Layer '{0}' is being ignored since it has no image.", tmxLayer.Name); tmxLayer.Ignore = IgnoreSettings.True; return(tmxLayer); } // An image layer is sort of like an tile layer but with just one tile tmxLayer.Width = 1; tmxLayer.Height = 1; // Find the "tile" that matches our image string imagePath = TmxHelper.GetAttributeAsFullPath(elem.Element("image"), "source"); TmxTile tile = tmxMap.Tiles.First(t => t.Value.TmxImage.AbsolutePath == imagePath).Value; tmxLayer.TileIds = new uint[1] { tile.GlobalId }; // The image layer needs to be tranlated in an interesting way when expressed as a tile layer PointF translated = tmxLayer.Offset; // Make up for height of a regular tile in the map translated.Y -= (float)tmxMap.TileHeight; // Make up for the height of this image translated.Y += (float)tile.TmxImage.Size.Height; // Correct for any orientation effects on the map (like isometric) // (We essentially undo the translation via orientation here) PointF orientation = TmxMath.TileCornerInScreenCoordinates(tmxMap, 0, 0); translated.X -= orientation.X; translated.Y -= orientation.Y; // Translate by the x and y coordiantes translated.X += TmxHelper.GetAttributeAsFloat(elem, "x", 0); translated.Y += TmxHelper.GetAttributeAsFloat(elem, "y", 0); tmxLayer.Offset = translated; } // Sometimes TMX files have "dead" tiles in them (tiles that were removed but are still referenced) // Remove these tiles from the layer by replacing them with zero for (int t = 0; t < tmxLayer.TileIds.Length; ++t) { uint tileId = tmxLayer.TileIds[t]; tileId = TmxMath.GetTileIdWithoutFlags(tileId); if (!tmxMap.Tiles.ContainsKey(tileId)) { tmxLayer.TileIds[t] = 0; } } // Each layer will be broken down into "meshes" which are collections of tiles matching the same texture or animation tmxLayer.Meshes = TmxMesh.ListFromTmxLayer(tmxLayer); // Each layer may contain different collision types which are themselves put into "Collison Layers" to be processed later tmxLayer.UnityLayerOverrideName = tmxLayer.Properties.GetPropertyValueAsString("unity:layer", ""); tmxLayer.BuildCollisionLayers(); return(tmxLayer); }