diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 0000000..727dfd7 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "0.38.5", + "commands": [ + "dotnet-cake" + ] + } + } +} \ No newline at end of file diff --git a/Demos/AnimationDemo.cs b/Demos/AnimationDemo.cs index a9a4179..2b22b7d 100644 --- a/Demos/AnimationDemo.cs +++ b/Demos/AnimationDemo.cs @@ -1,7 +1,6 @@ using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Microsoft.Xna.Framework.Input; using MLEM.Animations; using MLEM.Misc; using MLEM.Startup; diff --git a/Demos/PathfindingDemo.cs b/Demos/PathfindingDemo.cs index 5f4d05b..24343e3 100644 --- a/Demos/PathfindingDemo.cs +++ b/Demos/PathfindingDemo.cs @@ -4,7 +4,6 @@ using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; -using MLEM.Input; using MLEM.Misc; using MLEM.Pathfinding; using MLEM.Startup; diff --git a/Jenkinsfile b/Jenkinsfile index ff10585..24c4614 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,8 +3,8 @@ pipeline { stages { stage('Cake Build') { steps { - sh 'chmod +x ./build.sh' - sh './build.sh -Target=Publish -Branch=' + env.BRANCH_NAME + sh 'dotnet tool restore' + sh 'dotnet dotnet-cake -Target=Publish -Branch=' + env.BRANCH_NAME } } stage('Document') { @@ -12,7 +12,7 @@ pipeline { branch 'release' } steps { - sh './build.sh -Target=Document' + sh 'dotnet dotnet-cake -Target=Document' sh 'cp Docs/_site/** /var/www/MLEM/ -r' } } diff --git a/MLEM.Data/DataTextureAtlas.cs b/MLEM.Data/DataTextureAtlas.cs index 775bd72..277bd4e 100644 --- a/MLEM.Data/DataTextureAtlas.cs +++ b/MLEM.Data/DataTextureAtlas.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; using MLEM.Textures; namespace MLEM.Data { @@ -19,7 +20,7 @@ namespace MLEM.Data { /// /// The texture to use for this atlas /// - public readonly Texture2D Texture; + public readonly TextureRegion Texture; /// /// Returns the texture region with the given name, or null if it does not exist. /// @@ -37,21 +38,23 @@ namespace MLEM.Data { private readonly Dictionary regions = new Dictionary(); - /// - /// Creates a new data texture atlas with the given texture and region amount. - /// - /// The texture to use for this atlas - public DataTextureAtlas(Texture2D texture) { + private DataTextureAtlas(TextureRegion texture) { this.Texture = texture; } - internal static DataTextureAtlas Load(ContentManager content, string texturePath, string infoPath, bool pivotRelative) { - var info = new FileInfo(Path.Combine(content.RootDirectory, infoPath ?? $"{texturePath}.atlas")); + /// + /// Loads a from the given loaded texture and texture data file. + /// + /// The texture to use for this data texture atlas + /// The content manager to use for loading + /// The path, including extension, to the atlas info file + /// If this value is true, then the pivot points passed in the info file will be relative to the coordinates of the texture region, not relative to the entire texture's origin. + /// A new data texture atlas with the given settings + public static DataTextureAtlas LoadAtlasData(TextureRegion texture, ContentManager content, string infoPath, bool pivotRelative = false) { + var info = new FileInfo(Path.Combine(content.RootDirectory, infoPath)); string text; using (var reader = info.OpenText()) text = reader.ReadToEnd(); - - var texture = content.Load(texturePath); var atlas = new DataTextureAtlas(texture); // parse each texture region: " loc piv " @@ -92,7 +95,8 @@ namespace MLEM.Data { /// If this value is true, then the pivot points passed in the info file will be relative to the coordinates of the texture region, not relative to the entire texture's origin. /// A new data texture atlas with the given settings public static DataTextureAtlas LoadTextureAtlas(this ContentManager content, string texturePath, string infoPath = null, bool pivotRelative = false) { - return DataTextureAtlas.Load(content, texturePath, infoPath, pivotRelative); + var texture = new TextureRegion(content.Load(texturePath)); + return DataTextureAtlas.LoadAtlasData(texture, content, infoPath ?? $"{texturePath}.atlas", pivotRelative); } } diff --git a/MLEM.Data/RuntimeTexturePacker.cs b/MLEM.Data/RuntimeTexturePacker.cs new file mode 100644 index 0000000..d75f8d4 --- /dev/null +++ b/MLEM.Data/RuntimeTexturePacker.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; +using MLEM.Textures; + +namespace MLEM.Data { + /// + /// A runtime texture packer provides the user with the ability to combine multiple instances into a single texture. + /// Packing textures in this manner allows for faster rendering, as fewer texture swaps are required. + /// The resulting texture segments are returned as instances. + /// + public class RuntimeTexturePacker { + + private readonly List textures = new List(); + + /// + /// The generated packed texture. + /// This value is null before is called. + /// + public Texture2D PackedTexture { get; private set; } + /// + /// The time that it took to calculate the required areas the last time that was called + /// + public TimeSpan LastCalculationTime { get; private set; } + /// + /// The time that it took to copy the texture data from the invidiual textures onto the the last time that was called + /// + public TimeSpan LastPackTime { get; private set; } + /// + /// The time that took the last time it was called + /// + public TimeSpan LastTotalTime => this.LastCalculationTime + this.LastPackTime; + private readonly int maxWidth; + + /// + /// Creates a new runtime texture packer with the given settings + /// + /// The maximum width that the packed texture can have. Defaults to 2048. + public RuntimeTexturePacker(int maxWidth = 2048) { + this.maxWidth = maxWidth; + } + + /// + /// Adds a new texture to this texture packer to be packed. + /// The passed is invoked in and provides the caller with the resulting texture region on the . + /// + /// The texture to pack + /// The result callback which will receive the resulting texture region + public void Add(Texture2D texture, Action result) { + this.Add(new TextureRegion(texture), result); + } + + /// + /// Adds a new to this texture packer to be packed. + /// The passed is invoked in and provides the caller with the resulting texture region on the . + /// + /// The texture to pack + /// The result callback which will receive the resulting texture region + /// Thrown when trying to add data to a packer that has already been packed, or when trying to add a texture width a width greater than the defined max width + public void Add(TextureRegion texture, Action result) { + if (this.PackedTexture != null) + throw new InvalidOperationException("Cannot add texture to a texture packer that is already packed"); + if (texture.Width > this.maxWidth) + throw new InvalidOperationException($"Cannot add texture with width {texture.Width} to a texture packer with max width {this.maxWidth}"); + this.textures.Add(new Request(texture, result)); + } + + /// + /// Packs all of the textures and texture regions added using into one texture. + /// The resulting texture will be stored in . + /// All of the result callbacks that were added will also be invoked. + /// + /// The graphics device to use for texture generation + /// Thrown when calling this method on a texture packer that has already been packed + public void Pack(GraphicsDevice device) { + if (this.PackedTexture != null) + throw new InvalidOperationException("Cannot pack a texture packer that is already packed"); + + // set pack areas for each request + var stopwatch = Stopwatch.StartNew(); + foreach (var request in this.textures.OrderByDescending(t => t.Texture.Width * t.Texture.Height)) { + var area = this.FindFreeArea(new Point(request.Texture.Width, request.Texture.Height)); + request.PackedArea = area; + } + stopwatch.Stop(); + this.LastCalculationTime = stopwatch.Elapsed; + + // generate texture based on required size + var width = this.textures.Max(t => t.PackedArea.Right); + var height = this.textures.Max(t => t.PackedArea.Bottom); + this.PackedTexture = new Texture2D(device, width, height); + device.Disposing += (o, a) => this.PackedTexture.Dispose(); + + // copy texture data onto the packed texture + stopwatch.Restart(); + using (var data = this.PackedTexture.GetTextureData()) { + foreach (var request in this.textures) + CopyRegion(data, request); + } + stopwatch.Stop(); + this.LastPackTime = stopwatch.Elapsed; + + // invoke callbacks + foreach (var request in this.textures) + request.Result.Invoke(new TextureRegion(this.PackedTexture, request.PackedArea)); + } + + private Rectangle FindFreeArea(Point size) { + var pos = new Point(0, 0); + var lowestY = int.MaxValue; + while (true) { + var intersected = false; + var area = new Rectangle(pos, size); + foreach (var tex in this.textures) { + if (tex.PackedArea.Intersects(area)) { + pos.X = tex.PackedArea.Right; + // when we move down, we want to move down by the smallest intersecting texture's height + if (lowestY > tex.PackedArea.Bottom) + lowestY = tex.PackedArea.Bottom; + intersected = true; + break; + } + } + if (!intersected) + return area; + if (pos.X + size.X > this.maxWidth) { + pos.X = 0; + pos.Y = lowestY; + lowestY = int.MaxValue; + } + } + } + + private static void CopyRegion(TextureExtensions.TextureData destination, Request request) { + using (var data = request.Texture.Texture.GetTextureData()) { + for (var x = 0; x < request.Texture.Width; x++) { + for (var y = 0; y < request.Texture.Height; y++) { + var dest = request.PackedArea.Location + new Point(x, y); + var src = request.Texture.Position + new Point(x, y); + destination[dest] = data[src]; + } + } + } + } + + private class Request { + + public readonly TextureRegion Texture; + public readonly Action Result; + public Rectangle PackedArea; + + public Request(TextureRegion texture, Action result) { + this.Texture = texture; + this.Result = result; + } + + } + + } +} \ No newline at end of file diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index df9486f..e832f94 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using MLEM.Extensions; diff --git a/MLEM.Ui/Style/UiStyle.cs b/MLEM.Ui/Style/UiStyle.cs index 6585b8f..d6eac48 100644 --- a/MLEM.Ui/Style/UiStyle.cs +++ b/MLEM.Ui/Style/UiStyle.cs @@ -1,6 +1,5 @@ using System; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Audio; using MLEM.Font; using MLEM.Formatting; using MLEM.Misc; diff --git a/MLEM.Ui/UiControls.cs b/MLEM.Ui/UiControls.cs index 9edb9a2..2549ef7 100644 --- a/MLEM.Ui/UiControls.cs +++ b/MLEM.Ui/UiControls.cs @@ -1,7 +1,5 @@ -using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input.Touch; diff --git a/MLEM.Ui/UiSystem.cs b/MLEM.Ui/UiSystem.cs index 3f3d98e..2807d86 100644 --- a/MLEM.Ui/UiSystem.cs +++ b/MLEM.Ui/UiSystem.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Text.RegularExpressions; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using MLEM.Extensions; using MLEM.Formatting; using MLEM.Formatting.Codes; using MLEM.Input; diff --git a/MLEM/Extensions/CharExtensions.cs b/MLEM/Extensions/CharExtensions.cs index 0c2be9d..78a0dbc 100644 --- a/MLEM/Extensions/CharExtensions.cs +++ b/MLEM/Extensions/CharExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; namespace MLEM.Extensions { diff --git a/MLEM/Extensions/SpriteBatchExtensions.cs b/MLEM/Extensions/SpriteBatchExtensions.cs index d31e517..2b3678a 100644 --- a/MLEM/Extensions/SpriteBatchExtensions.cs +++ b/MLEM/Extensions/SpriteBatchExtensions.cs @@ -1,4 +1,3 @@ -using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Misc; diff --git a/MLEM/Font/GenericFont.cs b/MLEM/Font/GenericFont.cs index 856cd62..0a8a40a 100644 --- a/MLEM/Font/GenericFont.cs +++ b/MLEM/Font/GenericFont.cs @@ -21,6 +21,11 @@ namespace MLEM.Font { /// Whereas a regular would have to explicitly support this character for width calculations, generic fonts implicitly support it in . /// public const char Nbsp = '\u00A0'; + /// + /// This field holds the unicode representation of a zero-width space. + /// Whereas a regular would have to explicitly support this character for width calculations and string splitting, generic fonts implicitly support it in and . + /// + public const char Zwsp = '\u8203'; /// /// The bold version of this font. @@ -108,6 +113,9 @@ namespace MLEM.Font { case Nbsp: xOffset += this.MeasureChar(' ').X; break; + case Zwsp: + // don't add width for a zero-width space + break; default: xOffset += this.MeasureChar(c).X; break; @@ -154,7 +162,7 @@ namespace MLEM.Font { /// /// Splits a string to a given maximum width, adding newline characters between each line. - /// Also splits long words. + /// Also splits long words and supports zero-width spaces. /// /// The text to split into multiple lines /// The maximum width that each line should have @@ -164,7 +172,7 @@ namespace MLEM.Font { var total = new StringBuilder(); foreach (var line in text.Split('\n')) { var curr = new StringBuilder(); - foreach (var word in line.Split(' ')) { + foreach (var word in line.Split(' ', Zwsp)) { if (this.MeasureString(word).X * scale >= width) { if (curr.Length > 0) { total.Append(curr).Append('\n'); diff --git a/MLEM/Font/GenericSpriteFont.cs b/MLEM/Font/GenericSpriteFont.cs index 37ffe3c..baac7eb 100644 --- a/MLEM/Font/GenericSpriteFont.cs +++ b/MLEM/Font/GenericSpriteFont.cs @@ -3,7 +3,6 @@ using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Extensions; -using MLEM.Misc; namespace MLEM.Font { /// diff --git a/MLEM/Formatting/Codes/ImageCode.cs b/MLEM/Formatting/Codes/ImageCode.cs index dbbfbbc..5a7fe34 100644 --- a/MLEM/Formatting/Codes/ImageCode.cs +++ b/MLEM/Formatting/Codes/ImageCode.cs @@ -12,10 +12,12 @@ namespace MLEM.Formatting.Codes { public class ImageCode : Code { private readonly SpriteAnimation image; + private readonly bool copyTextColor; /// - public ImageCode(Match match, Regex regex, SpriteAnimation image) : base(match, regex) { + public ImageCode(Match match, Regex regex, SpriteAnimation image, bool copyTextColor) : base(match, regex) { this.image = image; + this.copyTextColor = copyTextColor; } /// @@ -35,7 +37,8 @@ namespace MLEM.Formatting.Codes { /// public override void DrawSelf(GameTime time, SpriteBatch batch, Vector2 pos, GenericFont font, Color color, float scale, float depth) { - batch.Draw(this.image.CurrentRegion, new RectangleF(pos, new Vector2(font.LineHeight * scale)), Color.White.CopyAlpha(color)); + var actualColor = this.copyTextColor ? color : Color.White.CopyAlpha(color); + batch.Draw(this.image.CurrentRegion, new RectangleF(pos, new Vector2(font.LineHeight * scale)), actualColor); } } @@ -51,13 +54,14 @@ namespace MLEM.Formatting.Codes { /// The formatter to add the code to /// The name of the formatting code. The regex for this code will be between angle brackets. /// The image to render at the code's position - public static void AddImage(this TextFormatter formatter, string name, TextureRegion image) { - formatter.AddImage(name, new SpriteAnimation(1, image)); + /// Whether or not the image code should use the text's color instead of White + public static void AddImage(this TextFormatter formatter, string name, TextureRegion image, bool copyTextColor = false) { + formatter.AddImage(name, new SpriteAnimation(1, image), copyTextColor); } - /// - public static void AddImage(this TextFormatter formatter, string name, SpriteAnimation image) { - formatter.Codes.Add(new Regex($""), (f, m, r) => new ImageCode(m, r, image)); + /// + public static void AddImage(this TextFormatter formatter, string name, SpriteAnimation image, bool copyTextColor = false) { + formatter.Codes.Add(new Regex($""), (f, m, r) => new ImageCode(m, r, image, copyTextColor)); } } diff --git a/MLEM/Formatting/TextFormatter.cs b/MLEM/Formatting/TextFormatter.cs index e10a915..ab23037 100644 --- a/MLEM/Formatting/TextFormatter.cs +++ b/MLEM/Formatting/TextFormatter.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; diff --git a/MLEM/Misc/Direction2.cs b/MLEM/Misc/Direction2.cs index 71e8ee4..036aa05 100644 --- a/MLEM/Misc/Direction2.cs +++ b/MLEM/Misc/Direction2.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; -using MLEM.Extensions; namespace MLEM.Misc { /// diff --git a/MLEM/Misc/GenericDataHolder.cs b/MLEM/Misc/GenericDataHolder.cs index 2bd1b22..c9bdd64 100644 --- a/MLEM/Misc/GenericDataHolder.cs +++ b/MLEM/Misc/GenericDataHolder.cs @@ -3,21 +3,14 @@ using System.Collections.Generic; using System.Runtime.Serialization; namespace MLEM.Misc { - /// - /// Represents an object that can hold generic key-value based data. - /// A lot of MLEM components extend this class to allow for users to add additional data to them easily. - /// + /// [DataContract] - public class GenericDataHolder { + public class GenericDataHolder : IGenericDataHolder { - [DataMember] + [DataMember(EmitDefaultValue = false)] private Dictionary data; - /// - /// Store a piece of generic data on this object. - /// - /// The key to store the data by - /// The data to store in the object + /// public void SetData(string key, object data) { if (data == default) { if (this.data != null) @@ -29,22 +22,14 @@ namespace MLEM.Misc { } } - /// - /// Returns a piece of generic data of the given type on this object. - /// - /// The key that the data is stored by - /// The type of the data stored - /// The data, or default if it doesn't exist + /// public T GetData(string key) { if (this.data != null && this.data.TryGetValue(key, out var val) && val is T t) return t; return default; } - /// - /// Returns all of the generic data that this object stores. - /// - /// The generic data on this object + /// public IReadOnlyCollection GetDataKeys() { if (this.data == null) return Array.Empty(); @@ -52,4 +37,33 @@ namespace MLEM.Misc { } } + + /// + /// Represents an object that can hold generic key-value based data. + /// A lot of MLEM components extend this class to allow for users to add additional data to them easily. + /// + public interface IGenericDataHolder { + + /// + /// Store a piece of generic data on this object. + /// + /// The key to store the data by + /// The data to store in the object + void SetData(string key, object data); + + /// + /// Returns a piece of generic data of the given type on this object. + /// + /// The key that the data is stored by + /// The type of the data stored + /// The data, or default if it doesn't exist + T GetData(string key); + + /// + /// Returns all of the generic data that this object stores. + /// + /// The generic data on this object + IReadOnlyCollection GetDataKeys(); + + } } \ No newline at end of file diff --git a/MLEM/Pathfinding/AStar.cs b/MLEM/Pathfinding/AStar.cs index 74e465d..6f1746d 100644 --- a/MLEM/Pathfinding/AStar.cs +++ b/MLEM/Pathfinding/AStar.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; namespace MLEM.Pathfinding { diff --git a/MLEM/Textures/TextureRegion.cs b/MLEM/Textures/TextureRegion.cs index abe25f8..2f69183 100644 --- a/MLEM/Textures/TextureRegion.cs +++ b/MLEM/Textures/TextureRegion.cs @@ -72,7 +72,8 @@ namespace MLEM.Textures { /// Creates a new texture region that spans the entire texture /// /// The texture to use - public TextureRegion(Texture2D texture) : this(texture, new Rectangle(0, 0, texture.Width, texture.Height)) { + public TextureRegion(Texture2D texture) : + this(texture, new Rectangle(0, 0, texture.Width, texture.Height)) { } /// @@ -83,7 +84,8 @@ namespace MLEM.Textures { /// The y coordinate of the top left corner of this area /// The width of this area /// The height of this area - public TextureRegion(Texture2D texture, int u, int v, int width, int height) : this(texture, new Rectangle(u, v, width, height)) { + public TextureRegion(Texture2D texture, int u, int v, int width, int height) : + this(texture, new Rectangle(u, v, width, height)) { } /// @@ -92,7 +94,39 @@ namespace MLEM.Textures { /// The texture to use /// The top left corner of this area /// The size of this area - public TextureRegion(Texture2D texture, Point uv, Point size) : this(texture, new Rectangle(uv, size)) { + public TextureRegion(Texture2D texture, Point uv, Point size) : + this(texture, new Rectangle(uv, size)) { + } + + /// + /// Creates a new texture region which is a sub-region of the given texture region + /// + /// The texture region to create a sub-region of + /// The new texture region area + public TextureRegion(TextureRegion region, Rectangle area) : + this(region, area.Location, area.Size) { + } + + /// + /// Creates a new texture region which is a sub-region of the given texture region + /// + /// The texture region to create a sub-region of + /// The x coordinate of the top left corner of this area + /// The y coordinate of the top left corner of this area + /// The width of this area + /// The height of this area + public TextureRegion(TextureRegion region, int u, int v, int width, int height) : + this(region, new Point(u, v), new Point(width, height)) { + } + + /// + /// Creates a new texture region which is a sub-region of the given texture region + /// + /// The texture region to create a sub-region of + /// The top left corner of this area + /// The size of this area + public TextureRegion(TextureRegion region, Point uv, Point size) : + this(region.Texture, region.Position + uv, size) { } } diff --git a/MLEM/Textures/UniformTextureAtlas.cs b/MLEM/Textures/UniformTextureAtlas.cs index 490365a..3526700 100644 --- a/MLEM/Textures/UniformTextureAtlas.cs +++ b/MLEM/Textures/UniformTextureAtlas.cs @@ -12,9 +12,10 @@ namespace MLEM.Textures { public class UniformTextureAtlas : GenericDataHolder { /// - /// The texture to use for this atlas + /// The that this uniform texture atlas uses as its basis. + /// In most cases, has the full area of the underlying . /// - public readonly Texture2D Texture; + public readonly TextureRegion Region; /// /// The amount of sub-regions this atlas has in the x direction /// @@ -32,6 +33,11 @@ namespace MLEM.Textures { /// public readonly int RegionHeight; /// + /// The texture to use for this atlas. + /// Note that stores the actual area that we depend on. + /// + public Texture2D Texture => this.Region.Texture; + /// /// Returns the at this texture atlas's given index. /// The index is zero-based, where rows come first and columns come second. /// @@ -55,24 +61,35 @@ namespace MLEM.Textures { private readonly Dictionary regions = new Dictionary(); + /// + /// Creates a new uniform texture atlas with the given texture region and region amount. + /// This atlas will only ever pull information from the given and never exit the region's bounds. + /// + /// The texture region to use for this atlas + /// The amount of texture regions in the x direction + /// The amount of texture regions in the y direction + public UniformTextureAtlas(TextureRegion region, int regionAmountX, int regionAmountY) { + this.Region = region; + this.RegionAmountX = regionAmountX; + this.RegionAmountY = regionAmountY; + this.RegionWidth = region.Width / regionAmountX; + this.RegionHeight = region.Height / regionAmountY; + } + /// /// Creates a new uniform texture atlas with the given texture and region amount. /// /// The texture to use for this atlas /// The amount of texture regions in the x direction /// The amount of texture regions in the y direction - public UniformTextureAtlas(Texture2D texture, int regionAmountX, int regionAmountY) { - this.Texture = texture; - this.RegionAmountX = regionAmountX; - this.RegionAmountY = regionAmountY; - this.RegionWidth = texture.Width / regionAmountX; - this.RegionHeight = texture.Height / regionAmountY; + public UniformTextureAtlas(Texture2D texture, int regionAmountX, int regionAmountY) : + this(new TextureRegion(texture), regionAmountX, regionAmountY) { } private TextureRegion GetOrAddRegion(Rectangle rect) { if (this.regions.TryGetValue(rect, out var region)) return region; - region = new TextureRegion(this.Texture, + region = new TextureRegion(this.Region, rect.X * this.RegionWidth, rect.Y * this.RegionHeight, rect.Width * this.RegionWidth, rect.Height * this.RegionHeight); this.regions.Add(rect, region); diff --git a/Sandbox/GameImpl.cs b/Sandbox/GameImpl.cs index 57300dd..1391cfe 100644 --- a/Sandbox/GameImpl.cs +++ b/Sandbox/GameImpl.cs @@ -7,11 +7,9 @@ using Microsoft.Xna.Framework.Input; using MLEM.Cameras; using MLEM.Data; using MLEM.Data.Content; -using MLEM.Extended.Extensions; using MLEM.Extended.Font; using MLEM.Extended.Tiled; using MLEM.Extensions; -using MLEM.Font; using MLEM.Formatting; using MLEM.Formatting.Codes; using MLEM.Input; @@ -21,12 +19,8 @@ using MLEM.Textures; using MLEM.Ui; using MLEM.Ui.Elements; using MLEM.Ui.Style; -using MonoGame.Extended; using MonoGame.Extended.BitmapFonts; -using MonoGame.Extended.Sprites; -using MonoGame.Extended.TextureAtlases; using MonoGame.Extended.Tiled; -using RectangleF = MonoGame.Extended.RectangleF; namespace Sandbox { public class GameImpl : MlemGame { diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index a336e29..0000000 --- a/build.ps1 +++ /dev/null @@ -1,256 +0,0 @@ -########################################################################## -# This is the Cake bootstrapper script for PowerShell. -# This file was downloaded from https://github.com/cake-build/resources -# Feel free to change this file to fit your needs. -########################################################################## - -<# - -.SYNOPSIS -This is a Powershell script to bootstrap a Cake build. - -.DESCRIPTION -This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) -and execute your Cake build script with the parameters you provide. - -.PARAMETER Script -The build script to execute. -.PARAMETER Target -The build script target to run. -.PARAMETER Configuration -The build configuration to use. -.PARAMETER Verbosity -Specifies the amount of information to be displayed. -.PARAMETER ShowDescription -Shows description about tasks. -.PARAMETER DryRun -Performs a dry run. -.PARAMETER SkipToolPackageRestore -Skips restoring of packages. -.PARAMETER ScriptArgs -Remaining arguments are added here. - -.LINK -https://cakebuild.net - -#> - -[CmdletBinding()] -Param( - [string]$Script = "build.cake", - [string]$Target, - [string]$Configuration, - [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] - [string]$Verbosity, - [switch]$ShowDescription, - [Alias("WhatIf", "Noop")] - [switch]$DryRun, - [switch]$SkipToolPackageRestore, - [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] - [string[]]$ScriptArgs -) - -# Attempt to set highest encryption available for SecurityProtocol. -# PowerShell will not set this by default (until maybe .NET 4.6.x). This -# will typically produce a message for PowerShell v2 (just an info -# message though) -try { - # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) - # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't - # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is - # installed (.NET 4.5 is an in-place upgrade). - # PowerShell Core already has support for TLS 1.2 so we can skip this if running in that. - if (-not $IsCoreCLR) { - [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 - } - } catch { - Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' - } - -[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null -function MD5HashFile([string] $filePath) -{ - if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) - { - return $null - } - - [System.IO.Stream] $file = $null; - [System.Security.Cryptography.MD5] $md5 = $null; - try - { - $md5 = [System.Security.Cryptography.MD5]::Create() - $file = [System.IO.File]::OpenRead($filePath) - return [System.BitConverter]::ToString($md5.ComputeHash($file)) - } - finally - { - if ($file -ne $null) - { - $file.Dispose() - } - } -} - -function GetProxyEnabledWebClient -{ - $wc = New-Object System.Net.WebClient - $proxy = [System.Net.WebRequest]::GetSystemWebProxy() - $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials - $wc.Proxy = $proxy - return $wc -} - -Write-Host "Preparing to run build script..." - -if(!$PSScriptRoot){ - $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent -} - -$TOOLS_DIR = Join-Path $PSScriptRoot "tools" -$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" -$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" -$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" -$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" -$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" -$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" -$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" -$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" - -# Make sure tools folder exists -if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { - Write-Verbose -Message "Creating tools directory..." - New-Item -Path $TOOLS_DIR -Type Directory | Out-Null -} - -# Make sure that packages.config exist. -if (!(Test-Path $PACKAGES_CONFIG)) { - Write-Verbose -Message "Downloading packages.config..." - try { - $wc = GetProxyEnabledWebClient - $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) - } catch { - Throw "Could not download packages.config." - } -} - -# Try find NuGet.exe in path if not exists -if (!(Test-Path $NUGET_EXE)) { - Write-Verbose -Message "Trying to find nuget.exe in PATH..." - $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } - $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 - if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { - Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." - $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName - } -} - -# Try download NuGet.exe if not exists -if (!(Test-Path $NUGET_EXE)) { - Write-Verbose -Message "Downloading NuGet.exe..." - try { - $wc = GetProxyEnabledWebClient - $wc.DownloadFile($NUGET_URL, $NUGET_EXE) - } catch { - Throw "Could not download NuGet.exe." - } -} - -# Save nuget.exe path to environment to be available to child processed -$env:NUGET_EXE = $NUGET_EXE -$env:NUGET_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { - "mono `"$NUGET_EXE`"" -} else { - "`"$NUGET_EXE`"" -} - -# Restore tools from NuGet? -if(-Not $SkipToolPackageRestore.IsPresent) { - Push-Location - Set-Location $TOOLS_DIR - - # Check for changes in packages.config and remove installed tools if true. - [string] $md5Hash = MD5HashFile $PACKAGES_CONFIG - if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or - ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { - Write-Verbose -Message "Missing or changed package.config hash..." - Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | - Remove-Item -Recurse -Force - } - - Write-Verbose -Message "Restoring tools from NuGet..." - - $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet tools." - } - else - { - $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" - } - Write-Verbose -Message ($NuGetOutput | Out-String) - - Pop-Location -} - -# Restore addins from NuGet -if (Test-Path $ADDINS_PACKAGES_CONFIG) { - Push-Location - Set-Location $ADDINS_DIR - - Write-Verbose -Message "Restoring addins from NuGet..." - $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet addins." - } - - Write-Verbose -Message ($NuGetOutput | Out-String) - - Pop-Location -} - -# Restore modules from NuGet -if (Test-Path $MODULES_PACKAGES_CONFIG) { - Push-Location - Set-Location $MODULES_DIR - - Write-Verbose -Message "Restoring modules from NuGet..." - $NuGetOutput = Invoke-Expression "& $env:NUGET_EXE_INVOCATION install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" - - if ($LASTEXITCODE -ne 0) { - Throw "An error occurred while restoring NuGet modules." - } - - Write-Verbose -Message ($NuGetOutput | Out-String) - - Pop-Location -} - -# Make sure that Cake has been installed. -if (!(Test-Path $CAKE_EXE)) { - Throw "Could not find Cake.exe at $CAKE_EXE" -} - -$CAKE_EXE_INVOCATION = if ($IsLinux -or $IsMacOS) { - "mono `"$CAKE_EXE`"" -} else { - "`"$CAKE_EXE`"" -} - - # Build an array (not a string) of Cake arguments to be joined later -$cakeArguments = @() -if ($Script) { $cakeArguments += "`"$Script`"" } -if ($Target) { $cakeArguments += "-target=`"$Target`"" } -if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } -if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } -if ($ShowDescription) { $cakeArguments += "-showdescription" } -if ($DryRun) { $cakeArguments += "-dryrun" } -$cakeArguments += $ScriptArgs - -# Start Cake -Write-Host "Running build script..." -Invoke-Expression "& $CAKE_EXE_INVOCATION $($cakeArguments -join " ")" -exit $LASTEXITCODE diff --git a/build.sh b/build.sh deleted file mode 100755 index b9e1252..0000000 --- a/build.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env bash - -########################################################################## -# This is the Cake bootstrapper script for Linux and OS X. -# This file was downloaded from https://github.com/cake-build/resources -# Feel free to change this file to fit your needs. -########################################################################## - -# Define directories. -SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) -TOOLS_DIR=$SCRIPT_DIR/tools -ADDINS_DIR=$TOOLS_DIR/Addins -MODULES_DIR=$TOOLS_DIR/Modules -NUGET_EXE=$TOOLS_DIR/nuget.exe -CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe -PACKAGES_CONFIG=$TOOLS_DIR/packages.config -PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum -ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config -MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config - -# Define md5sum or md5 depending on Linux/OSX -MD5_EXE= -if [[ "$(uname -s)" == "Darwin" ]]; then - MD5_EXE="md5 -r" -else - MD5_EXE="md5sum" -fi - -# Define default arguments. -SCRIPT="build.cake" -CAKE_ARGUMENTS=() - -# Parse arguments. -for i in "$@"; do - case $1 in - -s|--script) SCRIPT="$2"; shift ;; - --) shift; CAKE_ARGUMENTS+=("$@"); break ;; - *) CAKE_ARGUMENTS+=("$1") ;; - esac - shift -done - -# Make sure the tools folder exist. -if [ ! -d "$TOOLS_DIR" ]; then - mkdir "$TOOLS_DIR" -fi - -# Make sure that packages.config exist. -if [ ! -f "$TOOLS_DIR/packages.config" ]; then - echo "Downloading packages.config..." - curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages - if [ $? -ne 0 ]; then - echo "An error occurred while downloading packages.config." - exit 1 - fi -fi - -# Download NuGet if it does not exist. -if [ ! -f "$NUGET_EXE" ]; then - echo "Downloading NuGet..." - curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - if [ $? -ne 0 ]; then - echo "An error occurred while downloading nuget.exe." - exit 1 - fi -fi - -# Restore tools from NuGet. -pushd "$TOOLS_DIR" >/dev/null -if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then - find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf -fi - -mono "$NUGET_EXE" install -ExcludeVersion -if [ $? -ne 0 ]; then - echo "Could not restore NuGet tools." - exit 1 -fi - -$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" - -popd >/dev/null - -# Restore addins from NuGet. -if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then - pushd "$ADDINS_DIR" >/dev/null - - mono "$NUGET_EXE" install -ExcludeVersion - if [ $? -ne 0 ]; then - echo "Could not restore NuGet addins." - exit 1 - fi - - popd >/dev/null -fi - -# Restore modules from NuGet. -if [ -f "$MODULES_PACKAGES_CONFIG" ]; then - pushd "$MODULES_DIR" >/dev/null - - mono "$NUGET_EXE" install -ExcludeVersion - if [ $? -ne 0 ]; then - echo "Could not restore NuGet modules." - exit 1 - fi - - popd >/dev/null -fi - -# Make sure that Cake has been installed. -if [ ! -f "$CAKE_EXE" ]; then - echo "Could not find Cake.exe at '$CAKE_EXE'." - exit 1 -fi - -# Start Cake -exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}"