1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-06-12 01:03:50 +02:00

Merge branch 'main' of https://github.com/Ellpeck/MLEM into main

This commit is contained in:
Ell 2021-01-09 00:58:00 +01:00
commit 81f8485022
24 changed files with 313 additions and 448 deletions

12
.config/dotnet-tools.json Normal file
View file

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"cake.tool": {
"version": "0.38.5",
"commands": [
"dotnet-cake"
]
}
}
}

View file

@ -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;

View file

@ -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;

6
Jenkinsfile vendored
View file

@ -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'
}
}

View file

@ -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 {
/// <summary>
/// The texture to use for this atlas
/// </summary>
public readonly Texture2D Texture;
public readonly TextureRegion Texture;
/// <summary>
/// Returns the texture region with the given name, or null if it does not exist.
/// </summary>
@ -37,21 +38,23 @@ namespace MLEM.Data {
private readonly Dictionary<string, TextureRegion> regions = new Dictionary<string, TextureRegion>();
/// <summary>
/// Creates a new data texture atlas with the given texture and region amount.
/// </summary>
/// <param name="texture">The texture to use for this atlas</param>
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"));
/// <summary>
/// Loads a <see cref="DataTextureAtlas"/> from the given loaded texture and texture data file.
/// </summary>
/// <param name="texture">The texture to use for this data texture atlas</param>
/// <param name="content">The content manager to use for loading</param>
/// <param name="infoPath">The path, including extension, to the atlas info file</param>
/// <param name="pivotRelative">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.</param>
/// <returns>A new data texture atlas with the given settings</returns>
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<Texture2D>(texturePath);
var atlas = new DataTextureAtlas(texture);
// parse each texture region: "<name> loc <u> <v> <w> <h> piv <px> <py>"
@ -92,7 +95,8 @@ namespace MLEM.Data {
/// <param name="pivotRelative">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.</param>
/// <returns>A new data texture atlas with the given settings</returns>
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<Texture2D>(texturePath));
return DataTextureAtlas.LoadAtlasData(texture, content, infoPath ?? $"{texturePath}.atlas", pivotRelative);
}
}

View file

@ -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 {
/// <summary>
/// A runtime texture packer provides the user with the ability to combine multiple <see cref="Texture2D"/> 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 <see cref="TextureRegion"/> instances.
/// </summary>
public class RuntimeTexturePacker {
private readonly List<Request> textures = new List<Request>();
/// <summary>
/// The generated packed texture.
/// This value is null before <see cref="Pack"/> is called.
/// </summary>
public Texture2D PackedTexture { get; private set; }
/// <summary>
/// The time that it took to calculate the required areas the last time that <see cref="Pack"/> was called
/// </summary>
public TimeSpan LastCalculationTime { get; private set; }
/// <summary>
/// The time that it took to copy the texture data from the invidiual textures onto the <see cref="PackedTexture"/> the last time that <see cref="Pack"/> was called
/// </summary>
public TimeSpan LastPackTime { get; private set; }
/// <summary>
/// The time that <see cref="Pack"/> took the last time it was called
/// </summary>
public TimeSpan LastTotalTime => this.LastCalculationTime + this.LastPackTime;
private readonly int maxWidth;
/// <summary>
/// Creates a new runtime texture packer with the given settings
/// </summary>
/// <param name="maxWidth">The maximum width that the packed texture can have. Defaults to 2048.</param>
public RuntimeTexturePacker(int maxWidth = 2048) {
this.maxWidth = maxWidth;
}
/// <summary>
/// Adds a new texture to this texture packer to be packed.
/// The passed <see cref="Action{T}"/> is invoked in <see cref="Pack"/> and provides the caller with the resulting texture region on the <see cref="PackedTexture"/>.
/// </summary>
/// <param name="texture">The texture to pack</param>
/// <param name="result">The result callback which will receive the resulting texture region</param>
public void Add(Texture2D texture, Action<TextureRegion> result) {
this.Add(new TextureRegion(texture), result);
}
/// <summary>
/// Adds a new <see cref="TextureRegion"/> to this texture packer to be packed.
/// The passed <see cref="Action{T}"/> is invoked in <see cref="Pack"/> and provides the caller with the resulting texture region on the <see cref="PackedTexture"/>.
/// </summary>
/// <param name="texture">The texture to pack</param>
/// <param name="result">The result callback which will receive the resulting texture region</param>
/// <exception cref="InvalidOperationException">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</exception>
public void Add(TextureRegion texture, Action<TextureRegion> 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));
}
/// <summary>
/// Packs all of the textures and texture regions added using <see cref="Add(Microsoft.Xna.Framework.Graphics.Texture2D,System.Action{MLEM.Textures.TextureRegion})"/> into one texture.
/// The resulting texture will be stored in <see cref="PackedTexture"/>.
/// All of the result callbacks that were added will also be invoked.
/// </summary>
/// <param name="device">The graphics device to use for texture generation</param>
/// <exception cref="InvalidOperationException">Thrown when calling this method on a texture packer that has already been packed</exception>
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<TextureRegion> Result;
public Rectangle PackedArea;
public Request(TextureRegion texture, Action<TextureRegion> result) {
this.Texture = texture;
this.Result = result;
}
}
}
}

View file

@ -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;

View file

@ -1,6 +1,5 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using MLEM.Font;
using MLEM.Formatting;
using MLEM.Misc;

View file

@ -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;

View file

@ -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;

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
namespace MLEM.Extensions {

View file

@ -1,4 +1,3 @@
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Misc;

View file

@ -21,6 +21,11 @@ namespace MLEM.Font {
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations, generic fonts implicitly support it in <see cref="MeasureString"/>.
/// </summary>
public const char Nbsp = '\u00A0';
/// <summary>
/// This field holds the unicode representation of a zero-width space.
/// Whereas a regular <see cref="SpriteFont"/> would have to explicitly support this character for width calculations and string splitting, generic fonts implicitly support it in <see cref="MeasureString"/> and <see cref="SplitString"/>.
/// </summary>
public const char Zwsp = '\u8203';
/// <summary>
/// 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 {
/// <summary>
/// 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.
/// </summary>
/// <param name="text">The text to split into multiple lines</param>
/// <param name="width">The maximum width that each line should have</param>
@ -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');

View file

@ -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 {
/// <inheritdoc/>

View file

@ -12,10 +12,12 @@ namespace MLEM.Formatting.Codes {
public class ImageCode : Code {
private readonly SpriteAnimation image;
private readonly bool copyTextColor;
/// <inheritdoc />
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;
}
/// <inheritdoc />
@ -35,7 +37,8 @@ namespace MLEM.Formatting.Codes {
/// <inheritdoc />
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 {
/// <param name="formatter">The formatter to add the code to</param>
/// <param name="name">The name of the formatting code. The regex for this code will be between angle brackets.</param>
/// <param name="image">The image to render at the code's position</param>
public static void AddImage(this TextFormatter formatter, string name, TextureRegion image) {
formatter.AddImage(name, new SpriteAnimation(1, image));
/// <param name="copyTextColor">Whether or not the image code should use the text's color instead of White</param>
public static void AddImage(this TextFormatter formatter, string name, TextureRegion image, bool copyTextColor = false) {
formatter.AddImage(name, new SpriteAnimation(1, image), copyTextColor);
}
/// <inheritdoc cref="AddImage(MLEM.Formatting.TextFormatter,string,MLEM.Textures.TextureRegion)"/>
public static void AddImage(this TextFormatter formatter, string name, SpriteAnimation image) {
formatter.Codes.Add(new Regex($"<i {name}>"), (f, m, r) => new ImageCode(m, r, image));
/// <inheritdoc cref="AddImage(MLEM.Formatting.TextFormatter,string,MLEM.Textures.TextureRegion,bool)"/>
public static void AddImage(this TextFormatter formatter, string name, SpriteAnimation image, bool copyTextColor = false) {
formatter.Codes.Add(new Regex($"<i {name}>"), (f, m, r) => new ImageCode(m, r, image, copyTextColor));
}
}

View file

@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

View file

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using MLEM.Extensions;
namespace MLEM.Misc {
/// <summary>

View file

@ -3,21 +3,14 @@ using System.Collections.Generic;
using System.Runtime.Serialization;
namespace MLEM.Misc {
/// <summary>
/// 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.
/// </summary>
/// <inheritdoc />
[DataContract]
public class GenericDataHolder {
public class GenericDataHolder : IGenericDataHolder {
[DataMember]
[DataMember(EmitDefaultValue = false)]
private Dictionary<string, object> data;
/// <summary>
/// Store a piece of generic data on this object.
/// </summary>
/// <param name="key">The key to store the data by</param>
/// <param name="data">The data to store in the object</param>
/// <inheritdoc />
public void SetData(string key, object data) {
if (data == default) {
if (this.data != null)
@ -29,22 +22,14 @@ namespace MLEM.Misc {
}
}
/// <summary>
/// Returns a piece of generic data of the given type on this object.
/// </summary>
/// <param name="key">The key that the data is stored by</param>
/// <typeparam name="T">The type of the data stored</typeparam>
/// <returns>The data, or default if it doesn't exist</returns>
/// <inheritdoc />
public T GetData<T>(string key) {
if (this.data != null && this.data.TryGetValue(key, out var val) && val is T t)
return t;
return default;
}
/// <summary>
/// Returns all of the generic data that this object stores.
/// </summary>
/// <returns>The generic data on this object</returns>
/// <inheritdoc />
public IReadOnlyCollection<string> GetDataKeys() {
if (this.data == null)
return Array.Empty<string>();
@ -52,4 +37,33 @@ namespace MLEM.Misc {
}
}
/// <summary>
/// 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.
/// </summary>
public interface IGenericDataHolder {
/// <summary>
/// Store a piece of generic data on this object.
/// </summary>
/// <param name="key">The key to store the data by</param>
/// <param name="data">The data to store in the object</param>
void SetData(string key, object data);
/// <summary>
/// Returns a piece of generic data of the given type on this object.
/// </summary>
/// <param name="key">The key that the data is stored by</param>
/// <typeparam name="T">The type of the data stored</typeparam>
/// <returns>The data, or default if it doesn't exist</returns>
T GetData<T>(string key);
/// <summary>
/// Returns all of the generic data that this object stores.
/// </summary>
/// <returns>The generic data on this object</returns>
IReadOnlyCollection<string> GetDataKeys();
}
}

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace MLEM.Pathfinding {

View file

@ -72,7 +72,8 @@ namespace MLEM.Textures {
/// Creates a new texture region that spans the entire texture
/// </summary>
/// <param name="texture">The texture to use</param>
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)) {
}
/// <summary>
@ -83,7 +84,8 @@ namespace MLEM.Textures {
/// <param name="v">The y coordinate of the top left corner of this area</param>
/// <param name="width">The width of this area</param>
/// <param name="height">The height of this area</param>
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)) {
}
/// <summary>
@ -92,7 +94,39 @@ namespace MLEM.Textures {
/// <param name="texture">The texture to use</param>
/// <param name="uv">The top left corner of this area</param>
/// <param name="size">The size of this area</param>
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)) {
}
/// <summary>
/// Creates a new texture region which is a sub-region of the given texture region
/// </summary>
/// <param name="region">The texture region to create a sub-region of</param>
/// <param name="area">The new texture region area</param>
public TextureRegion(TextureRegion region, Rectangle area) :
this(region, area.Location, area.Size) {
}
/// <summary>
/// Creates a new texture region which is a sub-region of the given texture region
/// </summary>
/// <param name="region">The texture region to create a sub-region of</param>
/// <param name="u">The x coordinate of the top left corner of this area</param>
/// <param name="v">The y coordinate of the top left corner of this area</param>
/// <param name="width">The width of this area</param>
/// <param name="height">The height of this area</param>
public TextureRegion(TextureRegion region, int u, int v, int width, int height) :
this(region, new Point(u, v), new Point(width, height)) {
}
/// <summary>
/// Creates a new texture region which is a sub-region of the given texture region
/// </summary>
/// <param name="region">The texture region to create a sub-region of</param>
/// <param name="uv">The top left corner of this area</param>
/// <param name="size">The size of this area</param>
public TextureRegion(TextureRegion region, Point uv, Point size) :
this(region.Texture, region.Position + uv, size) {
}
}

View file

@ -12,9 +12,10 @@ namespace MLEM.Textures {
public class UniformTextureAtlas : GenericDataHolder {
/// <summary>
/// The texture to use for this atlas
/// The <see cref="TextureRegion"/> that this uniform texture atlas uses as its basis.
/// In most cases, <see cref="Region"/> has the full area of the underlying <see cref="Texture"/>.
/// </summary>
public readonly Texture2D Texture;
public readonly TextureRegion Region;
/// <summary>
/// The amount of sub-regions this atlas has in the x direction
/// </summary>
@ -32,6 +33,11 @@ namespace MLEM.Textures {
/// </summary>
public readonly int RegionHeight;
/// <summary>
/// The texture to use for this atlas.
/// Note that <see cref="Region"/> stores the actual area that we depend on.
/// </summary>
public Texture2D Texture => this.Region.Texture;
/// <summary>
/// Returns the <see cref="TextureRegion"/> at this texture atlas's given index.
/// The index is zero-based, where rows come first and columns come second.
/// </summary>
@ -55,24 +61,35 @@ namespace MLEM.Textures {
private readonly Dictionary<Rectangle, TextureRegion> regions = new Dictionary<Rectangle, TextureRegion>();
/// <summary>
/// Creates a new uniform texture atlas with the given texture region and region amount.
/// This atlas will only ever pull information from the given <see cref="TextureRegion"/> and never exit the region's bounds.
/// </summary>
/// <param name="region">The texture region to use for this atlas</param>
/// <param name="regionAmountX">The amount of texture regions in the x direction</param>
/// <param name="regionAmountY">The amount of texture regions in the y direction</param>
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;
}
/// <summary>
/// Creates a new uniform texture atlas with the given texture and region amount.
/// </summary>
/// <param name="texture">The texture to use for this atlas</param>
/// <param name="regionAmountX">The amount of texture regions in the x direction</param>
/// <param name="regionAmountY">The amount of texture regions in the y direction</param>
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);

View file

@ -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 {

256
build.ps1
View file

@ -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

117
build.sh
View file

@ -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[@]}"