diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6f98b12 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "FNA"] + path = FNA + url = https://github.com/FNA-XNA/FNA +[submodule "FontStashSharp"] + path = FontStashSharp + url = https://github.com/FontStashSharp/FontStashSharp diff --git a/CHANGELOG.md b/CHANGELOG.md index 35d1a04..403f4c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Additions - Added an Enum constructor to GenericInput - Added RandomPitchModifier and GetRandomPitch to SoundEffectInfo - Added TextInput class, which is an isolated version of MLEM.Ui's TextField logic +- Added MLEM.FNA, which is fully compatible with FNA Improvements - Allow comparing Keybind and Combination based on the amount of modifiers they have @@ -31,6 +32,7 @@ Removals Additions - Added Element.AutoNavGroup which allows forming groups for auto-navigation - Added UiMarkdownParser +- Added MLEM.Ui.FNA, which is fully compatible with FNA Improvements - Ensure that Element.IsMouseOver is always accurate by making it an auto-property @@ -59,6 +61,7 @@ Removals ### MLEM.Extended Additions - Added LayerPositionF +- Added MLEM.Extended.FNA, which is fully compatible with FNA Improvements - Allow using a StaticSpriteBatch to render an IndividualTiledMapRenderer @@ -67,6 +70,7 @@ Improvements Additions - Added the ability to add padding to RuntimeTexturePacker texture regions - Added the ability to pack UniformTextureAtlas and DataTextureAtlas using RuntimeTexturePacker +- Added MLEM.Data.FNA, which is fully compatible with FNA Improvements - Premultiply textures when using RawContentManager @@ -77,6 +81,10 @@ Improvements Fixes - Fixed SoundEffectReader incorrectly claiming it could read ogg and mp3 files +### MLEM.Startup +Additions +- Added MLEM.Startup.FNA, which is fully compatible with FNA + ## 5.3.0 ### MLEM Additions diff --git a/Demos.Android/Demos.Android.csproj b/Demos.Android/Demos.Android.csproj index aef6f1e..0d6ecd8 100644 --- a/Demos.Android/Demos.Android.csproj +++ b/Demos.Android/Demos.Android.csproj @@ -80,7 +80,7 @@ - + diff --git a/Demos.DesktopGL/Demos.DesktopGL.FNA.csproj b/Demos.DesktopGL/Demos.DesktopGL.FNA.csproj new file mode 100644 index 0000000..988c685 --- /dev/null +++ b/Demos.DesktopGL/Demos.DesktopGL.FNA.csproj @@ -0,0 +1,37 @@ + + + + Exe + net5.0 + Icon.ico + MLEM Desktop Demos + Demos.DesktopGL + $(DefineConstants);FNA + + DesktopGL + + + + + + + + + + + + + + + + + + + + + PreserveNewest + %(Filename)%(Extension) + + + + diff --git a/Demos.DesktopGL/FNA/FAudio.dll b/Demos.DesktopGL/FNA/FAudio.dll new file mode 100644 index 0000000..57c6067 Binary files /dev/null and b/Demos.DesktopGL/FNA/FAudio.dll differ diff --git a/Demos.DesktopGL/FNA/FNA3D.dll b/Demos.DesktopGL/FNA/FNA3D.dll new file mode 100644 index 0000000..fe8024f Binary files /dev/null and b/Demos.DesktopGL/FNA/FNA3D.dll differ diff --git a/Demos.DesktopGL/FNA/SDL2.dll b/Demos.DesktopGL/FNA/SDL2.dll new file mode 100644 index 0000000..7a6c63d Binary files /dev/null and b/Demos.DesktopGL/FNA/SDL2.dll differ diff --git a/Demos.DesktopGL/FNA/libFAudio.0.dylib b/Demos.DesktopGL/FNA/libFAudio.0.dylib new file mode 100644 index 0000000..035020c Binary files /dev/null and b/Demos.DesktopGL/FNA/libFAudio.0.dylib differ diff --git a/Demos.DesktopGL/FNA/libFAudio.so.0 b/Demos.DesktopGL/FNA/libFAudio.so.0 new file mode 100644 index 0000000..5b3b116 Binary files /dev/null and b/Demos.DesktopGL/FNA/libFAudio.so.0 differ diff --git a/Demos.DesktopGL/FNA/libFNA3D.0.dylib b/Demos.DesktopGL/FNA/libFNA3D.0.dylib new file mode 100644 index 0000000..17b80fb Binary files /dev/null and b/Demos.DesktopGL/FNA/libFNA3D.0.dylib differ diff --git a/Demos.DesktopGL/FNA/libFNA3D.so.0 b/Demos.DesktopGL/FNA/libFNA3D.so.0 new file mode 100644 index 0000000..b1c1986 Binary files /dev/null and b/Demos.DesktopGL/FNA/libFNA3D.so.0 differ diff --git a/Demos.DesktopGL/FNA/libMoltenVK.dylib b/Demos.DesktopGL/FNA/libMoltenVK.dylib new file mode 100644 index 0000000..9cf069d Binary files /dev/null and b/Demos.DesktopGL/FNA/libMoltenVK.dylib differ diff --git a/Demos.DesktopGL/FNA/libSDL2-2.0.0.dylib b/Demos.DesktopGL/FNA/libSDL2-2.0.0.dylib new file mode 100644 index 0000000..71d8bec Binary files /dev/null and b/Demos.DesktopGL/FNA/libSDL2-2.0.0.dylib differ diff --git a/Demos.DesktopGL/FNA/libSDL2-2.0.so.0 b/Demos.DesktopGL/FNA/libSDL2-2.0.so.0 new file mode 100644 index 0000000..55d47d4 Binary files /dev/null and b/Demos.DesktopGL/FNA/libSDL2-2.0.so.0 differ diff --git a/Demos.DesktopGL/FNA/libtheorafile.dll b/Demos.DesktopGL/FNA/libtheorafile.dll new file mode 100644 index 0000000..a9b2929 Binary files /dev/null and b/Demos.DesktopGL/FNA/libtheorafile.dll differ diff --git a/Demos.DesktopGL/FNA/libtheorafile.dylib b/Demos.DesktopGL/FNA/libtheorafile.dylib new file mode 100644 index 0000000..49116fb Binary files /dev/null and b/Demos.DesktopGL/FNA/libtheorafile.dylib differ diff --git a/Demos.DesktopGL/FNA/libtheorafile.so b/Demos.DesktopGL/FNA/libtheorafile.so new file mode 100644 index 0000000..687e841 Binary files /dev/null and b/Demos.DesktopGL/FNA/libtheorafile.so differ diff --git a/Demos.DesktopGL/FNA/libvulkan.1.dylib b/Demos.DesktopGL/FNA/libvulkan.1.dylib new file mode 100644 index 0000000..ff138d4 Binary files /dev/null and b/Demos.DesktopGL/FNA/libvulkan.1.dylib differ diff --git a/Demos.DesktopGL/Program.cs b/Demos.DesktopGL/Program.cs index 5915bfc..0ec70d1 100644 --- a/Demos.DesktopGL/Program.cs +++ b/Demos.DesktopGL/Program.cs @@ -1,11 +1,17 @@ -using Microsoft.Xna.Framework; +using System; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; using MLEM.Misc; namespace Demos.DesktopGL { public static class Program { public static void Main() { + #if FNA + MlemPlatform.Current = new MlemPlatform.DesktopFna(a => TextInputEXT.TextInput += a); + #else MlemPlatform.Current = new MlemPlatform.DesktopGl((w, c) => w.TextInput += c); + #endif using var game = new GameImpl(); game.Run(); } diff --git a/Demos/AnimationDemo.cs b/Demos/AnimationDemo.cs index 02b7f66..7b4731f 100644 --- a/Demos/AnimationDemo.cs +++ b/Demos/AnimationDemo.cs @@ -90,7 +90,7 @@ namespace Demos { public override void DoDraw(GameTime gameTime) { this.GraphicsDevice.Clear(Color.CornflowerBlue); - this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(10)); + this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(10)); // draw the group's current region // if not using a group, just draw the animation's CurrentRegion here this.SpriteBatch.Draw(this.group.CurrentRegion, new Vector2(10, 10), Color.White); diff --git a/Demos/AutoTilingDemo.cs b/Demos/AutoTilingDemo.cs index 0ce21d1..d062f36 100644 --- a/Demos/AutoTilingDemo.cs +++ b/Demos/AutoTilingDemo.cs @@ -37,7 +37,7 @@ namespace Demos { this.GraphicsDevice.Clear(Color.Black); // drawing the auto tiles - this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(10)); + this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(10)); for (var x = 0; x < 6; x++) { for (var y = 0; y < 5; y++) { // don't draw non-grass tiles ( ) diff --git a/Demos/Demos.FNA.csproj b/Demos/Demos.FNA.csproj new file mode 100644 index 0000000..6d088d2 --- /dev/null +++ b/Demos/Demos.FNA.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + Demos + $(DefineConstants);FNA + + + + + + + + + + + all + + + + diff --git a/Demos/EasingsDemo.cs b/Demos/EasingsDemo.cs index c28377b..14d5832 100644 --- a/Demos/EasingsDemo.cs +++ b/Demos/EasingsDemo.cs @@ -43,7 +43,7 @@ namespace Demos { this.GraphicsDevice.Clear(Color.CornflowerBlue); base.DoDraw(time); - this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp); + this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null); var view = this.GraphicsDevice.Viewport; // graph the easing function diff --git a/Demos/GameImpl.cs b/Demos/GameImpl.cs index d058cbc..55dcfd2 100644 --- a/Demos/GameImpl.cs +++ b/Demos/GameImpl.cs @@ -9,7 +9,9 @@ using MLEM.Textures; using MLEM.Ui; using MLEM.Ui.Elements; using MLEM.Ui.Style; +#if !FNA using MonoGame.Framework.Utilities; +#endif namespace Demos { public class GameImpl : MlemGame { @@ -47,11 +49,13 @@ namespace Demos { protected override void LoadContent() { // TODO remove with MonoGame 3.8.1 https://github.com/MonoGame/MonoGame/issues/7298 + #if !FNA if (PlatformInfo.MonoGamePlatform == MonoGamePlatform.DesktopGL) { this.GraphicsDeviceManager.PreferredBackBufferWidth = 1280; this.GraphicsDeviceManager.PreferredBackBufferHeight = 720; this.GraphicsDeviceManager.ApplyChanges(); } + #endif base.LoadContent(); this.UiSystem.AutoScaleReferenceSize = new Point(1280, 720); diff --git a/Demos/PathfindingDemo.cs b/Demos/PathfindingDemo.cs index 2325d76..716d33d 100644 --- a/Demos/PathfindingDemo.cs +++ b/Demos/PathfindingDemo.cs @@ -35,9 +35,9 @@ namespace Demos { // Create a cost function, which determines how expensive (or difficult) it should be to move from a given position // to the next, adjacent position. In our case, the only restriction should be walls and out-of-bounds positions, which - // both have a cost of AStar2.InfiniteCost, meaning they are completely unwalkable. + // both have a cost of AStar2.InfiniteCost, meaning they are completely unwalkable. // If your game contains harder-to-move-on areas like, say, a muddy pit, you can return a higher cost value for those - // locations. If you want to scale your cost function differently, you can specify a different default cost in your + // locations. If you want to scale your cost function differently, you can specify a different default cost in your // pathfinder's constructor float Cost(Point pos, Point nextPos) { if (nextPos.X < 0 || nextPos.Y < 0 || nextPos.X >= 50 || nextPos.Y >= 50) @@ -73,7 +73,7 @@ namespace Demos { public override void DoDraw(GameTime gameTime) { this.GraphicsDevice.Clear(Color.White); - this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, transformMatrix: Matrix.CreateScale(14)); + this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null, null, Matrix.CreateScale(14)); var tex = this.SpriteBatch.GetBlankTexture(); // draw the world with simple shapes for (var x = 0; x < 50; x++) { @@ -87,9 +87,9 @@ namespace Demos { // in a real game, you'd obviously make your characters walk along the path instead of drawing it if (this.path != null) { for (var i = 1; i < this.path.Count; i++) { - var (firstX, firstY) = this.path[i - 1]; - var (secondX, secondY) = this.path[i]; - this.SpriteBatch.Draw(tex, RectangleF.FromCorners(new Vector2(firstX + 0.25F, firstY + 0.25F), new Vector2(secondX + 0.75F, secondY + 0.75F)), Color.Blue); + var first = this.path[i - 1]; + var second = this.path[i]; + this.SpriteBatch.Draw(tex, RectangleF.FromCorners(new Vector2(first.X + 0.25F, first.Y + 0.25F), new Vector2(second.X + 0.75F, second.Y + 0.75F)), Color.Blue); } } this.SpriteBatch.End(); diff --git a/Demos/TextFormattingDemo.cs b/Demos/TextFormattingDemo.cs index c78d5c2..3cd0b8f 100644 --- a/Demos/TextFormattingDemo.cs +++ b/Demos/TextFormattingDemo.cs @@ -50,7 +50,7 @@ namespace Demos { public override void DoDraw(GameTime time) { this.GraphicsDevice.Clear(Color.DarkSlateGray); - this.SpriteBatch.Begin(samplerState: SamplerState.PointClamp); + this.SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.PointClamp, null, null); // we draw the tokenized text in the center of the screen // since the text is already center-aligned, we only need to align it on the y axis here diff --git a/Demos/UiDemo.cs b/Demos/UiDemo.cs index 89efc7f..1bb7b47 100644 --- a/Demos/UiDemo.cs +++ b/Demos/UiDemo.cs @@ -73,7 +73,7 @@ namespace Demos { // add the root to the demos' ui this.UiRoot.AddChild(this.root); - this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a small demo for MLEM.Ui, a user interface library that is part of the MLEM Library for Extending MonoGame.")); + this.root.AddChild(new Paragraph(Anchor.AutoLeft, 1, "This is a small demo for MLEM.Ui, a user interface library that is part of the MLEM Library for Extending MonoGame and FNA.")); var image = this.root.AddChild(new Image(Anchor.AutoCenter, new Vector2(50, 50), new TextureRegion(this.testTexture, 0, 0, 8, 8)) {IsHidden = true, Padding = new Padding(3)}); // Setting the x or y coordinate of the size to 1 or a lower number causes the width or height to be a percentage of the parent's width or height // (for example, setting the size's x to 0.75 would make the element's width be 0.75*parentWidth) diff --git a/Docs/index.md b/Docs/index.md index d30fe1c..21877fb 100644 --- a/Docs/index.md +++ b/Docs/index.md @@ -1,6 +1,6 @@ ![The MLEM logo](https://raw.githubusercontent.com/Ellpeck/MLEM/release/Media/Banner.png) -**MLEM Library for Extending MonoGame** is an addition to the game framework [MonoGame](https://www.monogame.net/) that provides extension methods, quality of life improvements and additional features like a ui system and easy input handling. +**MLEM Library for Extending MonoGame and FNA** is an addition to the game frameworks [MonoGame](https://www.monogame.net/) and [FNA](https://fna-xna.github.io/) that provides extension methods, quality of life improvements and additional features like a ui system and easy input handling. # What next? - Get it on [NuGet](https://www.nuget.org/packages?q=mlem) diff --git a/FNA b/FNA new file mode 160000 index 0000000..e27bfb5 --- /dev/null +++ b/FNA @@ -0,0 +1 @@ +Subproject commit e27bfb5a34112461a6bff960888c0cce2b0c5be2 diff --git a/FontStashSharp b/FontStashSharp new file mode 160000 index 0000000..460a60c --- /dev/null +++ b/FontStashSharp @@ -0,0 +1 @@ +Subproject commit 460a60ca817e10b6c9bae3e3c05affd1b0bb4ba7 diff --git a/MLEM.Data/Content/RawContentManager.cs b/MLEM.Data/Content/RawContentManager.cs index db08b7d..785811a 100644 --- a/MLEM.Data/Content/RawContentManager.cs +++ b/MLEM.Data/Content/RawContentManager.cs @@ -7,19 +7,22 @@ using Microsoft.Xna.Framework.Graphics; namespace MLEM.Data.Content { /// - /// Represents a version of that doesn't load content binary xnb files, but rather as their regular formats. + /// Represents a version of that doesn't load content binary xnb files, but rather as their regular formats. /// public class RawContentManager : ContentManager, IGameComponent { private static List readers; - private readonly List disposableAssets = new List(); - /// /// The graphics device that this content manager uses /// public readonly GraphicsDevice GraphicsDevice; + private readonly List disposableAssets = new List(); + #if FNA + private Dictionary LoadedAssets { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + #endif + /// /// Creates a new content manager with an optionally specified root directory. /// @@ -50,7 +53,11 @@ namespace MLEM.Data.Content { /// The original name of the asset. /// The current asset instance. /// The asset's type. - protected override void ReloadAsset(string originalAssetName, T currentAsset) { + protected + #if !FNA + override + #endif + void ReloadAsset(string originalAssetName, T currentAsset) { this.Read(originalAssetName, currentAsset); } diff --git a/MLEM.Data/Content/Texture2DReader.cs b/MLEM.Data/Content/Texture2DReader.cs index 27e4c9f..dcaf03c 100644 --- a/MLEM.Data/Content/Texture2DReader.cs +++ b/MLEM.Data/Content/Texture2DReader.cs @@ -9,10 +9,13 @@ namespace MLEM.Data.Content { /// protected override Texture2D Read(RawContentManager manager, string assetPath, Stream stream, Texture2D existing) { + #if !FNA if (existing != null) { existing.Reload(stream); return existing; - } else { + } else + #endif + { // premultiply the texture's color to be in line with the pipeline's texture reader // TODO this can be converted to use https://github.com/MonoGame/MonoGame/pull/7369 in the future using (var texture = Texture2D.FromStream(manager.GraphicsDevice, stream)) { diff --git a/MLEM.Data/DataTextureAtlas.cs b/MLEM.Data/DataTextureAtlas.cs index 46c92f7..8373328 100644 --- a/MLEM.Data/DataTextureAtlas.cs +++ b/MLEM.Data/DataTextureAtlas.cs @@ -5,12 +5,13 @@ 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 { /// /// - /// This class represents an atlas of objects which are loaded from a special texture atlas file. + /// This class represents an atlas of objects which are loaded from a special texture atlas file. /// To load a data texture atlas, you can use . /// /// @@ -95,7 +96,7 @@ namespace MLEM.Data { var loc = new Rectangle( int.Parse(match.Groups[2].Value), int.Parse(match.Groups[3].Value), int.Parse(match.Groups[4].Value), int.Parse(match.Groups[5].Value)); - loc.Offset(off); + loc.Offset(off.ToPoint()); // pivot var piv = !match.Groups[6].Success ? Vector2.Zero : off + new Vector2( diff --git a/MLEM.Data/MLEM.Data.FNA.csproj b/MLEM.Data/MLEM.Data.FNA.csproj new file mode 100644 index 0000000..d4dae9c --- /dev/null +++ b/MLEM.Data/MLEM.Data.FNA.csproj @@ -0,0 +1,45 @@ + + + netstandard2.0 + true + true + MLEM.Data + $(DefineConstants);FNA + NU1701 + + + + Ellpeck + Simple loading and processing of textures and other data for FNA, including the ability to load non-XNB content files easily + See the full changelog at https://mlem.ellpeck.de/CHANGELOG + fna ellpeck mlem utility extensions data serialize + https://mlem.ellpeck.de/ + https://github.com/Ellpeck/MLEM + MIT + Logo.png + README.md + + + + + + + + all + + + all + + + all + + + all + + + + + + + + diff --git a/MLEM.Data/MLEM.Data.csproj b/MLEM.Data/MLEM.Data.csproj index 7cb20da..4d47947 100644 --- a/MLEM.Data/MLEM.Data.csproj +++ b/MLEM.Data/MLEM.Data.csproj @@ -3,8 +3,9 @@ netstandard2.0 true true + NU1701 - + Ellpeck Simple loading and processing of textures and other data for MonoGame, including the ability to load non-XNB content files easily @@ -15,12 +16,11 @@ MIT Logo.png README.md - NU1701 - + - + all @@ -35,9 +35,9 @@ all - + - \ No newline at end of file + diff --git a/MLEM.Data/RuntimeTexturePacker.cs b/MLEM.Data/RuntimeTexturePacker.cs index 9d6f302..ac1e901 100644 --- a/MLEM.Data/RuntimeTexturePacker.cs +++ b/MLEM.Data/RuntimeTexturePacker.cs @@ -170,7 +170,7 @@ namespace MLEM.Data { foreach (var request in this.texturesToPack.OrderByDescending(t => t.Texture.Width * t.Texture.Height)) { request.PackedArea = this.FindFreeArea(request); // if this is the first position that this request fit in, no other requests of the same size will find a position before it - this.firstPossiblePosForSizeCache[request.PackedArea.Size] = request.PackedArea.Location; + this.firstPossiblePosForSizeCache[new Point(request.PackedArea.Width, request.PackedArea.Height)] = request.PackedArea.Location; this.alreadyPackedTextures.Add(request); } stopwatch.Stop(); @@ -198,7 +198,7 @@ namespace MLEM.Data { // invoke callbacks foreach (var request in this.alreadyPackedTextures) { - var packedArea = request.PackedArea.Shrink(new Point(request.Padding)); + var packedArea = request.PackedArea.Shrink(new Point(request.Padding, request.Padding)); request.Result.Invoke(new TextureRegion(this.PackedTexture, packedArea)); if (this.disposeTextures) request.Texture.Texture.Dispose(); @@ -232,7 +232,7 @@ namespace MLEM.Data { var lowestY = int.MaxValue; while (true) { var intersected = false; - var area = new Rectangle(pos, size); + var area = new Rectangle(pos.X, pos.Y, size.X, size.Y); foreach (var tex in this.alreadyPackedTextures) { if (tex.PackedArea.Intersects(area)) { pos.X = tex.PackedArea.Right; @@ -255,7 +255,7 @@ namespace MLEM.Data { private void CopyRegion(TextureData destination, Request request) { var data = this.GetCachedTextureData(request.Texture.Texture); - var location = request.PackedArea.Location + new Point(request.Padding); + var location = request.PackedArea.Location + new Point(request.Padding, request.Padding); for (var x = -request.Padding; x < request.Texture.Width + request.Padding; x++) { for (var y = -request.Padding; y < request.Texture.Height + request.Padding; y++) { Color srcColor; @@ -264,7 +264,7 @@ namespace MLEM.Data { srcColor = Color.Transparent; } else { // otherwise, we just use the closest pixel that is actually in bounds, causing the border pixels to be doubled up - var src = new Point(MathHelper.Clamp(x, 0, request.Texture.Width - 1), MathHelper.Clamp(y, 0, request.Texture.Height - 1)); + var src = new Point((int) MathHelper.Clamp(x, 0, request.Texture.Width - 1), (int) MathHelper.Clamp(y, 0, request.Texture.Height - 1)); srcColor = data[request.Texture.Position + src]; } destination[location + new Point(x, y)] = srcColor; diff --git a/MLEM.Extended/MLEM.Extended.FNA.csproj b/MLEM.Extended/MLEM.Extended.FNA.csproj new file mode 100644 index 0000000..6419392 --- /dev/null +++ b/MLEM.Extended/MLEM.Extended.FNA.csproj @@ -0,0 +1,42 @@ + + + netstandard2.0 + true + true + MLEM.Extended + $(DefineConstants);FNA + NU1702 + + + + Ellpeck + MLEM Library for Extending FNA extension that ties in with other FNA libraries + See the full changelog at https://mlem.ellpeck.de/CHANGELOG + fna ellpeck mlem utility extensions extended + https://mlem.ellpeck.de/ + https://github.com/Ellpeck/MLEM + MIT + Logo.png + README.md + + + + + + + all + + + all + + + + + + + + + + + + diff --git a/MLEM.Extended/MLEM.Extended.csproj b/MLEM.Extended/MLEM.Extended.csproj index 1739308..d54704a 100644 --- a/MLEM.Extended/MLEM.Extended.csproj +++ b/MLEM.Extended/MLEM.Extended.csproj @@ -26,7 +26,7 @@ all - + all diff --git a/MLEM.FNA.sln b/MLEM.FNA.sln new file mode 100644 index 0000000..ce9c1b9 --- /dev/null +++ b/MLEM.FNA.sln @@ -0,0 +1,76 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.FNA", "MLEM\MLEM.FNA.csproj", "{C2C88AE6-6274-4395-8B03-52AE898BA070}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Ui.FNA", "MLEM.Ui\MLEM.Ui.FNA.csproj", "{1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Startup.FNA", "MLEM.Startup\MLEM.Startup.FNA.csproj", "{FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Data.FNA", "MLEM.Data\MLEM.Data.FNA.csproj", "{6587BC91-0640-43FB-988A-4F545B8ACFC5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demos.FNA", "Demos\Demos.FNA.csproj", "{D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demos.DesktopGL.FNA", "Demos.DesktopGL\Demos.DesktopGL.FNA.csproj", "{AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.FNA", "Tests\Tests.FNA.csproj", "{C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MLEM.Extended.FNA", "MLEM.Extended\MLEM.Extended.FNA.csproj", "{A5B22930-DF4B-4A62-93ED-A6549F7B666B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FontStashSharp.FNA", "FontStashSharp\src\XNA\FontStashSharp.FNA.csproj", "{B805851C-9802-4239-AB16-AE77226771CA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNA", "FNA\FNA.csproj", "{35253CE1-C864-4CD3-8249-4D1319748E8F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FNA.Core", "FNA\FNA.Core.csproj", "{06459F72-CEAA-4B45-B2B1-708FC28D04F8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C2C88AE6-6274-4395-8B03-52AE898BA070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2C88AE6-6274-4395-8B03-52AE898BA070}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2C88AE6-6274-4395-8B03-52AE898BA070}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2C88AE6-6274-4395-8B03-52AE898BA070}.Release|Any CPU.Build.0 = Release|Any CPU + {1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B47A40B-3BF6-4933-A7DB-57EADEBDFB61}.Release|Any CPU.Build.0 = Release|Any CPU + {FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBE2C9B5-293D-47A7-9BA1-5A4BD1C4E816}.Release|Any CPU.Build.0 = Release|Any CPU + {6587BC91-0640-43FB-988A-4F545B8ACFC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6587BC91-0640-43FB-988A-4F545B8ACFC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6587BC91-0640-43FB-988A-4F545B8ACFC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6587BC91-0640-43FB-988A-4F545B8ACFC5}.Release|Any CPU.Build.0 = Release|Any CPU + {D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D83D7D24-14CB-4A7C-A80B-BA4FEC66AB55}.Release|Any CPU.Build.0 = Release|Any CPU + {AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB08FEC7-3AC3-4FDE-B632-226FAAD7F73F}.Release|Any CPU.Build.0 = Release|Any CPU + {C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C74FC4C5-3BE0-42A7-8BA6-4A9AD438A9E2}.Release|Any CPU.Build.0 = Release|Any CPU + {A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5B22930-DF4B-4A62-93ED-A6549F7B666B}.Release|Any CPU.Build.0 = Release|Any CPU + {B805851C-9802-4239-AB16-AE77226771CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B805851C-9802-4239-AB16-AE77226771CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B805851C-9802-4239-AB16-AE77226771CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B805851C-9802-4239-AB16-AE77226771CA}.Release|Any CPU.Build.0 = Release|Any CPU + {35253CE1-C864-4CD3-8249-4D1319748E8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35253CE1-C864-4CD3-8249-4D1319748E8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35253CE1-C864-4CD3-8249-4D1319748E8F}.Release|Any CPU.Build.0 = Release|Any CPU + {06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Debug|Any CPU.ActiveCfg = Debug|x64 + {06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Debug|Any CPU.Build.0 = Debug|x64 + {06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Release|Any CPU.ActiveCfg = Release|x64 + {06459F72-CEAA-4B45-B2B1-708FC28D04F8}.Release|Any CPU.Build.0 = Release|x64 + EndGlobalSection +EndGlobal diff --git a/MLEM.Startup/MLEM.Startup.FNA.csproj b/MLEM.Startup/MLEM.Startup.FNA.csproj new file mode 100644 index 0000000..57e5474 --- /dev/null +++ b/MLEM.Startup/MLEM.Startup.FNA.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.0 + true + true + MLEM.Startup + $(DefineConstants);FNA + + + + Ellpeck + MLEM Library for Extending FNA combined with some other useful libraries into a quick Game startup class + See the full changelog at https://mlem.ellpeck.de/CHANGELOG + fna ellpeck mlem utility extensions + https://mlem.ellpeck.de/ + https://github.com/Ellpeck/MLEM + MIT + Logo.png + README.md + + + + + + + + + all + + + + + + + + diff --git a/MLEM.Startup/MlemGame.cs b/MLEM.Startup/MlemGame.cs index 36a820f..2d7445d 100644 --- a/MLEM.Startup/MlemGame.cs +++ b/MLEM.Startup/MlemGame.cs @@ -69,7 +69,9 @@ namespace MLEM.Startup { this.GraphicsDeviceManager = new GraphicsDeviceManager(this) { PreferredBackBufferWidth = windowWidth, PreferredBackBufferHeight = windowHeight, + #if !FNA HardwareModeSwitch = false + #endif }; this.Window.AllowUserResizing = true; this.Content.RootDirectory = "Content"; diff --git a/MLEM.Ui/Elements/Element.cs b/MLEM.Ui/Elements/Element.cs index 2e9efd0..c0ef4a7 100644 --- a/MLEM.Ui/Elements/Element.cs +++ b/MLEM.Ui/Elements/Element.cs @@ -70,7 +70,7 @@ namespace MLEM.Ui.Elements { /// /// The size of this element, where X represents the width and Y represents the height. /// If the x or y value of the size is between 0 and 1, the size will be seen as a percentage of its parent's size rather than as an absolute value. - /// If the x (or y) value of the size is negative, the width (or height) is seen as a percentage of the element's resulting height (or width). + /// If the x (or y) value of the size is negative, the width (or height) is seen as a percentage of the element's resulting height (or width). /// /// /// The following example combines both types of percentage-based sizing. @@ -178,12 +178,12 @@ namespace MLEM.Ui.Elements { /// /// This element's transform matrix. /// Can easily be scaled using . - /// Note that, when this is non-null, a new call is used for this element. + /// Note that, when this is non-null, a new SpriteBatch.Begin call is used for this element. /// public Matrix Transform = Matrix.Identity; /// /// The call that this element should make to to begin drawing. - /// Note that, when this is non-null, a new call is used for this element. + /// Note that, when this is non-null, a new SpriteBatch.Begin call is used for this element. /// #pragma warning disable CS0618 [Obsolete("BeginImpl is deprecated. You can create a custom element class and override Draw instead.")] @@ -927,7 +927,7 @@ namespace MLEM.Ui.Elements { /// /// Draws this element by calling internally. - /// If or is set, a new call is also started. + /// If or is set, a new SpriteBatch.Begin call is also started. /// /// The game's time /// The sprite batch to use for drawing @@ -944,7 +944,7 @@ namespace MLEM.Ui.Elements { /// /// Draws this element by calling internally. - /// If or is set, a new call is also started. + /// If or is set, a new SpriteBatch.Begin call is also started. /// /// The game's time /// The sprite batch to use for drawing @@ -981,7 +981,7 @@ namespace MLEM.Ui.Elements { /// /// Draws this element and all of its children. Override this method to draw the content of custom elements. - /// Note that, when this is called, has already been called with custom etc. applied. + /// Note that, when this is called, SpriteBatch.Begin has already been called with custom etc. applied. /// /// The game's time /// The sprite batch to use for drawing @@ -998,7 +998,7 @@ namespace MLEM.Ui.Elements { /// /// Draws this element and all of its children. Override this method to draw the content of custom elements. - /// Note that, when this is called, has already been called with custom etc. applied. + /// Note that, when this is called, SpriteBatch.Begin has already been called with custom etc. applied. /// /// The game's time /// The sprite batch to use for drawing @@ -1021,7 +1021,7 @@ namespace MLEM.Ui.Elements { /// /// Draws this element and all of its early. /// Drawing early involves drawing onto instances rather than onto the screen. - /// Note that, when this is called, has not yet been called. + /// Note that, when this is called, SpriteBatch.Begin has not yet been called. /// /// The game's time /// The sprite batch to use for drawing diff --git a/MLEM.Ui/Elements/Image.cs b/MLEM.Ui/Elements/Image.cs index 67d5484..79ad69e 100644 --- a/MLEM.Ui/Elements/Image.cs +++ b/MLEM.Ui/Elements/Image.cs @@ -1,6 +1,7 @@ using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; using MLEM.Graphics; using MLEM.Misc; using MLEM.Textures; diff --git a/MLEM.Ui/Elements/Paragraph.cs b/MLEM.Ui/Elements/Paragraph.cs index 86c9def..2e81a5d 100644 --- a/MLEM.Ui/Elements/Paragraph.cs +++ b/MLEM.Ui/Elements/Paragraph.cs @@ -135,8 +135,8 @@ namespace MLEM.Ui.Elements { protected override Vector2 CalcActualSize(RectangleF parentArea) { var size = base.CalcActualSize(parentArea); this.ParseText(size); - var (w, h) = this.TokenizedText.Measure(this.RegularFont) * this.TextScale * this.TextScaleMultiplier * this.Scale; - return new Vector2(this.AutoAdjustWidth ? w + this.ScaledPadding.Width : size.X, h + this.ScaledPadding.Height); + var textSize = this.TokenizedText.Measure(this.RegularFont) * this.TextScale * this.TextScaleMultiplier * this.Scale; + return new Vector2(this.AutoAdjustWidth ? textSize.X + this.ScaledPadding.Width : size.X, textSize.Y + this.ScaledPadding.Height); } /// diff --git a/MLEM.Ui/Elements/ScrollBar.cs b/MLEM.Ui/Elements/ScrollBar.cs index 4af3790..e421def 100644 --- a/MLEM.Ui/Elements/ScrollBar.cs +++ b/MLEM.Ui/Elements/ScrollBar.cs @@ -3,6 +3,7 @@ using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input.Touch; +using MLEM.Extensions; using MLEM.Graphics; using MLEM.Input; using MLEM.Misc; @@ -195,13 +196,13 @@ namespace MLEM.Ui.Elements { } private void ScrollToPos(Vector2 position) { - var (width, height) = this.ScrollerSize * this.Scale; + var size = this.ScrollerSize * this.Scale; if (this.Horizontal) { - var offset = this.scrollStartOffset.X >= 0 && this.scrollStartOffset.X <= width ? this.scrollStartOffset.X : width / 2; - this.CurrentValue = (position.X - this.Area.X - offset) / (this.Area.Width - width) * this.MaxValue; + var offset = this.scrollStartOffset.X >= 0 && this.scrollStartOffset.X <= size.X ? this.scrollStartOffset.X : size.X / 2; + this.CurrentValue = (position.X - this.Area.X - offset) / (this.Area.Width - size.X) * this.MaxValue; } else { - var offset = this.scrollStartOffset.Y >= 0 && this.scrollStartOffset.Y <= height ? this.scrollStartOffset.Y : height / 2; - this.CurrentValue = (position.Y - this.Area.Y - offset) / (this.Area.Height - height) * this.MaxValue; + var offset = this.scrollStartOffset.Y >= 0 && this.scrollStartOffset.Y <= size.Y ? this.scrollStartOffset.Y : size.Y / 2; + this.CurrentValue = (position.Y - this.Area.Y - offset) / (this.Area.Height - size.Y) * this.MaxValue; } } diff --git a/MLEM.Ui/Elements/TextField.cs b/MLEM.Ui/Elements/TextField.cs index d319b97..8b97a98 100644 --- a/MLEM.Ui/Elements/TextField.cs +++ b/MLEM.Ui/Elements/TextField.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MLEM.Font; diff --git a/MLEM.Ui/Elements/Tooltip.cs b/MLEM.Ui/Elements/Tooltip.cs index 91670af..18b1f6e 100644 --- a/MLEM.Ui/Elements/Tooltip.cs +++ b/MLEM.Ui/Elements/Tooltip.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Microsoft.Xna.Framework; +using MLEM.Extensions; using MLEM.Input; using MLEM.Ui.Style; diff --git a/MLEM.Ui/MLEM.Ui.FNA.csproj b/MLEM.Ui/MLEM.Ui.FNA.csproj new file mode 100644 index 0000000..23f0414 --- /dev/null +++ b/MLEM.Ui/MLEM.Ui.FNA.csproj @@ -0,0 +1,35 @@ + + + netstandard2.0 + true + true + MLEM.Ui + $(DefineConstants);FNA + + + + Ellpeck + A mouse, keyboard, gamepad and touch ready Ui system for FNA that features automatic anchoring, sizing and several ready-to-use element types + See the full changelog at https://mlem.ellpeck.de/CHANGELOG + fna ellpeck mlem ui user interface graphical gui system mouse keyboard gamepad touch + https://mlem.ellpeck.de/ + https://github.com/Ellpeck/MLEM + MIT + Logo.png + README.md + + + + + + + + all + + + + + + + + diff --git a/MLEM.Ui/MLEM.Ui.csproj b/MLEM.Ui/MLEM.Ui.csproj index 49b3d35..e9aecaa 100644 --- a/MLEM.Ui/MLEM.Ui.csproj +++ b/MLEM.Ui/MLEM.Ui.csproj @@ -18,7 +18,7 @@ - + diff --git a/MLEM.Ui/Parsers/UiMarkdownParser.cs b/MLEM.Ui/Parsers/UiMarkdownParser.cs index 69ee617..449ba95 100644 --- a/MLEM.Ui/Parsers/UiMarkdownParser.cs +++ b/MLEM.Ui/Parsers/UiMarkdownParser.cs @@ -170,8 +170,13 @@ namespace MLEM.Ui.Parsers { Texture2D tex; if (loc.StartsWith("http")) { using (var client = new HttpClient()) { - using (var src = await client.GetStreamAsync(loc)) - tex = Texture2D.FromStream(this.GraphicsDevice, src); + using (var src = await client.GetStreamAsync(loc)) { + using (var memory = new MemoryStream()) { + // download the full stream before passing it to texture + await src.CopyToAsync(memory); + tex = Texture2D.FromStream(this.GraphicsDevice, memory); + } + } } } else { using (var stream = Path.IsPathRooted(loc) ? File.OpenRead(loc) : TitleContainer.OpenStream(loc)) diff --git a/MLEM.Ui/UiControls.cs b/MLEM.Ui/UiControls.cs index 6827ce5..a279e5e 100644 --- a/MLEM.Ui/UiControls.cs +++ b/MLEM.Ui/UiControls.cs @@ -159,7 +159,7 @@ namespace MLEM.Ui { // MOUSE INPUT if (this.HandleMouse) { - var mousedNow = this.GetElementUnderPos(this.Input.ViewportMousePosition.ToVector2()); + var mousedNow = this.GetElementUnderPos(new Vector2(this.Input.ViewportMousePosition.X, this.Input.ViewportMousePosition.Y)); this.SetMousedElement(mousedNow); if (this.Input.IsMouseButtonPressedAvailable(MouseButton.Left)) { @@ -414,8 +414,8 @@ namespace MLEM.Ui { foreach (var child in children) { if (child == this.SelectedElement) continue; - var (xOffset, yOffset) = child.Area.Center - this.SelectedElement.Area.Center; - var angle = Math.Abs(MathHelper.WrapAngle(direction.Angle() - (float) Math.Atan2(yOffset, xOffset))); + var offset = child.Area.Center - this.SelectedElement.Area.Center; + var angle = Math.Abs(MathHelper.WrapAngle(direction.Angle() - (float) Math.Atan2(offset.Y, offset.X))); if (angle >= MathHelper.PiOver2 - Element.Epsilon) continue; var distSq = child.Area.DistanceSquared(this.SelectedElement.Area); diff --git a/MLEM.Ui/UiMetrics.cs b/MLEM.Ui/UiMetrics.cs index d06b1f3..81901e0 100644 --- a/MLEM.Ui/UiMetrics.cs +++ b/MLEM.Ui/UiMetrics.cs @@ -5,7 +5,6 @@ using MLEM.Ui.Elements; namespace MLEM.Ui { /// /// A snapshot of update and rendering statistics from to be used for runtime debugging and profiling. - /// This metrics struct works similarly to . /// public struct UiMetrics { diff --git a/MLEM.Ui/UiSystem.cs b/MLEM.Ui/UiSystem.cs index bc5b2d3..f25ce22 100644 --- a/MLEM.Ui/UiSystem.cs +++ b/MLEM.Ui/UiSystem.cs @@ -88,7 +88,7 @@ namespace MLEM.Ui { public SamplerState SamplerState; /// /// The depth stencil state that this ui system and all of its elements draw with. - /// The default is , which is also the default for . + /// The default is , which is also the default for SpriteBatch.Begin. /// [Obsolete("Set this through SpriteBatchContext instead")] public DepthStencilState DepthStencilState; @@ -241,10 +241,10 @@ namespace MLEM.Ui { MlemPlatform.Current?.AddTextInputListener(game.Window, (sender, key, character) => this.ApplyToAll(e => e.OnTextInput?.Invoke(e, key, character))); if (automaticViewport) { - this.Viewport = new Rectangle(Point.Zero, game.Window.ClientBounds.Size); - this.AutoScaleReferenceSize = this.Viewport.Size; + this.Viewport = new Rectangle(0, 0, game.Window.ClientBounds.Width, game.Window.ClientBounds.Height); + this.AutoScaleReferenceSize = new Point(this.Viewport.X, this.Viewport.Y); game.Window.ClientSizeChanged += (sender, args) => { - this.Viewport = new Rectangle(Point.Zero, game.Window.ClientBounds.Size); + this.Viewport = new Rectangle(0, 0, game.Window.ClientBounds.Width, game.Window.ClientBounds.Height); }; } @@ -570,11 +570,11 @@ namespace MLEM.Ui { /// public event Element.GenericCallback OnElementRemoved; /// - /// Event that is invoked when this gets added to a in + /// Event that is invoked when this gets added to a in /// public event Action OnAddedToUi; /// - /// Event that is invoked when this gets removed from a in + /// Event that is invoked when this gets removed from a in /// public event Action OnRemovedFromUi; diff --git a/MLEM/Cameras/Camera.cs b/MLEM/Cameras/Camera.cs index d8f7e0e..ee7288a 100644 --- a/MLEM/Cameras/Camera.cs +++ b/MLEM/Cameras/Camera.cs @@ -58,7 +58,7 @@ namespace MLEM.Cameras { } /// /// The matrix that this camera "sees", based on its position and scale. - /// Use this in your calls to render based on the camera's viewport. + /// Use this in your SpriteBatch.Begin calls to render based on the camera's viewport. /// public Matrix ViewMatrix { get { @@ -105,7 +105,7 @@ namespace MLEM.Cameras { /// Whether the camera's should be rounded to full integers when calculating the public Camera(GraphicsDevice graphicsDevice, bool roundPosition = true) { this.graphicsDevice = graphicsDevice; - this.AutoScaleReferenceSize = this.Viewport.Size; + this.AutoScaleReferenceSize = new Point(this.Viewport.Width, this.Viewport.Height); this.RoundPosition = roundPosition; } @@ -173,7 +173,7 @@ namespace MLEM.Cameras { /// The amount to zoom in or out by /// The position that should be regarded as the zoom's center, in screen space. The default value is the center. public void Zoom(float delta, Vector2? zoomCenter = null) { - var center = (zoomCenter ?? this.Viewport.Size.ToVector2() / 2) / this.ActualScale; + var center = (zoomCenter ?? new Vector2(this.Viewport.Width, this.Viewport.Height) / 2) / this.ActualScale; var lastScale = this.Scale; this.Scale += delta; this.Position += center * ((this.Scale - lastScale) / this.Scale); diff --git a/MLEM/Extensions/GraphicsExtensions.cs b/MLEM/Extensions/GraphicsExtensions.cs index d6657a6..85aeac3 100644 --- a/MLEM/Extensions/GraphicsExtensions.cs +++ b/MLEM/Extensions/GraphicsExtensions.cs @@ -57,7 +57,7 @@ namespace MLEM.Extensions { /// The graphics device manager /// The window whose bounds to use public static void ResetWidthAndHeight(this GraphicsDeviceManager manager, GameWindow window) { - var (_, _, width, height) = window.ClientBounds; + var (width, height) = (window.ClientBounds.Width, window.ClientBounds.Height); manager.PreferredBackBufferWidth = Math.Max(height, width); manager.PreferredBackBufferHeight = Math.Min(height, width); manager.ApplyChanges(); @@ -90,7 +90,12 @@ namespace MLEM.Extensions { /// The target to apply public TargetContext(GraphicsDevice device, RenderTarget2D target) { this.device = device; + #if FNA + // RenderTargetCount doesn't exist in FNA but we still want the optimization in MG + this.lastTargets = device.GetRenderTargets(); + #else this.lastTargets = device.RenderTargetCount <= 0 ? null : device.GetRenderTargets(); + #endif device.SetRenderTarget(target); } diff --git a/MLEM/Extensions/NumberExtensions.cs b/MLEM/Extensions/NumberExtensions.cs index 61b12a9..3900c06 100644 --- a/MLEM/Extensions/NumberExtensions.cs +++ b/MLEM/Extensions/NumberExtensions.cs @@ -223,13 +223,13 @@ namespace MLEM.Extensions { /// The matrix /// The rotation of the matrix public static Quaternion Rotation(this Matrix matrix) { - var (scX, scY, scZ) = matrix.Scale(); - if (scX == 0 || scY == 0 || scZ == 0) + var sc = matrix.Scale(); + if (sc.X == 0 || sc.Y == 0 || sc.Z == 0) return Quaternion.Identity; return Quaternion.CreateFromRotationMatrix(new Matrix( - matrix.M11 / scX, matrix.M12 / scX, matrix.M13 / scX, 0, - matrix.M21 / scY, matrix.M22 / scY, matrix.M23 / scY, 0, - matrix.M31 / scZ, matrix.M32 / scZ, matrix.M33 / scZ, 0, + matrix.M11 / sc.X, matrix.M12 / sc.X, matrix.M13 / sc.X, 0, + matrix.M21 / sc.Y, matrix.M22 / sc.Y, matrix.M23 / sc.Y, 0, + matrix.M31 / sc.Z, matrix.M32 / sc.Z, matrix.M33 / sc.Z, 0, 0, 0, 0, 1)); } @@ -250,7 +250,7 @@ namespace MLEM.Extensions { /// The quaternion /// The rotation of the quaternion public static Vector3 RotationVector(this Quaternion quaternion) { - var (x, y, z, w) = quaternion; + var (x, y, z, w) = (quaternion.X, quaternion.Y, quaternion.Z, quaternion.W); return new Vector3( (float) Math.Atan2(2 * (w * x + y * z), 1 - 2 * (x * x + y * y)), (float) Math.Asin(MathHelper.Clamp(2 * (w * y - z * x), -1, 1)), @@ -268,16 +268,16 @@ namespace MLEM.Extensions { /// The amount that the penetration occured by, in the direction of /// Whether or not a penetration occured public static bool Penetrate(this RectangleF rect, RectangleF other, out Vector2 normal, out float penetration) { - var (offsetX, offsetY) = other.Center - rect.Center; - var overlapX = rect.Width / 2 + other.Width / 2 - Math.Abs(offsetX); + var offset = other.Center - rect.Center; + var overlapX = rect.Width / 2 + other.Width / 2 - Math.Abs(offset.X); if (overlapX > 0) { - var overlapY = rect.Height / 2 + other.Height / 2 - Math.Abs(offsetY); + var overlapY = rect.Height / 2 + other.Height / 2 - Math.Abs(offset.Y); if (overlapY > 0) { if (overlapX < overlapY) { - normal = new Vector2(offsetX < 0 ? -1 : 1, 0); + normal = new Vector2(offset.X < 0 ? -1 : 1, 0); penetration = overlapX; } else { - normal = new Vector2(0, offsetY < 0 ? -1 : 1); + normal = new Vector2(0, offset.Y < 0 ? -1 : 1); penetration = overlapY; } return true; @@ -288,5 +288,23 @@ namespace MLEM.Extensions { return false; } + #if FNA + /// + /// Gets a representation for this object. + /// + /// A representation for this object. + public static Point ToPoint(this Vector2 vector) { + return new Point((int) vector.X, (int) vector.Y); + } + + /// + /// Gets a representation for this object. + /// + /// A representation for this object. + public static Vector2 ToVector2(this Point point) { + return new Vector2(point.X, point.Y); + } + #endif + } } diff --git a/MLEM/Font/GenericFont.cs b/MLEM/Font/GenericFont.cs index dc8b370..28c4748 100644 --- a/MLEM/Font/GenericFont.cs +++ b/MLEM/Font/GenericFont.cs @@ -285,18 +285,18 @@ namespace MLEM.Font { } private void DrawString(SpriteBatch batch, CharSource text, Vector2 position, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth) { - var (flipX, flipY) = Vector2.Zero; + var (flipX, flipY) = (0F, 0F); var flippedV = (effects & SpriteEffects.FlipVertically) != 0; var flippedH = (effects & SpriteEffects.FlipHorizontally) != 0; if (flippedV || flippedH) { - var (w, h) = this.MeasureString(text, false, null); + var size = this.MeasureString(text, false, null); if (flippedH) { origin.X *= -1; - flipX = -w; + flipX = -size.X; } if (flippedV) { origin.Y *= -1; - flipY = this.LineHeight - h; + flipY = this.LineHeight - size.Y; } } @@ -327,17 +327,17 @@ namespace MLEM.Font { } var cString = c.ToCachedString(); - var (cW, cH) = this.MeasureString(cString); + var cSize = this.MeasureString(cString); var charPos = offset; if (flippedH) - charPos.X += cW; + charPos.X += cSize.X; if (flippedV) - charPos.Y += cH - this.LineHeight; + charPos.Y += cSize.Y - this.LineHeight; Vector2.Transform(ref charPos, ref trans, out charPos); this.DrawChar(batch, cString, charPos, color, rotation, scale, effects, layerDepth); - offset.X += cW; + offset.X += cSize.X; } } diff --git a/MLEM/Font/GenericSpriteFont.cs b/MLEM/Font/GenericSpriteFont.cs index d350028..3250149 100644 --- a/MLEM/Font/GenericSpriteFont.cs +++ b/MLEM/Font/GenericSpriteFont.cs @@ -42,6 +42,10 @@ namespace MLEM.Font { } private static SpriteFont SetDefaults(SpriteFont font) { + #if FNA + // none of the copying is available with FNA + return font; + #else // we copy the font here to set the default character to a space return new SpriteFont( font.Texture, @@ -52,6 +56,7 @@ namespace MLEM.Font { font.Spacing, font.Glyphs.Select(g => new Vector3(g.LeftSideBearing, g.Width, g.RightSideBearing)).ToList(), ' '); + #endif } } diff --git a/MLEM/Formatting/Codes/UnderlineCode.cs b/MLEM/Formatting/Codes/UnderlineCode.cs index 9df532f..90aecf7 100644 --- a/MLEM/Formatting/Codes/UnderlineCode.cs +++ b/MLEM/Formatting/Codes/UnderlineCode.cs @@ -23,9 +23,9 @@ namespace MLEM.Formatting.Codes { // don't underline spaces at the end of lines if (c == ' ' && token.DisplayString.Length > indexInToken + 1 && token.DisplayString[indexInToken + 1] == '\n') return false; - var (w, h) = font.MeasureString(cString) * scale; - var t = h * this.thickness; - batch.Draw(batch.GetBlankTexture(), new RectangleF(pos.X, pos.Y + this.yOffset * h - t, w, t), color); + var size = font.MeasureString(cString) * scale; + var t = size.Y * this.thickness; + batch.Draw(batch.GetBlankTexture(), new RectangleF(pos.X, pos.Y + this.yOffset * size.Y - t, size.X, t), color); return false; } diff --git a/MLEM/Graphics/AutoTiling.cs b/MLEM/Graphics/AutoTiling.cs index 676828a..f5efc7f 100644 --- a/MLEM/Graphics/AutoTiling.cs +++ b/MLEM/Graphics/AutoTiling.cs @@ -62,8 +62,8 @@ namespace MLEM.Graphics { /// /// This method allows for a tiled texture to be drawn in an auto-tiling mode. /// This allows, for example, a grass patch on a tilemap to have nice looking edges that transfer over into a path without any hard edges between tiles. - /// - /// This method is a more complex version of that overlays separate border textures on a background texture region, which also allows for non-rectangular texture areas to be used easily. + /// + /// This method is a more complex version of that overlays separate border textures on a background texture region, which also allows for non-rectangular texture areas to be used easily. /// For auto-tiling in this way to work, the overlay sections have to be laid out as follows: 16 sections aligned horizontally within the texture file, with the following information: /// /// The texture used for straight, horizontal borders, with the borders facing away from the center, split up into four parts: top left, then top right, then bottom left, then bottom right @@ -138,8 +138,8 @@ namespace MLEM.Graphics { var xUr = up && right ? connectsTo(1, -1) ? 0 : 4 : right ? 1 : up ? 3 : 2; var xDl = down && left ? connectsTo(-1, 1) ? 0 : 4 : left ? 1 : down ? 3 : 2; var xDr = down && right ? connectsTo(1, 1) ? 0 : 4 : right ? 1 : down ? 3 : 2; - var (w, h) = textureRegion.Size; - var (w2, h2) = new Point(w / 2, h / 2); + var (w, h) = (textureRegion.Width, textureRegion.Height); + var (w2, h2) = (w / 2, h / 2); return ( new Vector2(pos.X, pos.Y), new Rectangle(textureRegion.X + xUl * w, textureRegion.Y, w2, h2), new Vector2(pos.X + w2 * scale.X, pos.Y), new Rectangle(textureRegion.X + w2 + xUr * w, textureRegion.Y, w2, h2), @@ -156,7 +156,7 @@ namespace MLEM.Graphics { var xUr = up && right ? connectsTo(1, -1) ? -1 : 13 : right ? 1 : up ? 9 : 5; var xDl = down && left ? connectsTo(-1, 1) ? -1 : 14 : left ? 2 : down ? 10 : 6; var xDr = down && right ? connectsTo(1, 1) ? -1 : 15 : right ? 3 : down ? 11 : 7; - var (w, h) = textureRegion.Size; + var (w, h) = (textureRegion.Width, textureRegion.Height); return ( xUl < 0 ? Rectangle.Empty : new Rectangle(textureRegion.X + xUl * w, textureRegion.Y, w, h), xUr < 0 ? Rectangle.Empty : new Rectangle(textureRegion.X + xUr * w, textureRegion.Y, w, h), diff --git a/MLEM/Graphics/SpriteBatchContext.cs b/MLEM/Graphics/SpriteBatchContext.cs index 0e1734e..4983075 100644 --- a/MLEM/Graphics/SpriteBatchContext.cs +++ b/MLEM/Graphics/SpriteBatchContext.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics; namespace MLEM.Graphics { /// - /// A sprite batch context is a set of information for a to use, which encapsulates all of the information usually passed directly to . + /// A sprite batch context is a set of information for a to use, which encapsulates all of the information usually passed directly to SpriteBatch.Begin. /// To use a sprite batch context effectively, the extension methods in should be used. /// public struct SpriteBatchContext { @@ -17,7 +17,7 @@ namespace MLEM.Graphics { /// public BlendState BlendState; /// - /// State of the sampler. + /// State of the sampler. /// public SamplerState SamplerState; /// diff --git a/MLEM/Graphics/StaticSpriteBatch.cs b/MLEM/Graphics/StaticSpriteBatch.cs index e27d00c..3bdc033 100644 --- a/MLEM/Graphics/StaticSpriteBatch.cs +++ b/MLEM/Graphics/StaticSpriteBatch.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using MLEM.Extensions; namespace MLEM.Graphics { /// @@ -174,18 +176,18 @@ namespace MLEM.Graphics { for (var i = 0; i < this.FilledBuffers; i++) { var buffer = this.vertexBuffers[i]; var texture = this.textures[i]; - var tris = Math.Min(this.items.Count * 4 - totalIndex, buffer.VertexCount) / 4 * 2; + var verts = Math.Min(this.items.Count * 4 - totalIndex, buffer.VertexCount); this.graphicsDevice.SetVertexBuffer(buffer); if (effect != null) { foreach (var pass in effect.CurrentTechnique.Passes) { pass.Apply(); this.graphicsDevice.Textures[0] = texture; - this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, tris); + this.DrawPrimitives(verts); } } else { this.graphicsDevice.Textures[0] = texture; - this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, tris); + this.DrawPrimitives(verts); } totalIndex += buffer.VertexCount; @@ -290,10 +292,11 @@ namespace MLEM.Graphics { if ((effects & SpriteEffects.FlipHorizontally) != 0) (texBr.X, texTl.X) = (texTl.X, texBr.X); + var destSize = new Vector2(destinationRectangle.Width, destinationRectangle.Height); if (rotation == 0) { - return this.Add(texture, destinationRectangle.Location.ToVector2() - origin, destinationRectangle.Size.ToVector2(), color, texTl, texBr, layerDepth); + return this.Add(texture, destinationRectangle.Location.ToVector2() - origin, destSize, color, texTl, texBr, layerDepth); } else { - return this.Add(texture, destinationRectangle.Location.ToVector2(), -origin, destinationRectangle.Size.ToVector2(), (float) Math.Sin(rotation), (float) Math.Cos(rotation), color, texTl, texBr, layerDepth); + return this.Add(texture, destinationRectangle.Location.ToVector2(), -origin, destSize, (float) Math.Sin(rotation), (float) Math.Cos(rotation), color, texTl, texBr, layerDepth); } } @@ -439,6 +442,14 @@ namespace MLEM.Graphics { this.textures.Insert(index, texture); } + private void DrawPrimitives(int vertices) { + #if FNA + this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices, 0, vertices / 4 * 2); + #else + this.graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices / 4 * 2); + #endif + } + /// /// A struct that represents an item added to a using Add or any of its overloads. /// An item returned after adding can be removed using . @@ -463,5 +474,58 @@ namespace MLEM.Graphics { } + #if FNA + private class SpriteEffect : Effect { + + private EffectParameter matrixParam; + private Viewport lastViewport; + private Matrix projection; + + public Matrix? TransformMatrix { get; set; } + + public SpriteEffect(GraphicsDevice device) : base(device, SpriteEffect.LoadEffectCode()) { + this.CacheEffectParameters(); + } + + private SpriteEffect(SpriteEffect cloneSource) : base(cloneSource) { + this.CacheEffectParameters(); + } + + public override Effect Clone() { + return new SpriteEffect(this); + } + + private void CacheEffectParameters() { + this.matrixParam = this.Parameters["MatrixTransform"]; + } + + protected override void OnApply() { + var vp = this.GraphicsDevice.Viewport; + if (vp.Width != this.lastViewport.Width || vp.Height != this.lastViewport.Height) { + Matrix.CreateOrthographicOffCenter(0, vp.Width, vp.Height, 0, 0, -1, out this.projection); + this.projection.M41 += -0.5f * this.projection.M11; + this.projection.M42 += -0.5f * this.projection.M22; + this.lastViewport = vp; + } + + if (this.TransformMatrix.HasValue) { + this.matrixParam.SetValue(this.TransformMatrix.GetValueOrDefault() * this.projection); + } else { + this.matrixParam.SetValue(this.projection); + } + } + + private static byte[] LoadEffectCode() { + using (var stream = typeof(Effect).Assembly.GetManifestResourceStream("Microsoft.Xna.Framework.Graphics.Effect.Resources.SpriteEffect.fxb")) { + using (var memory = new MemoryStream()) { + stream.CopyTo(memory); + return memory.ToArray(); + } + } + } + + } + #endif + } } diff --git a/MLEM/Input/InputHandler.cs b/MLEM/Input/InputHandler.cs index d063f34..90064a6 100644 --- a/MLEM/Input/InputHandler.cs +++ b/MLEM/Input/InputHandler.cs @@ -15,6 +15,12 @@ namespace MLEM.Input { /// public class InputHandler : GameComponent { + #if FNA + private const int MaximumGamePadCount = 4; + #else + private static readonly int MaximumGamePadCount = GamePad.MaximumGamePadCount; + #endif + /// /// Contains all of the gestures that have finished during the last update call. /// To easily query these gestures, use or . @@ -80,11 +86,11 @@ namespace MLEM.Input { /// /// Contains the touch state from the last update call /// - public TouchCollection LastTouchState { get; private set; } + public TouchCollection LastTouchState { get; private set; } = new TouchCollection(Array.Empty()); /// /// Contains the current touch state /// - public TouchCollection TouchState { get; private set; } + public TouchCollection TouchState { get; private set; } = new TouchCollection(Array.Empty()); /// /// Contains the , but with the taken into account. /// @@ -109,7 +115,7 @@ namespace MLEM.Input { /// /// Contains the position of the mouse from the last update call, extracted from /// - public Point LastMousePosition => this.LastMouseState.Position; + public Point LastMousePosition => new Point(this.LastMouseState.X, this.LastMouseState.Y); /// /// Contains the , but with the taken into account. /// @@ -117,7 +123,7 @@ namespace MLEM.Input { /// /// Contains the current position of the mouse, extracted from /// - public Point MousePosition => this.MouseState.Position; + public Point MousePosition => new Point(this.MouseState.X, this.MouseState.Y); /// /// Contains the , but with the taken into account. /// @@ -139,11 +145,11 @@ namespace MLEM.Input { /// public KeyboardState KeyboardState { get; private set; } - private readonly GamePadState[] lastGamepads = new GamePadState[GamePad.MaximumGamePadCount]; - private readonly GamePadState[] gamepads = new GamePadState[GamePad.MaximumGamePadCount]; - private readonly DateTime[] lastGamepadButtonRepeats = new DateTime[GamePad.MaximumGamePadCount]; - private readonly bool[] triggerGamepadButtonRepeat = new bool[GamePad.MaximumGamePadCount]; - private readonly Buttons?[] heldGamepadButtons = new Buttons?[GamePad.MaximumGamePadCount]; + private readonly GamePadState[] lastGamepads = new GamePadState[MaximumGamePadCount]; + private readonly GamePadState[] gamepads = new GamePadState[MaximumGamePadCount]; + private readonly DateTime[] lastGamepadButtonRepeats = new DateTime[MaximumGamePadCount]; + private readonly bool[] triggerGamepadButtonRepeat = new bool[MaximumGamePadCount]; + private readonly Buttons?[] heldGamepadButtons = new Buttons?[MaximumGamePadCount]; private readonly List gestures = new List(); private readonly HashSet<(GenericInput, int)> consumedPresses = new HashSet<(GenericInput, int)>(); @@ -209,7 +215,7 @@ namespace MLEM.Input { if (this.HandleMouse) { this.LastMouseState = this.MouseState; var state = Mouse.GetState(); - if (active && this.Game.GraphicsDevice.Viewport.Bounds.Contains(state.Position)) { + if (active && this.Game.GraphicsDevice.Viewport.Bounds.Contains(state.X, state.Y)) { this.MouseState = state; foreach (var button in MouseExtensions.MouseButtons) { if (state.GetState(button) == ButtonState.Pressed) @@ -217,18 +223,22 @@ namespace MLEM.Input { } } else { // mouse position and scroll wheel value should be preserved when the mouse is out of bounds + #if FNA + this.MouseState = new MouseState(state.X, state.Y, state.ScrollWheelValue, 0, 0, 0, 0, 0); + #else this.MouseState = new MouseState(state.X, state.Y, state.ScrollWheelValue, 0, 0, 0, 0, 0, state.HorizontalScrollWheelValue); + #endif } } if (this.HandleGamepads) { - this.ConnectedGamepads = GamePad.MaximumGamePadCount; - for (var i = 0; i < GamePad.MaximumGamePadCount; i++) { + this.ConnectedGamepads = MaximumGamePadCount; + for (var i = 0; i < MaximumGamePadCount; i++) { this.lastGamepads[i] = this.gamepads[i]; - this.gamepads[i] = GamePadState.Default; - if (GamePad.GetCapabilities(i).IsConnected) { + this.gamepads[i] = default; + if (GamePad.GetCapabilities((PlayerIndex) i).IsConnected) { if (active) { - this.gamepads[i] = GamePad.GetState(i); + this.gamepads[i] = GamePad.GetState((PlayerIndex) i); foreach (var button in EnumHelper.Buttons) { if (this.IsGamepadButtonDown(button, i)) this.AccumulateDown(button, i); @@ -263,12 +273,13 @@ namespace MLEM.Input { this.LastTouchState = this.TouchState; this.LastViewportTouchState = this.ViewportTouchState; - this.TouchState = active ? TouchPanel.GetState() : default; + this.TouchState = active ? TouchPanel.GetState() : new TouchCollection(Array.Empty()); if (this.TouchState.Count > 0 && this.ViewportOffset != Point.Zero) { this.ViewportTouchState = new List(); foreach (var touch in this.TouchState) { touch.TryGetPreviousLocation(out var previous); - this.ViewportTouchState.Add(new TouchLocation(touch.Id, touch.State, touch.Position + this.ViewportOffset.ToVector2(), previous.State, previous.Position + this.ViewportOffset.ToVector2())); + var offset = new Vector2(this.ViewportOffset.X, this.ViewportOffset.Y); + this.ViewportTouchState.Add(new TouchLocation(touch.Id, touch.State, touch.Position + offset, previous.State, previous.Position + offset)); } } else { this.ViewportTouchState = this.TouchState; @@ -548,7 +559,7 @@ namespace MLEM.Input { /// /// Returns whether the given key is considered pressed. /// A gamepad button is considered pressed if it was down the last update call, and is up the current update call. If is true, this behavior is inverted. - /// This has the same behavior as , but ignores gamepad repeat events. + /// This has the same behavior as , but ignores gamepad repeat events. /// If is false, this method does the same as . /// /// The button to query @@ -613,7 +624,8 @@ namespace MLEM.Input { /// True if a gesture of the type was found, otherwise false public bool GetViewportGesture(GestureType type, out GestureSample sample) { if (this.GetGesture(type, out var original)) { - sample = new GestureSample(original.GestureType, original.Timestamp, original.Position + this.ViewportOffset.ToVector2(), original.Position2 + this.ViewportOffset.ToVector2(), original.Delta, original.Delta2); + var offset = new Vector2(this.ViewportOffset.X, this.ViewportOffset.Y); + sample = new GestureSample(original.GestureType, original.Timestamp, original.Position + offset, original.Position2 + offset, original.Delta, original.Delta2); return true; } sample = default; diff --git a/MLEM/Input/TextInput.cs b/MLEM/Input/TextInput.cs index 331b519..c574247 100644 --- a/MLEM/Input/TextInput.cs +++ b/MLEM/Input/TextInput.cs @@ -91,7 +91,7 @@ namespace MLEM.Input { public int CaretPos { get => this.caretPos; set { - var val = MathHelper.Clamp(value, 0, this.text.Length); + var val = (int) MathHelper.Clamp(value, 0F, this.text.Length); if (this.caretPos != val) { this.caretPos = val; this.caretBlinkTimer = 0; @@ -226,12 +226,14 @@ namespace MLEM.Input { /// /// A method that should be called when the given text should be entered into this text input. - /// This method is designed to be used with the event. + /// This method is designed to be used with or the TextInput event provided by MonoGame and FNA. /// /// The key that was pressed. /// The character that the represents. /// Whether text was successfully input. public bool OnTextInput(Keys key, char character) { + // FNA's text input event doesn't supply keys, so we handle this in Update + #if !FNA if (key == Keys.Back) { if (this.CaretPos > 0) { this.CaretPos--; @@ -246,6 +248,9 @@ namespace MLEM.Input { return this.InsertText(character); } return false; + #else + return this.InsertText(character); + #endif } /// @@ -254,26 +259,37 @@ namespace MLEM.Input { /// The current game time. /// The input handler to use for input querying. public void Update(GameTime time, InputHandler input) { + // FNA's text input event doesn't supply keys, so we handle this here + #if FNA + if (this.CaretPos > 0 && input.TryConsumePressed(Keys.Back)) { + this.CaretPos--; + this.RemoveText(this.CaretPos, 1); + } else if (this.CaretPos < this.text.Length && input.TryConsumePressed(Keys.Delete)) { + this.RemoveText(this.CaretPos, 1); + } else if (this.Multiline && input.TryConsumePressed(Keys.Enter)) { + this.InsertText('\n'); + } else + #endif if (this.CaretPos > 0 && input.TryConsumePressed(Keys.Left)) { this.CaretPos--; } else if (this.CaretPos < this.text.Length && input.TryConsumePressed(Keys.Right)) { this.CaretPos++; } else if (this.Multiline && input.IsKeyPressedAvailable(Keys.Up) && this.MoveCaretToLine(this.CaretLine - 1)) { - input.TryConsumeKeyPressed(Keys.Up); + input.TryConsumePressed(Keys.Up); } else if (this.Multiline && input.IsKeyPressedAvailable(Keys.Down) && this.MoveCaretToLine(this.CaretLine + 1)) { - input.TryConsumeKeyPressed(Keys.Down); - } else if (this.CaretPos != 0 && input.TryConsumeKeyPressed(Keys.Home)) { + input.TryConsumePressed(Keys.Down); + } else if (this.CaretPos != 0 && input.TryConsumePressed(Keys.Home)) { this.CaretPos = 0; - } else if (this.CaretPos != this.text.Length && input.TryConsumeKeyPressed(Keys.End)) { + } else if (this.CaretPos != this.text.Length && input.TryConsumePressed(Keys.End)) { this.CaretPos = this.text.Length; } else if (input.IsModifierKeyDown(ModifierKey.Control)) { if (input.IsKeyPressedAvailable(Keys.V)) { var clip = this.PasteFromClipboardFunction?.Invoke(); if (clip != null) { this.InsertText(clip, true); - input.TryConsumeKeyPressed(Keys.V); + input.TryConsumePressed(Keys.V); } - } else if (input.TryConsumeKeyPressed(Keys.C)) { + } else if (input.TryConsumePressed(Keys.C)) { // until there is text selection, just copy the whole content this.CopyToClipboardFunction?.Invoke(this.Text); } diff --git a/MLEM/MLEM.FNA.csproj b/MLEM/MLEM.FNA.csproj new file mode 100644 index 0000000..fe14dd5 --- /dev/null +++ b/MLEM/MLEM.FNA.csproj @@ -0,0 +1,32 @@ + + + netstandard2.0 + true + true + MLEM + $(DefineConstants);FNA + + + + Ellpeck + MLEM Library for Extending FNA provides extension methods and additional features for FNA + See the full changelog at https://mlem.ellpeck.de/CHANGELOG + fna ellpeck mlem utility extensions + https://mlem.ellpeck.de/ + https://github.com/Ellpeck/MLEM + MIT + Logo.png + README.md + + + + + all + + + + + + + + diff --git a/MLEM/Misc/Direction2.cs b/MLEM/Misc/Direction2.cs index f5f6aa6..443bbb6 100644 --- a/MLEM/Misc/Direction2.cs +++ b/MLEM/Misc/Direction2.cs @@ -182,8 +182,8 @@ namespace MLEM.Misc { /// The direction whose angle to get /// The direction's angle public static float Angle(this Direction2 dir) { - var (x, y) = dir.Offset(); - return (float) Math.Atan2(y, x); + var off = dir.Offset(); + return (float) Math.Atan2(off.Y, off.X); } /// diff --git a/MLEM/Misc/MlemPlatform.cs b/MLEM/Misc/MlemPlatform.cs index f63de32..eacbb07 100644 --- a/MLEM/Misc/MlemPlatform.cs +++ b/MLEM/Misc/MlemPlatform.cs @@ -57,9 +57,9 @@ namespace MLEM.Misc { /// /// A delegate method that can be used for /// - /// The object that sent the event. The used in most cases. - /// The key that was pressed - /// The character that corresponds to that key + /// The object that sent the event. The or used in most cases. + /// The key that was pressed. Note that this is always on FNA. + /// The character that corresponds to that key. public delegate void TextInputCallback(object sender, Keys key, char character); /// @@ -78,10 +78,10 @@ namespace MLEM.Misc { private readonly Action> addListener; /// - /// Creates a new DesktopGL-based platform + /// Creates a new DesktopGL-based platform. /// See class documentation for more detailed information. /// - /// The function that is used to add a text input listener + /// The function that is used to add a text input listener. public DesktopGl(Action> addListener) { this.addListener = addListener; } @@ -109,6 +109,44 @@ namespace MLEM.Misc { } + /// + /// The MLEM Desktop platform for FNA. + /// This platform uses the built-in FNA TextInputEXT event, which makes this listener work with any keyboard localization natively. + /// This platform is initialized as follows: + /// + /// MlemPlatform.Current = new MlemPlatform.DesktopFna(a => TextInputEXT.TextInput += a); + /// + /// + public class DesktopFna : MlemPlatform { + + private readonly Action> addListener; + + /// + /// Creates a new Desktop for FNA platform. + /// See class documentation for more detailed information. + /// + /// The function that is used to add a text input listener. + public DesktopFna(Action> addListener) { + this.addListener = addListener; + } + + /// + public override Task OpenOnScreenKeyboard(string title, string description, string defaultText, bool usePasswordMode) { + return Task.FromResult(null); + } + + /// + public override void AddTextInputListener(GameWindow window, TextInputCallback callback) { + this.addListener(c => callback(this, Keys.None, c)); + } + + /// + public override void OpenLinkOrFile(string link) { + Process.Start(new ProcessStartInfo(link) {UseShellExecute = true}); + } + + } + /// /// The MLEM platform for mobile platforms as well as consoles. /// This platform opens an on-screen keyboard using the KeyboardInput class on mobile devices. diff --git a/MLEM/Misc/RectangleF.cs b/MLEM/Misc/RectangleF.cs index 6a3145b..8473f3a 100644 --- a/MLEM/Misc/RectangleF.cs +++ b/MLEM/Misc/RectangleF.cs @@ -105,12 +105,12 @@ namespace MLEM.Misc { this.Height = size.Y; } - /// + /// public bool Contains(float x, float y) { return this.X <= x && x < this.X + this.Width && this.Y <= y && y < this.Y + this.Height; } - /// + /// public bool Contains(Vector2 value) { return this.Contains(value.X, value.Y); } @@ -140,7 +140,7 @@ namespace MLEM.Misc { return (((17 * 23 + this.X.GetHashCode()) * 23 + this.Y.GetHashCode()) * 23 + this.Width.GetHashCode()) * 23 + this.Height.GetHashCode(); } - /// + /// public void Inflate(float horizontalAmount, float verticalAmount) { this.X -= horizontalAmount; this.Y -= verticalAmount; @@ -153,13 +153,13 @@ namespace MLEM.Misc { return value.Left < this.Right && this.Left < value.Right && value.Top < this.Bottom && this.Top < value.Bottom; } - /// + /// public void Offset(float offsetX, float offsetY) { this.X += offsetX; this.Y += offsetY; } - /// + /// public void Offset(Vector2 amount) { this.X += amount.X; this.Y += amount.Y; @@ -173,7 +173,7 @@ namespace MLEM.Misc { /// The squared distance between the two rectangles. public float DistanceSquared(RectangleF value) { // we calculate the distance based on the quadrants that the other rectangle is in using 8 cases: - // 1 7 4 + // 1 7 4 // 3 T 6 // 2 8 5 var valueIsAbove = value.Bottom < this.Top; @@ -215,7 +215,13 @@ namespace MLEM.Misc { return "{X:" + this.X + " Y:" + this.Y + " Width:" + this.Width + " Height:" + this.Height + "}"; } - /// + /// + /// Deconstruction method for . + /// + /// + /// + /// + /// public void Deconstruct(out float x, out float y, out float width, out float height) { x = this.X; y = this.Y; diff --git a/MLEM/Textures/NinePatch.cs b/MLEM/Textures/NinePatch.cs index 30078e7..4de6384 100644 --- a/MLEM/Textures/NinePatch.cs +++ b/MLEM/Textures/NinePatch.cs @@ -158,7 +158,8 @@ namespace MLEM.Textures { for (var x = 0F; x < rect.Width; x += width) { for (var y = 0F; y < rect.Height; y += height) { var size = new Vector2(Math.Min(rect.Width - x, width), Math.Min(rect.Height - y, height)); - batch.Draw(texture.Region.Texture, new RectangleF(rect.Location + new Vector2(x, y), size), new Rectangle(src.Location, (size / patchScale).CeilCopy().ToPoint()), color, rotation, origin, effects, layerDepth); + var srcSize = (size / patchScale).CeilCopy().ToPoint(); + batch.Draw(texture.Region.Texture, new RectangleF(rect.Location + new Vector2(x, y), size), new Rectangle(src.X, src.Y, srcSize.X, srcSize.Y), color, rotation, origin, effects, layerDepth); } } break; diff --git a/MLEM/Textures/TextureRegion.cs b/MLEM/Textures/TextureRegion.cs index 6ed56d9..dc117bb 100644 --- a/MLEM/Textures/TextureRegion.cs +++ b/MLEM/Textures/TextureRegion.cs @@ -33,7 +33,7 @@ namespace MLEM.Textures { /// /// The size of this texture region /// - public Point Size => this.Area.Size; + public Point Size => new Point(this.Area.Width, this.Area.Height); /// /// The width of this texture region /// @@ -51,8 +51,8 @@ namespace MLEM.Textures { /// The of this texture region, but in absolute pixels rather than percentage. /// public Vector2 PivotPixels { - get => this.Pivot * this.Size.ToVector2(); - set => this.Pivot = value / this.Size.ToVector2(); + get => this.Pivot * new Vector2(this.Size.X, this.Size.Y); + set => this.Pivot = value / new Vector2(this.Size.X, this.Size.Y); } /// /// The name of this texture region. By default, this name is . @@ -91,14 +91,14 @@ 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.X, uv.Y, size.X, size.Y)) {} /// /// 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) {} + public TextureRegion(TextureRegion region, Rectangle area) : this(region, area.Location, new Point(area.Width, area.Height)) {} /// /// Creates a new texture region which is a sub-region of the given texture region diff --git a/MLEM/Textures/UniformTextureAtlas.cs b/MLEM/Textures/UniformTextureAtlas.cs index 01d414f..9162033 100644 --- a/MLEM/Textures/UniformTextureAtlas.cs +++ b/MLEM/Textures/UniformTextureAtlas.cs @@ -47,7 +47,7 @@ namespace MLEM.Textures { /// Returns the at this texture atlas' given region position /// /// The region's x and y location - public TextureRegion this[Point point] => this[new Rectangle(point, new Point(1, 1))]; + public TextureRegion this[Point point] => this[new Rectangle(point.X, point.Y, 1, 1)]; /// public TextureRegion this[int x, int y] => this[new Point(x, y)]; /// diff --git a/README.md b/README.md index 478f135..9637045 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![The MLEM logo](https://raw.githubusercontent.com/Ellpeck/MLEM/main/Media/Banner.png) -**MLEM Library for Extending MonoGame** is an addition to the game framework [MonoGame](https://www.monogame.net/) that provides extension methods, quality of life improvements and additional features like a ui system and easy input handling. +**MLEM Library for Extending MonoGame and FNA** is an addition to the game frameworks [MonoGame](https://www.monogame.net/) and [FNA](https://fna-xna.github.io/) that provides extension methods, quality of life improvements and additional features like a ui system and easy input handling. # What next? - Get it on [NuGet](https://www.nuget.org/packages?q=mlem) diff --git a/Sandbox/Sandbox.csproj b/Sandbox/Sandbox.csproj index f24f47a..ca0fb86 100644 --- a/Sandbox/Sandbox.csproj +++ b/Sandbox/Sandbox.csproj @@ -18,7 +18,7 @@ - + diff --git a/Tests/FNA/FAudio.dll b/Tests/FNA/FAudio.dll new file mode 100644 index 0000000..57c6067 Binary files /dev/null and b/Tests/FNA/FAudio.dll differ diff --git a/Tests/FNA/FNA3D.dll b/Tests/FNA/FNA3D.dll new file mode 100644 index 0000000..fe8024f Binary files /dev/null and b/Tests/FNA/FNA3D.dll differ diff --git a/Tests/FNA/SDL2.dll b/Tests/FNA/SDL2.dll new file mode 100644 index 0000000..7a6c63d Binary files /dev/null and b/Tests/FNA/SDL2.dll differ diff --git a/Tests/FNA/libFAudio.0.dylib b/Tests/FNA/libFAudio.0.dylib new file mode 100644 index 0000000..035020c Binary files /dev/null and b/Tests/FNA/libFAudio.0.dylib differ diff --git a/Tests/FNA/libFAudio.so.0 b/Tests/FNA/libFAudio.so.0 new file mode 100644 index 0000000..5b3b116 Binary files /dev/null and b/Tests/FNA/libFAudio.so.0 differ diff --git a/Tests/FNA/libFNA3D.0.dylib b/Tests/FNA/libFNA3D.0.dylib new file mode 100644 index 0000000..17b80fb Binary files /dev/null and b/Tests/FNA/libFNA3D.0.dylib differ diff --git a/Tests/FNA/libFNA3D.so.0 b/Tests/FNA/libFNA3D.so.0 new file mode 100644 index 0000000..b1c1986 Binary files /dev/null and b/Tests/FNA/libFNA3D.so.0 differ diff --git a/Tests/FNA/libMoltenVK.dylib b/Tests/FNA/libMoltenVK.dylib new file mode 100644 index 0000000..9cf069d Binary files /dev/null and b/Tests/FNA/libMoltenVK.dylib differ diff --git a/Tests/FNA/libSDL2-2.0.0.dylib b/Tests/FNA/libSDL2-2.0.0.dylib new file mode 100644 index 0000000..71d8bec Binary files /dev/null and b/Tests/FNA/libSDL2-2.0.0.dylib differ diff --git a/Tests/FNA/libSDL2-2.0.so.0 b/Tests/FNA/libSDL2-2.0.so.0 new file mode 100644 index 0000000..55d47d4 Binary files /dev/null and b/Tests/FNA/libSDL2-2.0.so.0 differ diff --git a/Tests/FNA/libtheorafile.dll b/Tests/FNA/libtheorafile.dll new file mode 100644 index 0000000..a9b2929 Binary files /dev/null and b/Tests/FNA/libtheorafile.dll differ diff --git a/Tests/FNA/libtheorafile.dylib b/Tests/FNA/libtheorafile.dylib new file mode 100644 index 0000000..49116fb Binary files /dev/null and b/Tests/FNA/libtheorafile.dylib differ diff --git a/Tests/FNA/libtheorafile.so b/Tests/FNA/libtheorafile.so new file mode 100644 index 0000000..687e841 Binary files /dev/null and b/Tests/FNA/libtheorafile.so differ diff --git a/Tests/FNA/libvulkan.1.dylib b/Tests/FNA/libvulkan.1.dylib new file mode 100644 index 0000000..ff138d4 Binary files /dev/null and b/Tests/FNA/libvulkan.1.dylib differ diff --git a/Tests/Tests.FNA.csproj b/Tests/Tests.FNA.csproj new file mode 100644 index 0000000..8bb56c8 --- /dev/null +++ b/Tests/Tests.FNA.csproj @@ -0,0 +1,38 @@ + + + net5.0 + nunit + Tests + $(DefineConstants);FNA + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + PreserveNewest + + + PreserveNewest + %(Filename)%(Extension) + + + diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 4a00718..fcf5c37 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -13,14 +13,14 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + diff --git a/build.cake b/build.cake index 469b29b..21207e3 100644 --- a/build.cake +++ b/build.cake @@ -9,13 +9,13 @@ var config = Argument("configuration", "Release"); Task("Prepare").Does(() => { DotNetCoreRestore("MLEM.sln"); - + if (branch != "release") { var buildNum = EnvironmentVariable("BUILD_NUMBER"); if (buildNum != null) version += "-" + buildNum; } - + DeleteFiles("**/*.nupkg"); }); @@ -27,13 +27,16 @@ Task("Build").IsDependentOn("Prepare").Does(() =>{ foreach (var project in GetFiles("**/MLEM*.csproj")) DotNetCoreBuild(project.FullPath, settings); DotNetCoreBuild("Demos/Demos.csproj", settings); + DotNetCoreBuild("Demos/Demos.FNA.csproj", settings); }); Task("Test").IsDependentOn("Build").Does(() => { - DotNetCoreTest("Tests/Tests.csproj", new DotNetCoreTestSettings { + var settings = new DotNetCoreTestSettings { Configuration = config, Collectors = {"XPlat Code Coverage"} - }); + }; + DotNetCoreTest("Tests/Tests.csproj", settings); + DotNetCoreTest("Tests/Tests.FNA.csproj", settings); }); Task("Pack").IsDependentOn("Test").Does(() => { @@ -71,4 +74,4 @@ Task("Document").Does(() => { Task("Default").IsDependentOn("Pack"); Task("Publish").IsDependentOn("Push"); -RunTarget(target); \ No newline at end of file +RunTarget(target);