| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.IO.Compression;
- using System.Linq;
- using Microsoft.Xna.Framework.Content.Pipeline;
- using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
- using MonoGame.Extended.Content.Tiled;
- using MonoGame.Extended.Tiled;
- namespace MonoGame.Extended.Content.Pipeline.Tiled
- {
- public static class TiledMapContentHelper
- {
- public static void Process(TiledMapObjectContent obj, ContentProcessorContext context)
- {
- if (!string.IsNullOrWhiteSpace(obj.TemplateSource))
- {
- var externalReference = new ExternalReference<TiledMapObjectLayerContent>(obj.TemplateSource);
- var template = context.BuildAndLoadAsset<TiledMapObjectLayerContent, TiledMapObjectTemplateContent>(externalReference, "");
- // Nothing says a template can't reference another template.
- // Yay recusion!
- Process(template.Object, context);
- if (!obj._globalIdentifier.HasValue && template.Object._globalIdentifier.HasValue)
- obj.GlobalIdentifier = template.Object.GlobalIdentifier;
- if (!obj._height.HasValue && template.Object._height.HasValue)
- obj.Height = template.Object.Height;
- if (!obj._identifier.HasValue && template.Object._identifier.HasValue)
- obj.Identifier = template.Object.Identifier;
- if (!obj._rotation.HasValue && template.Object._rotation.HasValue)
- obj.Rotation = template.Object.Rotation;
- if (!obj._visible.HasValue && template.Object._visible.HasValue)
- obj.Visible = template.Object.Visible;
- if (!obj._width.HasValue && template.Object._width.HasValue)
- obj.Width = template.Object.Width;
- if (!obj._x.HasValue && template.Object._x.HasValue)
- obj.X = template.Object.X;
- if (!obj._y.HasValue && template.Object._y.HasValue)
- obj.Y = template.Object.Y;
- if (obj.Ellipse == null && template.Object.Ellipse != null)
- obj.Ellipse = template.Object.Ellipse;
- if (string.IsNullOrWhiteSpace(obj.Name) && !string.IsNullOrWhiteSpace(template.Object.Name))
- obj.Name = template.Object.Name;
- if (obj.Polygon == null && template.Object.Polygon != null)
- obj.Polygon = template.Object.Polygon;
- if (obj.Polyline == null && template.Object.Polyline != null)
- obj.Polyline = template.Object.Polyline;
- foreach (var tProperty in template.Object.Properties)
- {
- if (!obj.Properties.Exists(p => p.Name == tProperty.Name))
- obj.Properties.Add(tProperty);
- }
- if (string.IsNullOrWhiteSpace(obj.Type) && !string.IsNullOrWhiteSpace(template.Object.Type))
- obj.Type = template.Object.Type;
- if (string.IsNullOrWhiteSpace(obj.Class) && !string.IsNullOrWhiteSpace(template.Object.Class))
- obj.Class = template.Object.Class;
- }
- }
- }
- [ContentProcessor(DisplayName = "Tiled Map Processor - MonoGame.Extended")]
- public class TiledMapProcessor : ContentProcessor<TiledMapContentItem, TiledMapContentItem>
- {
- public override TiledMapContentItem Process(TiledMapContentItem contentItem, ContentProcessorContext context)
- {
- try
- {
- ContentLogger.Logger = context.Logger;
- var map = contentItem.Data;
- if (map.Orientation == TiledMapOrientationContent.Hexagonal || map.Orientation == TiledMapOrientationContent.Staggered)
- throw new NotSupportedException($"{map.Orientation} Tiled Maps are currently not implemented!");
- foreach (var tileset in map.Tilesets)
- {
- if (string.IsNullOrWhiteSpace(tileset.Source))
- {
- // Load the Texture2DContent for the tileset as it will be saved into the map content file.
- contentItem.BuildExternalReference<Texture2DContent>(context, tileset.Image);
- }
- else
- {
- // Link to the tileset for the content loader to load at runtime.
- //var externalReference = new ExternalReference<TiledMapTilesetContent>(tileset.Source);
- //tileset.Content = context.BuildAsset<TiledMapTilesetContent, TiledMapTilesetContent>(externalReference, "");
- contentItem.BuildExternalReference<TiledMapTilesetContent>(context, tileset.Source);
- }
- }
- ProcessLayers(contentItem, map, context, map.Layers);
- return contentItem;
- }
- catch (Exception ex)
- {
- context.Logger.LogImportantMessage(ex.Message);
- throw;
- }
- }
- private static void ProcessLayers(TiledMapContentItem contentItem, TiledMapContent map, ContentProcessorContext context, List<TiledMapLayerContent> layers)
- {
- foreach (var layer in layers)
- {
- switch (layer)
- {
- case TiledMapImageLayerContent imageLayer:
- ContentLogger.Log($"Processing image layer '{imageLayer.Name}'");
- contentItem.BuildExternalReference<Texture2DContent>(context, imageLayer.Image);
- ContentLogger.Log($"Processed image layer '{imageLayer.Name}'");
- break;
- case TiledMapTileLayerContent tileLayer when tileLayer.Data.Chunks.Count > 0:
- throw new NotSupportedException($"{map.FilePath} contains data chunks. These are currently not supported.");
- case TiledMapTileLayerContent tileLayer:
- var data = tileLayer.Data;
- var encodingType = data.Encoding ?? "xml";
- var compressionType = data.Compression ?? "xml";
- ContentLogger.Log($"Processing tile layer '{tileLayer.Name}': Encoding: '{encodingType}', Compression: '{compressionType}'");
- var tileData = DecodeTileLayerData(encodingType, tileLayer);
- var tiles = CreateTiles(map.RenderOrder, map.Width, map.Height, tileData);
- tileLayer.Tiles = tiles;
- ContentLogger.Log($"Processed tile layer '{tileLayer}': {tiles.Length} tiles");
- break;
- case TiledMapObjectLayerContent objectLayer:
- ContentLogger.Log($"Processing object layer '{objectLayer.Name}'");
- foreach (var obj in objectLayer.Objects)
- TiledMapContentHelper.Process(obj, context);
- ContentLogger.Log($"Processed object layer '{objectLayer.Name}'");
- break;
- case TiledMapGroupLayerContent groupLayer:
- ProcessLayers(contentItem, map, context, groupLayer.Layers);
- break;
- }
- }
- }
- private static List<TiledMapTileContent> DecodeTileLayerData(string encodingType, TiledMapTileLayerContent tileLayer)
- {
- List<TiledMapTileContent> tiles;
- switch (encodingType)
- {
- case "xml":
- tiles = tileLayer.Data.Tiles;
- break;
- case "csv":
- tiles = DecodeCommaSeperatedValuesData(tileLayer.Data);
- break;
- case "base64":
- tiles = DecodeBase64Data(tileLayer.Data, tileLayer.Width, tileLayer.Height);
- break;
- default:
- throw new NotSupportedException($"The tile layer encoding '{encodingType}' is not supported.");
- }
- return tiles;
- }
- private static TiledMapTile[] CreateTiles(TiledMapTileDrawOrderContent renderOrder, int mapWidth, int mapHeight, List<TiledMapTileContent> tileData)
- {
- TiledMapTile[] tiles;
- switch (renderOrder)
- {
- case TiledMapTileDrawOrderContent.LeftDown:
- tiles = CreateTilesInLeftDownOrder(tileData, mapWidth, mapHeight).ToArray();
- break;
- case TiledMapTileDrawOrderContent.LeftUp:
- tiles = CreateTilesInLeftUpOrder(tileData, mapWidth, mapHeight).ToArray();
- break;
- case TiledMapTileDrawOrderContent.RightDown:
- tiles = CreateTilesInRightDownOrder(tileData, mapWidth, mapHeight).ToArray();
- break;
- case TiledMapTileDrawOrderContent.RightUp:
- tiles = CreateTilesInRightUpOrder(tileData, mapWidth, mapHeight).ToArray();
- break;
- default:
- throw new NotSupportedException($"{renderOrder} is not supported.");
- }
- return tiles.ToArray();
- }
- private static IEnumerable<TiledMapTile> CreateTilesInLeftDownOrder(List<TiledMapTileContent> tileLayerData, int mapWidth, int mapHeight)
- {
- for (var y = 0; y < mapHeight; y++)
- {
- for (var x = mapWidth - 1; x >= 0; x--)
- {
- var dataIndex = x + y * mapWidth;
- var globalIdentifier = tileLayerData[dataIndex].GlobalIdentifier;
- if (globalIdentifier == 0)
- continue;
- var tile = new TiledMapTile(globalIdentifier, (ushort)x, (ushort)y);
- yield return tile;
- }
- }
- }
- private static IEnumerable<TiledMapTile> CreateTilesInLeftUpOrder(List<TiledMapTileContent> tileLayerData, int mapWidth, int mapHeight)
- {
- for (var y = mapHeight - 1; y >= 0; y--)
- {
- for (var x = mapWidth - 1; x >= 0; x--)
- {
- var dataIndex = x + y * mapWidth;
- var globalIdentifier = tileLayerData[dataIndex].GlobalIdentifier;
- if (globalIdentifier == 0)
- continue;
- var tile = new TiledMapTile(globalIdentifier, (ushort)x, (ushort)y);
- yield return tile;
- }
- }
- }
- private static IEnumerable<TiledMapTile> CreateTilesInRightDownOrder(List<TiledMapTileContent> tileLayerData, int mapWidth, int mapHeight)
- {
- for (var y = 0; y < mapHeight; y++)
- {
- for (var x = 0; x < mapWidth; x++)
- {
- var dataIndex = x + y * mapWidth;
- var globalIdentifier = tileLayerData[dataIndex].GlobalIdentifier;
- if (globalIdentifier == 0)
- continue;
- var tile = new TiledMapTile(globalIdentifier, (ushort)x, (ushort)y);
- yield return tile;
- }
- }
- }
- private static IEnumerable<TiledMapTile> CreateTilesInRightUpOrder(List<TiledMapTileContent> tileLayerData, int mapWidth, int mapHeight)
- {
- for (var y = mapHeight - 1; y >= 0; y--)
- {
- for (var x = mapWidth - 1; x >= 0; x--)
- {
- var dataIndex = x + y * mapWidth;
- var globalIdentifier = tileLayerData[dataIndex].GlobalIdentifier;
- if (globalIdentifier == 0)
- continue;
- var tile = new TiledMapTile(globalIdentifier, (ushort)x, (ushort)y);
- yield return tile;
- }
- }
- }
- private static List<TiledMapTileContent> DecodeBase64Data(TiledMapTileLayerDataContent data, int width, int height)
- {
- var tileList = new List<TiledMapTileContent>();
- var encodedData = data.Value.Trim();
- var decodedData = Convert.FromBase64String(encodedData);
- using (var stream = OpenStream(decodedData, data.Compression))
- {
- using (var reader = new BinaryReader(stream))
- {
- data.Tiles = new List<TiledMapTileContent>();
- for (var y = 0; y < width; y++)
- {
- for (var x = 0; x < height; x++)
- {
- var gid = reader.ReadUInt32();
- tileList.Add(new TiledMapTileContent
- {
- GlobalIdentifier = gid
- });
- }
- }
- }
- }
- return tileList;
- }
- private static List<TiledMapTileContent> DecodeCommaSeperatedValuesData(TiledMapTileLayerDataContent data)
- {
- return data.Value
- .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
- .Select(uint.Parse)
- .Select(x => new TiledMapTileContent { GlobalIdentifier = x })
- .ToList();
- }
- private static Stream OpenStream(byte[] decodedData, string compressionMode)
- {
- var memoryStream = new MemoryStream(decodedData, false);
- return compressionMode switch
- {
- "gzip" => new GZipStream(memoryStream, CompressionMode.Decompress),
- "zlib" => new ZLibStream(memoryStream, CompressionMode.Decompress),
- _ => memoryStream
- };
- }
- }
- }
|