diff --git a/.github/workflows/build_test_and_publish.yml b/.github/workflows/build_test_and_publish.yml index 956ce7d4..30dfa352 100644 --- a/.github/workflows/build_test_and_publish.yml +++ b/.github/workflows/build_test_and_publish.yml @@ -52,7 +52,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: extract tag version - run: echo ::set-env name=TAG_VERSION::${GITHUB_REF#refs/tags/v*} + run: echo "TAG_VERSION=${GITHUB_REF#refs/tags/v*}" >> $GITHUB_ENV shell: bash - uses: actions/setup-dotnet@v1 with: @@ -78,7 +78,7 @@ jobs: steps: - uses: actions/checkout@v2 - name: extract tag version - run: echo ::set-env name=TAG_VERSION::${GITHUB_REF#refs/tags/v*} + run: echo "TAG_VERSION=${GITHUB_REF#refs/tags/v*}" >> $GITHUB_ENV shell: bash - uses: actions/setup-dotnet@v1 with: diff --git a/Exomia.Framework.sln b/Exomia.Framework.sln index ec18fa6a..bebcb7c0 100644 --- a/Exomia.Framework.sln +++ b/Exomia.Framework.sln @@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exomia.Framework.Example.Ju EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exomia.Framework.BasicSetup", "examples\Exomia.Framework.BasicSetup\Exomia.Framework.BasicSetup.csproj", "{8329DE95-55FA-4176-91BF-BBC793B9EBE2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Exomia.Framework.Example.Canvas", "examples\Exomia.Framework.Example.Canvas\Exomia.Framework.Example.Canvas.csproj", "{BEB473A8-DF7F-4402-97C2-0344308EE17F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -108,6 +110,18 @@ Global {8329DE95-55FA-4176-91BF-BBC793B9EBE2}.Release|x64.Build.0 = Release|x64 {8329DE95-55FA-4176-91BF-BBC793B9EBE2}.Release|x86.ActiveCfg = Release|x86 {8329DE95-55FA-4176-91BF-BBC793B9EBE2}.Release|x86.Build.0 = Release|x86 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Debug|x64.ActiveCfg = Debug|x64 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Debug|x64.Build.0 = Debug|x64 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Debug|x86.ActiveCfg = Debug|x86 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Debug|x86.Build.0 = Debug|x86 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Release|Any CPU.Build.0 = Release|Any CPU + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Release|x64.ActiveCfg = Release|x64 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Release|x64.Build.0 = Release|x64 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Release|x86.ActiveCfg = Release|x86 + {BEB473A8-DF7F-4402-97C2-0344308EE17F}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -118,6 +132,7 @@ Global {E86DC9EB-8FC0-4C5D-95F2-206C67A069C2} = {65A2B0A0-B984-4A33-AFA5-A5E70862A10A} {22EC1807-99FD-4EC1-8A86-9553CD743498} = {9D20989E-7C37-42F4-AE05-19CED958EA7A} {8329DE95-55FA-4176-91BF-BBC793B9EBE2} = {9D20989E-7C37-42F4-AE05-19CED958EA7A} + {BEB473A8-DF7F-4402-97C2-0344308EE17F} = {9D20989E-7C37-42F4-AE05-19CED958EA7A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E4E52C8-D1D4-488D-8700-A3E45E8968BE} diff --git a/examples/Exomia.Framework.BasicSetup/MyGame.cs b/examples/Exomia.Framework.BasicSetup/MyGame.cs index aa962c4d..d89e4868 100644 --- a/examples/Exomia.Framework.BasicSetup/MyGame.cs +++ b/examples/Exomia.Framework.BasicSetup/MyGame.cs @@ -57,6 +57,7 @@ protected override void OnInitializeGameGraphicsParameters(ref GameGraphicsParam protected override void OnInitialize() { Content.RootDirectory = "Content"; + _spriteBatch = ToDispose(new SpriteBatch(GraphicsDevice)); /* * TODO: Add your initialization logic here @@ -67,8 +68,6 @@ protected override void OnInitialize() /// OnLoadContent will be called once per game and is the place to load all of your content. protected override void OnLoadContent() { - _spriteBatch = ToDispose(new SpriteBatch(GraphicsDevice)); - /* * TODO: use base.Content to load your game content here */ diff --git a/examples/Exomia.Framework.Example.Canvas/Content/logo1.jpg b/examples/Exomia.Framework.Example.Canvas/Content/logo1.jpg new file mode 100644 index 00000000..45dde99b Binary files /dev/null and b/examples/Exomia.Framework.Example.Canvas/Content/logo1.jpg differ diff --git a/examples/Exomia.Framework.Example.Canvas/Content/logo2.png b/examples/Exomia.Framework.Example.Canvas/Content/logo2.png new file mode 100644 index 00000000..e0d843a7 Binary files /dev/null and b/examples/Exomia.Framework.Example.Canvas/Content/logo2.png differ diff --git a/examples/Exomia.Framework.Example.Canvas/Exomia.Framework.Example.Canvas.csproj b/examples/Exomia.Framework.Example.Canvas/Exomia.Framework.Example.Canvas.csproj new file mode 100644 index 00000000..633e5ff7 --- /dev/null +++ b/examples/Exomia.Framework.Example.Canvas/Exomia.Framework.Example.Canvas.csproj @@ -0,0 +1,61 @@ + + + Exe + netcoreapp3.1 + AnyCPU;x64;x86 + 8.0 + enable + true + + + + 1.0.0 + baetz + + Copyright © $([System.DateTime]::Now.Year) baetz + + + + portable + true + DEBUG;TRACE;x86 + DEBUG;TRACE;$(Platform) + true + + + + none + false + TRACE;x86 + TRACE;$(Platform) + + + + 1701;1702;IDE0063 + NU1605 + false + + + + $(MSBuildProjectName) + $(MSBuildProjectName) + + + + $(MSBuildProjectName).$(Platform) + $(MSBuildProjectName).$(Platform) + + + + + + + + + PreserveNewest + + + PreserveNewest + + + \ No newline at end of file diff --git a/examples/Exomia.Framework.Example.Canvas/MyGame.cs b/examples/Exomia.Framework.Example.Canvas/MyGame.cs new file mode 100644 index 00000000..4e718373 --- /dev/null +++ b/examples/Exomia.Framework.Example.Canvas/MyGame.cs @@ -0,0 +1,281 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using Exomia.Framework.Components; +using Exomia.Framework.Game; +using Exomia.Framework.Graphics; +using Exomia.Framework.Mathematics; +using Exomia.Framework.Resources; +using SharpDX; + +namespace Exomia.Framework.Example.Canvas +{ + /// + /// my game. This class cannot be inherited. + /// + sealed class MyGame : Game.Game + { +#pragma warning disable IDE0052 // Remove unread private members + private SpriteBatch _spriteBatch = null!; +#pragma warning restore IDE0052 // Remove unread private members + + private Graphics.Canvas _canvas = null!; + + private Texture _texture = null!; + private Texture _texture2 = null!; + private SpriteFont _spriteFont1_12Px = null!; + private SpriteFont _spriteFont1_24Px = null!; + + private float k; + + private readonly Vector2[] polyA = + { + new Vector2(550, 250), new Vector2(550, 120), new Vector2(650, 320), new Vector2(520, 250) + }; + + private readonly Vector2[] polyB = + { + new Vector2(450, 400), new Vector2(700, 420), new Vector2(700, 450), new Vector2(600, 480), + new Vector2(600, 600) + }; + + private readonly Vector2[] polyC = + { + new Vector2(400, 600), new Vector2(550, 620), new Vector2(650, 820), new Vector2(520, 750) + }; + + private readonly Vector2[] polyD = + { + new Vector2(600, 600), new Vector2(650, 610), new Vector2(650, 720), new Vector2(450, 750), + new Vector2(400, 630) + }; + + /// + /// Initializes a new instance of the class. + /// + public MyGame() + : base("MyGame") + { + Add( + new DebugComponent + { + Enabled = true, + Visible = true, + UpdateOrder = 0, + DrawOrder = 0, + EnableTitleInformation = false, + }); + + IsFixedTimeStep = false; + TargetElapsedTime = 1000f / 144f; //144fps + } + + /// + protected override void OnInitializeGameGraphicsParameters(ref GameGraphicsParameters parameters) + { + parameters.IsMouseVisible = true; + parameters.Width = 1600; + parameters.Height = 1024; + parameters.EnableMultiSampling = true; + parameters.MultiSampleCount = MultiSampleCount.MsaaX8; + } + + /// + /// This is where you can query for any required services and load any non-graphic related content. + protected override void OnInitialize() + { + Content.RootDirectory = "Content"; + _spriteBatch = ToDispose(new SpriteBatch(GraphicsDevice)); + _canvas = ToDispose(new Graphics.Canvas(GraphicsDevice)); + + /* + * TODO: Add your initialization logic here + */ + } + + /// + /// OnLoadContent will be called once per game and is the place to load all of your content. + protected override void OnLoadContent() + { + /* + * TODO: use base.Content to load your game content here + */ + + _texture = Content.Load("logo1.jpg"); + _texture2 = Content.Load("logo2.png"); + + _spriteFont1_12Px = Content.Load(Fonts.ARIAL_12_PX, true); + _spriteFont1_24Px = Content.Load(Fonts.ARIAL_24_PX, true); + } + + /// + /// OnUnloadContent will be called once per game and is the place to unload all content + protected override void OnUnloadContent() + { + /* + * TODO: Unload any non ContentManager content here + */ + } + + /// + /// Allows the game to run logic such as updating the world, checking for collisions, gathering input, and playing audio. + protected override void Update(GameTime gameTime) + { + /* + * TODO: Add your update logic here + */ + + base.Update(gameTime); + } + + /// + /// This is called when the game should draw itself. + protected override void Draw(GameTime gameTime) + { + GraphicsDevice.Clear(Color.CornflowerBlue); + + _canvas.Begin(rasterizerState: GraphicsDevice.RasterizerStates.CullBackDepthClipOffMultiSampleOn); + + k += gameTime.DeltaTimeS / 2; + + _canvas.DrawFillTriangle(new Triangle2(100, 250, 150, 300, 50, 300), Color.Red, 0, Vector2.Zero, 1.0f); + _canvas.DrawTriangle(new Triangle2(100, 250, 150, 300, 50, 300), Color.Green, 5.0f, 0, Vector2.Zero, 1.0f); + + _canvas.DrawFillTriangle( + new Triangle2(100, 250, 150, 300, 50, 300), Color.Yellow, k, new Vector2(100, 250), 1.0f); + _canvas.DrawTriangle( + new Triangle2(100, 250, 150, 300, 50, 300), Color.Green, 5.0f, k, new Vector2(100, 250), 1.0f); + + _canvas.DrawFillRectangle(new RectangleF(200, 200, 100, 50), Color.Red, 0, Vector2.Zero, 1.0f); + _canvas.DrawRectangle(new RectangleF(200, 200, 100, 50), Color.Green, 5.0f, 0, Vector2.Zero, 1.0f); + + _canvas.DrawFillRectangle(new RectangleF(200, 200, 100, 50), Color.Yellow, k, new Vector2(200, 200), 1.0f); + _canvas.DrawRectangle(new RectangleF(200, 200, 100, 50), Color.Green, 5.0f, k, new Vector2(200, 200), 1.0f); + + _canvas.DrawLine(new Line2(300, 400, 345, 400), Color.Red, 5.0f, 1.0f, 0, Vector2.Zero); + _canvas.DrawLine(new Line2(400, 400, 450, 450), Color.Red, 5.0f, 1.0f, k, new Vector2(425, 425)); + + _canvas.DrawLine(new Line2(300, 450, 345, 450), Color.Yellow, 5.0f, 1.0f, 0, Vector2.Zero); + _canvas.DrawLine(new Line2(400, 450, 450, 400), Color.Yellow, 5.0f, 1.0f, k, new Vector2(425, 425)); + + _canvas.DrawFillPolygon(polyA, Color.Red, 0.0f, Vector2.Zero, 1.0f); + _canvas.DrawPolygon(polyA, Color.Green, 5.0f, 0.0f, Vector2.Zero, 1.0f); + + _canvas.DrawFillPolygon(polyB, Color.Red, 0.0f, Vector2.Zero, 1.0f); + _canvas.DrawPolygon(polyB, Color.Green, 5.0f, 0.0f, Vector2.Zero, 1.0f); + + _canvas.DrawFillPolygon(polyC, Color.Yellow, k, new Vector2(400, 600), 1.0f); + _canvas.DrawPolygon(polyC, Color.Green, 5.0f, k, new Vector2(400, 600), 1.0f); + + _canvas.DrawFillPolygon(polyD, Color.Red, 0.0f, Vector2.Zero, 1.0f); + _canvas.DrawPolygon(polyD, Color.Yellow, 5.0f, 0.0f, Vector2.Zero, 1.0f); + + Vector2 center = new Vector2(800, 400); + + _canvas.DrawRectangle( + new RectangleF(center.X - 100, center.Y - 100, 200, 200), Color.Green, 2, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc(new Arc2(center, 100f), Color.Black, 0, center, 1.0f); + + float l = 20; + float r = 100; + _canvas.DrawArc( + new Arc2(center, r, MathUtil.DegreesToRadians(90), MathUtil.DegreesToRadians(-45)), Color.Yellow, l, + 0, Vector2.Zero, 1.0f); + + float rr = (r - l) * 0.685f; + _canvas.DrawRectangle( + new RectangleF(center.X - rr, center.Y - rr, rr * 2, rr * 2), Color.Green, 2, 0, Vector2.Zero, 1.0f); + + Vector2 center2 = new Vector2(1200, 400); + _canvas.DrawRectangle( + new RectangleF(center2.X - 50f, center2.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center2, 50f, MathUtil.DegreesToRadians(80), MathUtil.DegreesToRadians(200)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + + Vector2 center21 = new Vector2(1300, 400); + _canvas.DrawRectangle( + new RectangleF(center21.X - 50f, center21.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center21, 50f, MathUtil.DegreesToRadians(80), MathUtil.DegreesToRadians(-10)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + + Vector2 center3 = new Vector2(1200, 500); + _canvas.DrawRectangle( + new RectangleF(center3.X - 50f, center3.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center3, 50f, MathUtil.DegreesToRadians(80), 0), Color.Blue, 0.0f, Vector2.Zero, 1.0f); + + Vector2 center31 = new Vector2(1300, 500); + _canvas.DrawRectangle( + new RectangleF(center31.X - 50f, center31.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center31, 50f, MathUtil.DegreesToRadians(-80), MathUtil.DegreesToRadians(200)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + + Vector2 center4 = new Vector2(1200, 600); + _canvas.DrawRectangle( + new RectangleF(center4.X - 50f, center4.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center4, 50f, MathUtil.DegreesToRadians(-80), MathUtil.DegreesToRadians(-200)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + + Vector2 center41 = new Vector2(1300, 600); + _canvas.DrawRectangle( + new RectangleF(center41.X - 50f, center41.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center41, 50f, MathUtil.DegreesToRadians(-80), MathUtil.DegreesToRadians(0)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + + Vector2 center5 = new Vector2(1200, 700); + _canvas.DrawRectangle( + new RectangleF(center5.X - 50f, center5.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center5, 50f, MathUtil.DegreesToRadians(0), MathUtil.DegreesToRadians(200)), Color.Blue, 0.0f, + Vector2.Zero, 1.0f); + + Vector2 center51 = new Vector2(1300, 700); + _canvas.DrawRectangle( + new RectangleF(center51.X - 50f, center51.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center51, 50f, MathUtil.DegreesToRadians(0), MathUtil.DegreesToRadians(-200)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + + Vector2 center200 = new Vector2(1400, 400); + _canvas.DrawRectangle( + new RectangleF(center200.X - 50f, center200.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, + 1.0f); + _canvas.DrawFillArc( + new Arc2(center200, 50f, MathUtil.DegreesToRadians(80), MathUtil.DegreesToRadians(10)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + _canvas.DrawFillArc( + new Arc2(center200, 50f, MathUtil.DegreesToRadians(80) + k, MathUtil.DegreesToRadians(10) + k), + Color.DeepPink, 0.0f, center200, 1.0f); + + Vector2 center400 = new Vector2(1400, 500); + _canvas.DrawRectangle( + new RectangleF(center400.X - 50f, center400.Y - 50f, 100, 100), Color.Green, 2.0f, 0, Vector2.Zero, + 1.0f); + _canvas.DrawFillArc( + new Arc2(center400, 50f, MathUtil.DegreesToRadians(-200), MathUtil.DegreesToRadians(-80)), Color.Blue, + 0.0f, Vector2.Zero, 1.0f); + + _canvas.Draw(_texture, new RectangleF(1100, 50, 200, 200), Color.White); + _canvas.Draw(_texture2, new RectangleF(1350, 50, 200, 200), Color.White); + + _canvas.DrawText(_spriteFont1_12Px, "This is the canvas example.", new Vector2(450, 50), Color.Black, 0); + _canvas.DrawText(_spriteFont1_24Px, "This is the canvas example.", new Vector2(450, 65), Color.Black, k, new Vector2(450, 65), 1.0f, TextureEffects.None); + + _canvas.End(); + + base.Draw(gameTime); + } + } +} \ No newline at end of file diff --git a/examples/Exomia.Framework.Example.Canvas/Program.cs b/examples/Exomia.Framework.Example.Canvas/Program.cs new file mode 100644 index 00000000..cb968fc4 --- /dev/null +++ b/examples/Exomia.Framework.Example.Canvas/Program.cs @@ -0,0 +1,29 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +namespace Exomia.Framework.Example.Canvas +{ + /// + /// A program. This class cannot be inherited. + /// + sealed class Program + { + /// + /// Main entry-point for this application. + /// + private static void Main() + { + using (MyGame game = new MyGame()) + { + game.Run(); + } + } + } +} \ No newline at end of file diff --git a/examples/Exomia.Framework.Example.JumpAndRun/Components/InputComponent.cs b/examples/Exomia.Framework.Example.JumpAndRun/Components/InputComponent.cs index 38f2ca92..a0503c12 100644 --- a/examples/Exomia.Framework.Example.JumpAndRun/Components/InputComponent.cs +++ b/examples/Exomia.Framework.Example.JumpAndRun/Components/InputComponent.cs @@ -1,12 +1,22 @@ -using Exomia.ECS.Attributes; +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using Exomia.ECS.Attributes; namespace Exomia.Framework.Example.JumpAndRun.Components { [EntityComponentConfiguration(PoolSize = 8)] sealed class InputComponent { - public bool Left { get; set; } + public bool Left { get; set; } public bool Right { get; set; } - public bool Jump { get; set; } + public bool Jump { get; set; } } -} +} \ No newline at end of file diff --git a/examples/Exomia.Framework.Example.JumpAndRun/Scenes/GameScene.cs b/examples/Exomia.Framework.Example.JumpAndRun/Scenes/GameScene.cs index ab86f9d1..87007be1 100644 --- a/examples/Exomia.Framework.Example.JumpAndRun/Scenes/GameScene.cs +++ b/examples/Exomia.Framework.Example.JumpAndRun/Scenes/GameScene.cs @@ -28,7 +28,7 @@ sealed class GameScene : SceneBase, IInputHandler private EntityManager _entityManager = null!; // ReSharper disable once NotAccessedField.Local - private Entity _player = null!; + private Entity _player = null!; private InputComponent _inputComponent = null!; /// @@ -38,7 +38,21 @@ public GameScene(string key) _mapRenderer = Add( new MapRenderer("mapRenderer") { DrawOrder = 1, Visible = true }); } - + + /// + public void RegisterInput(IInputDevice device) + { + device.RegisterKeyDown(OnKeyDown); + device.RegisterKeyUp(OnKeyUp); + } + + /// + public void UnregisterInput(IInputDevice device) + { + device.UnregisterKeyUp(OnKeyDown); + device.UnregisterKeyUp(OnKeyUp); + } + /// protected override void OnInitialize(IServiceRegistry registry) { @@ -156,20 +170,6 @@ protected override void OnLoadContent(IServiceRegistry registry) }); } - /// - public void RegisterInput(IInputDevice device) - { - device.RegisterKeyDown(OnKeyDown); - device.RegisterKeyUp(OnKeyUp); - } - - /// - public void UnregisterInput(IInputDevice device) - { - device.UnregisterKeyUp(OnKeyDown); - device.UnregisterKeyUp(OnKeyUp); - } - private bool OnKeyDown(int keyValue, KeyModifier modifiers) { switch (keyValue) diff --git a/examples/Exomia.Framework.Example.JumpAndRun/Systems/CollisionSystem.cs b/examples/Exomia.Framework.Example.JumpAndRun/Systems/CollisionSystem.cs index 17061738..1a492f53 100644 --- a/examples/Exomia.Framework.Example.JumpAndRun/Systems/CollisionSystem.cs +++ b/examples/Exomia.Framework.Example.JumpAndRun/Systems/CollisionSystem.cs @@ -22,24 +22,24 @@ interface ICollisionSystem { MapRenderer MapRenderer { get; set; } } - + [EntitySystemConfiguration( nameof(CollisionSystem), EntitySystemType.Update, After = new[] { nameof(PhysicSystem) })] sealed class CollisionSystem : EntitySystemBaseR2, ICollisionSystem { private MapRenderer _mapRenderer = null!; - + /// public MapRenderer MapRenderer { get { return _mapRenderer; } set { _mapRenderer = value; } } - + /// public CollisionSystem(EntityManager manager) : base(manager) { } - + /// protected override void Tick(GameTime gameTime, Entity entity, PositionComponent c1, VelocityComponent c2) { diff --git a/examples/Exomia.Framework.Example.JumpAndRun/Systems/InputSystem.cs b/examples/Exomia.Framework.Example.JumpAndRun/Systems/InputSystem.cs index b9cb821e..4d640a44 100644 --- a/examples/Exomia.Framework.Example.JumpAndRun/Systems/InputSystem.cs +++ b/examples/Exomia.Framework.Example.JumpAndRun/Systems/InputSystem.cs @@ -1,4 +1,14 @@ -using Exomia.ECS; +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using Exomia.ECS; using Exomia.ECS.Attributes; using Exomia.ECS.Systems; using Exomia.Framework.Example.JumpAndRun.Components; @@ -19,7 +29,7 @@ protected override void Tick(GameTime gameTime, Entity entity, InputComponent c1 { if (c1.Jump) { - c1.Jump = false; + c1.Jump = false; c2.Velocity.Y -= 600; } diff --git a/framework.wiki b/framework.wiki index ed40697d..89bb7be5 160000 --- a/framework.wiki +++ b/framework.wiki @@ -1 +1 @@ -Subproject commit ed40697d3ea5107efe55efd3599d2f5749ea0838 +Subproject commit 89bb7be513e702d434f66ae6236a47694d3df750 diff --git a/src/Exomia.Framework/Collections/LinkedList.cs b/src/Exomia.Framework/Collections/LinkedList.cs index 16506264..189807be 100644 --- a/src/Exomia.Framework/Collections/LinkedList.cs +++ b/src/Exomia.Framework/Collections/LinkedList.cs @@ -276,6 +276,15 @@ internal LinkedListNode(in T value) Item = value; } + /// + /// Invalidates this object. + /// + internal void Invalidate() + { + Next = null; + Previous = null; + } + /// /// Implicit cast that converts the given LinkedListNode to a T. /// @@ -287,15 +296,6 @@ public static implicit operator T(LinkedListNode node) { return node.Item; } - - /// - /// Invalidates this object. - /// - internal void Invalidate() - { - Next = null; - Previous = null; - } } /// diff --git a/src/Exomia.Framework/Components/DebugComponent.cs b/src/Exomia.Framework/Components/DebugComponent.cs index 8e67a159..a34e5c90 100644 --- a/src/Exomia.Framework/Components/DebugComponent.cs +++ b/src/Exomia.Framework/Components/DebugComponent.cs @@ -38,7 +38,6 @@ public class DebugComponent : DrawableComponent _sampleBuffer; private IGameWindow? _gameWindow; - private Vector2 _position1; private int _sampleCount, _totalFrames; private SpriteBatch? _spriteBatch; private string _title = string.Empty; @@ -71,13 +70,18 @@ public override void Draw(GameTime gameTime) if (EnableTitleInformation) { - _gameWindow!.Title = _title + " " + _fpsInfo; + _gameWindow!.Title = $"{_title} {_fpsInfo}"; } _spriteBatch!.Begin(); _spriteBatch.DrawText( - _arial12Px!, _fpsInfo, _position1, _fpsCurrent <= FRAME_DANGER_THRESHOLD ? Color.Red : Color.White, + _arial12Px!, + _fpsInfo, + Vector2.Zero, + _fpsCurrent <= FRAME_DANGER_THRESHOLD + ? Color.Red + : Color.White, 0.0f); _spriteBatch.End(); @@ -136,9 +140,7 @@ protected override void OnInitialize(IServiceRegistry registry) IGraphicsDevice graphicsDevice = registry.GetService(); _spriteBatch = new SpriteBatch(graphicsDevice); - _position1 = new Vector2(0, 0); - - _gpuName = graphicsDevice.Adapter.Desc3.Description; + _gpuName = graphicsDevice.Adapter?.Desc3.Description ?? ""; } /// diff --git a/src/Exomia.Framework/Content/ContentManager.cs b/src/Exomia.Framework/Content/ContentManager.cs index 0d7c156e..b4fc52d3 100644 --- a/src/Exomia.Framework/Content/ContentManager.cs +++ b/src/Exomia.Framework/Content/ContentManager.cs @@ -257,24 +257,12 @@ public object Load(Type assetType, string assetName, bool fromEmbeddedResource = } } - /// - public object Load(Type assetType, FileInfo assetFileInfo) - { - return Load(assetType, assetFileInfo.FullName); - } - /// public T Load(string assetName, bool fromEmbeddedResource = false) { return (T)Load(typeof(T), assetName, fromEmbeddedResource); } - /// - public T Load(FileInfo assetFileInfo) - { - return (T)Load(typeof(T), assetFileInfo.FullName); - } - /// public void Unload() { @@ -344,12 +332,6 @@ public bool Unload(string assetName) return Unload(typeof(T), assetName); } - /// - public bool Unload(FileInfo assetFileInfo) - { - return Unload(typeof(T), assetFileInfo.FullName); - } - /// /// Resolve stream. /// @@ -479,6 +461,7 @@ private object LoadAssetWithDynamicContentReader(Type assetType, string assetNam { lock (_registeredContentReaderFactories) { + // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator foreach (IContentReaderFactory factory in _registeredContentReaderFactories) { if (factory.TryCreate(assetType, out contentReader)) @@ -512,10 +495,7 @@ private object LoadAssetWithDynamicContentReader(Type assetType, string assetNam } } - /// - /// An asset key. - /// - private struct AssetKey : IEquatable + private readonly struct AssetKey : IEquatable { /// /// Initializes a new instance of the class. @@ -528,14 +508,7 @@ public AssetKey(Type assetType, string assetName) _assetName = assetName; } - /// - /// Type of the asset. - /// - private readonly Type _assetType; - - /// - /// Name of the asset. - /// + private readonly Type _assetType; private readonly string _assetName; /// @@ -548,7 +521,6 @@ public bool Equals(AssetKey other) /// public override bool Equals(object obj) { - if (obj is null) { return false; } return obj is AssetKey key && Equals(key); } diff --git a/src/Exomia.Framework/Content/ContentReaderParameters.cs b/src/Exomia.Framework/Content/ContentReaderParameters.cs index efae4863..bfb41fb2 100644 --- a/src/Exomia.Framework/Content/ContentReaderParameters.cs +++ b/src/Exomia.Framework/Content/ContentReaderParameters.cs @@ -14,17 +14,19 @@ namespace Exomia.Framework.Content { /// - /// A content reader parameters. + /// The content reader parameters. /// public struct ContentReaderParameters { /// - /// Name of the asset currently loaded when using . + /// Name of the asset currently loaded when using + /// . /// public string AssetName { get; } /// - /// Type of the asset currently loaded when using . + /// Type of the asset currently loaded when using + /// . /// public Type AssetType { get; } diff --git a/src/Exomia.Framework/Content/IContentManager.cs b/src/Exomia.Framework/Content/IContentManager.cs index ab73c456..a514cf85 100644 --- a/src/Exomia.Framework/Content/IContentManager.cs +++ b/src/Exomia.Framework/Content/IContentManager.cs @@ -9,7 +9,6 @@ #endregion using System; -using System.IO; using Exomia.Framework.Content.Resolver; using Exomia.Framework.Content.Resolver.EmbeddedResource; @@ -107,24 +106,6 @@ public interface IContentManager : IDisposable /// T Load(string assetName, bool fromEmbeddedResource = false); - /// - /// Loads an asset that has been processed by the content pipeline. - /// - /// Generic type parameter. - /// Information describing the asset file. - /// - /// A asset of type . - /// - /// - /// If the asset was not found from all - /// . - /// - /// - /// If no content reader was suitable to - /// decode the asset. - /// - T Load(FileInfo assetFileInfo); - /// /// Loads an asset that has been processed by the Content Pipeline. /// @@ -149,16 +130,6 @@ public interface IContentManager : IDisposable /// object Load(Type assetType, string assetName, bool fromEmbeddedResource = false); - /// - /// Loads an asset that has been processed by the Content Pipeline. - /// - /// Asset Type. - /// Information describing the asset file. - /// - /// An asset of type . - /// - object Load(Type assetType, FileInfo assetFileInfo); - /// /// Unloads all data that was loaded by this ContentManager. All data will be disposed. /// @@ -178,16 +149,6 @@ public interface IContentManager : IDisposable /// bool Unload(string assetName); - /// - /// Unloads and disposes an asset. - /// - /// Generic type parameter. - /// Information describing the asset file. - /// - /// true if the asset exists and was unloaded, false otherwise. - /// - bool Unload(FileInfo assetFileInfo); - /// /// Unloads and disposes an asset. /// diff --git a/src/Exomia.Framework/Content/Loader/ObjFileLoader.cs b/src/Exomia.Framework/Content/Loader/ObjFileLoader.cs index bc698a4e..7ec110b2 100644 --- a/src/Exomia.Framework/Content/Loader/ObjFileLoader.cs +++ b/src/Exomia.Framework/Content/Loader/ObjFileLoader.cs @@ -49,7 +49,7 @@ public Obj Load(Stream stream) { while (!sr.EndOfStream) { - string currentLine = sr.ReadLine(); + string? currentLine = sr.ReadLine(); if (string.IsNullOrWhiteSpace(currentLine) || currentLine[0] == '#') { diff --git a/src/Exomia.Framework/ContentSerialization/Compression/CompressMode.cs b/src/Exomia.Framework/ContentSerialization/Compression/CompressMode.cs index 277c8db9..eed2361b 100644 --- a/src/Exomia.Framework/ContentSerialization/Compression/CompressMode.cs +++ b/src/Exomia.Framework/ContentSerialization/Compression/CompressMode.cs @@ -11,12 +11,12 @@ namespace Exomia.Framework.ContentSerialization.Compression { /// - /// CompressMode. + /// Values that represent CompressMode. /// public enum CompressMode : byte { /// - /// gzip (default) + /// An enum constant representing the gzip option (default). /// Gzip = 1 } diff --git a/src/Exomia.Framework/ContentSerialization/Compression/ContentCompressor.cs b/src/Exomia.Framework/ContentSerialization/Compression/ContentCompressor.cs index 2856cf9a..79f9c2de 100644 --- a/src/Exomia.Framework/ContentSerialization/Compression/ContentCompressor.cs +++ b/src/Exomia.Framework/ContentSerialization/Compression/ContentCompressor.cs @@ -16,12 +16,12 @@ namespace Exomia.Framework.ContentSerialization.Compression { /// - /// ContentCompressor class. + /// A content compressor. /// public static class ContentCompressor { /// - /// the default compressed e1 extension. + /// The default compressed e1 extension. /// public const string DEFAULT_COMPRESSED_EXTENSION = ".e1"; @@ -29,7 +29,7 @@ public static class ContentCompressor private static readonly byte[] s_magicHeader = { 64, 101, 120, 49 }; /// - /// compress a given stream with the given compression mode. + /// Compress a given stream with the given compression mode. /// /// the stream to compress. /// [out] than finished the compressed out stream. @@ -71,7 +71,7 @@ public static bool CompressStream(Stream stream, } /// - /// decompress a given stream with the given compression mode. + /// Decompress a given stream with the given compression mode. /// /// the stream to compress. /// [out] than finished the decompressed out stream. @@ -115,11 +115,6 @@ public static bool DecompressStream(Stream stream, out Stream streamOut) #region GZIP - /// - /// Gzip compress. - /// - /// the stream to compress. - /// [out] than finished the compressed out stream. private static void GzipCompress(Stream stream, Stream streamOut) { using (GZipStream gs = new GZipStream(streamOut, CompressionLevel.Optimal, true)) @@ -134,11 +129,6 @@ private static void GzipCompress(Stream stream, Stream streamOut) streamOut.Position = 0; } - /// - /// Gzip decompress. - /// - /// the stream to compress. - /// [out] than finished the compressed out stream. private static void GzipDecompress(Stream stream, out Stream streamOut) { streamOut = new MemoryStream(); diff --git a/src/Exomia.Framework/ContentSerialization/ContentSerializableAttribute.cs b/src/Exomia.Framework/ContentSerialization/ContentSerializableAttribute.cs index 210331c5..8115289a 100644 --- a/src/Exomia.Framework/ContentSerialization/ContentSerializableAttribute.cs +++ b/src/Exomia.Framework/ContentSerialization/ContentSerializableAttribute.cs @@ -13,7 +13,7 @@ namespace Exomia.Framework.ContentSerialization { /// - /// used to mark a content serializable class. + /// Used to mark a content serializable class. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public sealed class ContentSerializableAttribute : Attribute @@ -34,9 +34,8 @@ public sealed class ContentSerializableAttribute : Attribute /// internal IContentSerializationWriter Writer { get; } - /// /// - /// ContentSerializableAttribute constructor. + /// Initializes a new instance of the class. /// /// /// the content reader type @@ -46,6 +45,7 @@ public sealed class ContentSerializableAttribute : Attribute /// the content writer type /// /// + /// Thrown when a Type Load error condition occurs. public ContentSerializableAttribute(Type reader, Type writer) { Reader = System.Activator.CreateInstance(reader) as IContentSerializationReader ?? diff --git a/src/Exomia.Framework/ContentSerialization/ContentSerializationContext.cs b/src/Exomia.Framework/ContentSerialization/ContentSerializationContext.cs index 3686277c..bb3d9478 100644 --- a/src/Exomia.Framework/ContentSerialization/ContentSerializationContext.cs +++ b/src/Exomia.Framework/ContentSerialization/ContentSerializationContext.cs @@ -15,9 +15,9 @@ namespace Exomia.Framework.ContentSerialization { /// - /// ContentSerializationContext class + /// A content serialization context. This class cannot be inherited. /// - public class ContentSerializationContext + public sealed class ContentSerializationContext { internal Dictionary Content { get; } = new Dictionary(); @@ -72,24 +72,13 @@ internal void Set(string key, object obj, Type type) } } - /// - /// A content serialization context value. - /// struct ContentSerializationContextValue { - /// - /// The type. - /// - public Type Type; - - /// - /// The object. - /// + public Type Type; public object Object; /// - /// Initializes a new instance of the - /// struct. + /// Initializes a new instance of the struct. /// /// The type. /// The object. diff --git a/src/Exomia.Framework/ContentSerialization/ContentSerializer.cs b/src/Exomia.Framework/ContentSerialization/ContentSerializer.cs index 2b915c7f..16954bbc 100644 --- a/src/Exomia.Framework/ContentSerialization/ContentSerializer.cs +++ b/src/Exomia.Framework/ContentSerialization/ContentSerializer.cs @@ -18,6 +18,10 @@ using Exomia.Framework.ContentSerialization.Writers; using SharpDX; +#if NETSTANDARD2_0 +using Exomia.Framework.Extensions; +#endif + namespace Exomia.Framework.ContentSerialization { /// @@ -117,8 +121,7 @@ static ContentSerializer() { if (a.FullName.StartsWith("System") || a.FullName.StartsWith("SharpDX") || - a.FullName.StartsWith("ms") || - a.FullName.StartsWith("Xilium.CefGlue")) { continue; } + a.FullName.StartsWith("ms")) { continue; } foreach (Type t in a.GetTypes()) { @@ -135,28 +138,28 @@ static ContentSerializer() #region SharpDX - AddWriter(new Vector3CW()); - AddWriter(new Vector2CW()); - AddWriter(new ColorCW()); - AddWriter(new RectangleCW()); - AddWriter(new RectangleFCW()); + AddWriter(new Vector3ContentSerializationWriter()); + AddWriter(new Vector2ContentSerializationWriter()); + AddWriter(new ColorContentSerializationWriter()); + AddWriter(new RectangleContentSerializationWriter()); + AddWriter(new RectangleFContentSerializationWriter()); - AddReader(new Vector3CR()); - AddReader(new Vector2CR()); - AddReader(new ColorCR()); - AddReader(new RectangleCR()); - AddReader(new RectangleFCR()); + AddReader(new Vector3ContentSerializationReader()); + AddReader(new Vector2ContentSerializationReader()); + AddReader(new ColorContentSerializationReader()); + AddReader(new RectangleContentSerializationReader()); + AddReader(new RectangleFContentSerializationReader()); #endregion } /// - /// Adds a new content pipeline reader to the content pipeline. + /// Adds a new to the content pipeline. /// - /// the type the reader can read. - /// IContentSerializationReader. - /// ArgumentNullException. - /// CSReaderException. + /// The type the reader can read. + /// The . + /// Thrown when one or more required arguments are null. + /// Thrown when a Create struct Reader error condition occurs. public static void AddReader(Type type, IContentSerializationReader reader) { if (reader == null) { throw new ArgumentNullException(nameof(reader)); } @@ -173,20 +176,20 @@ public static void AddReader(Type type, IContentSerializationReader reader) /// /// Adds a new content pipeline reader to the content pipeline. /// - /// the type the reader can read. - /// IContentSerializationReader. + /// Generic type parameter. + /// The . public static void AddReader(IContentSerializationReader reader) { AddReader(typeof(T), reader); } /// - /// Adds a new content pipeline writer to the content pipeline. + /// Adds a new to the content pipeline. /// - /// the type the writer can write. - /// IContentSerializationWriter. - /// ArgumentNullException. - /// CSWriterException. + /// The type the writer can write. + /// The . + /// Thrown when one or more required arguments are null. + /// Thrown when a Create struct Writer error condition occurs. public static void AddWriter(Type type, IContentSerializationWriter writer) { if (writer == null) { throw new ArgumentNullException(nameof(writer)); } @@ -202,17 +205,13 @@ public static void AddWriter(Type type, IContentSerializationWriter writer) /// /// Adds a new content pipeline writer to the content pipeline. /// - /// the type the writer can write. - /// IContentSerializationWriter. + /// Generic type parameter. + /// The . public static void AddWriter(IContentSerializationWriter writer) { AddWriter(typeof(T), writer); } - /// - /// Adds an assembly. - /// - /// The assembly. private static void AddAssembly(Assembly assembly) { string assemblyName = assembly.GetName().Name; @@ -229,7 +228,7 @@ private static void AddAssembly(Assembly assembly) /// /// Generic type parameter. /// Name of the asset. - /// Object. + /// The object. /// (Optional) True to minify. /// Thrown when one or more required arguments are null. public static void Write(string assetName, T obj, bool minify = false) where T : class @@ -265,13 +264,13 @@ public static void Write(string assetName, T obj, bool minify = false) where /// /// The write handler. /// The tab space. - /// Object. - /// the type the reader can read. + /// The Object. + /// The type the reader can read. /// /// Thrown when a Create struct Writer error condition /// occurs. /// - internal static void Write(Action writeHandler, string tabSpace, object obj, Type type) + internal static void Write(Action writeHandler, string tabSpace, object? obj, Type type) { if (obj == null) { return; } @@ -283,20 +282,20 @@ internal static void Write(Action writeHandler, string tabSpace, ContentSerializationContext context = new ContentSerializationContext(); writer.Write(context, obj); - foreach (KeyValuePair ctxt in context.Content) + foreach ((var key, ContentSerializationContextValue value) in context.Content) { - if (ctxt.Value.Object == null) { continue; } + if (value.Object == null) { continue; } - if (s_types.TryGetValue(ctxt.Value.Type.Name.ToUpper(), out IType it) || - s_types.TryGetValue(ctxt.Value.Type.BaseType.Name.ToUpper(), out it)) + if (s_types.TryGetValue(value.Type.Name.ToUpper(), out IType it) || + s_types.TryGetValue(value.Type.BaseType!.Name.ToUpper(), out it)) { - it.Write(writeHandler, tabSpace, ctxt.Key, ctxt.Value.Object); + it.Write(writeHandler, tabSpace, key, value.Object); } else { - writeHandler(tabSpace, $"[{ctxt.Key}:{ctxt.Value.Type}]"); - Write(writeHandler, tabSpace + TABSPACE, ctxt.Value.Object, ctxt.Value.Type); - writeHandler(tabSpace, $"[/{ctxt.Key}]"); + writeHandler(tabSpace, $"[{key}:{value.Type}]"); + Write(writeHandler, tabSpace + TABSPACE, value.Object, value.Type); + writeHandler(tabSpace, $"[/{key}]"); } } } @@ -306,10 +305,10 @@ internal static void Write(Action writeHandler, string tabSpace, #region ContentReader /// - /// Reads a object from the given stream. + /// Reads an object from the given stream. /// /// Generic type parameter. - /// Stream. + /// The stream. /// (Optional) True to keep open. /// /// A T. @@ -347,11 +346,11 @@ public static T Read(Stream stream, bool keepOpen = false) where T : class /// /// Reads a object from the given stream. /// - /// Stream. - /// the type the reader can read. + /// The stream. + /// The type the reader can read. /// The object key. /// - /// T Object. + /// An object. /// /// /// Thrown when a Create struct Reader error condition @@ -365,9 +364,7 @@ internal static object Read(CSStreamReader stream, Type type, string objKey) } ContentSerializationContext context = new ContentSerializationContext(); - Read(stream, ref context, objKey); - return reader.Read(context); } @@ -404,7 +401,7 @@ private static void Read(CSStreamReader stream, ref ContentSerializationContext if (string.IsNullOrEmpty(genericTypeInfo)) { throw new CSReaderException( - $"ERROR: NO GENERIC TYPE INFO DEFINED -> {baseTypeInfo}"); + $"ERROR: No generic type info defined -> {baseTypeInfo}"); } context.Set( key, it.Read(stream, key, genericTypeInfo, dimensionInfo), it.BaseType); @@ -422,14 +419,15 @@ private static void Read(CSStreamReader stream, ref ContentSerializationContext if ($"/{objKey}" != key) { throw new CSReaderException( - $"ERROR: INVALID ENDTAG DEFINITION! -> {objKey} != {key}"); + $"ERROR: Invalid end tag definition! -> {objKey} != {key}"); } return; } } break; case '/': - throw new CSReaderException($"ERROR: INVALID FILE CONTENT! -> invalid char '{c}'!"); + throw new CSReaderException( + $"ERROR: Invalid file content char '{c}' at ({stream.Line}:{stream.Index})!"); case '\n': case '\r': case '\t': @@ -437,7 +435,7 @@ private static void Read(CSStreamReader stream, ref ContentSerializationContext break; default: Console.WriteLine( - $"WARNING: invalid char '{c}' found near line {stream.Line} -> index {stream.Index}!"); + $"WARNING: invalid char '{c}' found near ({stream.Line}:{stream.Index})!"); break; } } diff --git a/src/Exomia.Framework/ContentSerialization/Readers/ColorCR.cs b/src/Exomia.Framework/ContentSerialization/Readers/ColorContentSerializationReader.cs similarity index 81% rename from src/Exomia.Framework/ContentSerialization/Readers/ColorCR.cs rename to src/Exomia.Framework/ContentSerialization/Readers/ColorContentSerializationReader.cs index 28a1b21f..807719e9 100644 --- a/src/Exomia.Framework/ContentSerialization/Readers/ColorCR.cs +++ b/src/Exomia.Framework/ContentSerialization/Readers/ColorContentSerializationReader.cs @@ -12,10 +12,7 @@ namespace Exomia.Framework.ContentSerialization.Readers { - /// - /// A color carriage return. This class cannot be inherited. - /// - sealed class ColorCR : ContentSerializationReader + sealed class ColorContentSerializationReader : ContentSerializationReader { /// public override Color ReadContext(ContentSerializationContext context) diff --git a/src/Exomia.Framework/ContentSerialization/Readers/RectangleContentSerializationReader.cs b/src/Exomia.Framework/ContentSerialization/Readers/RectangleContentSerializationReader.cs new file mode 100644 index 00000000..7a434a2d --- /dev/null +++ b/src/Exomia.Framework/ContentSerialization/Readers/RectangleContentSerializationReader.cs @@ -0,0 +1,29 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using SharpDX; + +namespace Exomia.Framework.ContentSerialization.Readers +{ + sealed class RectangleContentSerializationReader : ContentSerializationReader + { + /// + public override Rectangle ReadContext(ContentSerializationContext context) + { + return new Rectangle + { + X = context.Get(nameof(Rectangle.X)), + Y = context.Get(nameof(Rectangle.Y)), + Width = context.Get(nameof(Rectangle.Width)), + Height = context.Get(nameof(Rectangle.Height)) + }; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/ContentSerialization/Readers/RectangleFCR.cs b/src/Exomia.Framework/ContentSerialization/Readers/RectangleFCR.cs deleted file mode 100644 index 9ef0ec16..00000000 --- a/src/Exomia.Framework/ContentSerialization/Readers/RectangleFCR.cs +++ /dev/null @@ -1,50 +0,0 @@ -#region License - -// Copyright (c) 2018-2020, exomia -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -#endregion - -using SharpDX; - -namespace Exomia.Framework.ContentSerialization.Readers -{ - /// - /// A rectangle fcr. This class cannot be inherited. - /// - sealed class RectangleFCR : ContentSerializationReader - { - /// - public override RectangleF ReadContext(ContentSerializationContext context) - { - return new RectangleF - { - X = context.Get(nameof(RectangleF.X)), - Y = context.Get(nameof(RectangleF.Y)), - Width = context.Get(nameof(RectangleF.Width)), - Height = context.Get(nameof(RectangleF.Height)) - }; - } - } - - /// - /// A rectangle carriage return. This class cannot be inherited. - /// - sealed class RectangleCR : ContentSerializationReader - { - /// - public override Rectangle ReadContext(ContentSerializationContext context) - { - return new Rectangle - { - X = context.Get(nameof(Rectangle.X)), - Y = context.Get(nameof(Rectangle.Y)), - Width = context.Get(nameof(Rectangle.Width)), - Height = context.Get(nameof(Rectangle.Height)) - }; - } - } -} \ No newline at end of file diff --git a/src/Exomia.Framework/ContentSerialization/Readers/RectangleFContentSerializationReader.cs b/src/Exomia.Framework/ContentSerialization/Readers/RectangleFContentSerializationReader.cs new file mode 100644 index 00000000..b06cb526 --- /dev/null +++ b/src/Exomia.Framework/ContentSerialization/Readers/RectangleFContentSerializationReader.cs @@ -0,0 +1,29 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using SharpDX; + +namespace Exomia.Framework.ContentSerialization.Readers +{ + sealed class RectangleFContentSerializationReader : ContentSerializationReader + { + /// + public override RectangleF ReadContext(ContentSerializationContext context) + { + return new RectangleF + { + X = context.Get(nameof(RectangleF.X)), + Y = context.Get(nameof(RectangleF.Y)), + Width = context.Get(nameof(RectangleF.Width)), + Height = context.Get(nameof(RectangleF.Height)) + }; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/ContentSerialization/Readers/Vector2CR.cs b/src/Exomia.Framework/ContentSerialization/Readers/Vector2ContentSerializationReader.cs similarity index 76% rename from src/Exomia.Framework/ContentSerialization/Readers/Vector2CR.cs rename to src/Exomia.Framework/ContentSerialization/Readers/Vector2ContentSerializationReader.cs index 888231d3..13179d1a 100644 --- a/src/Exomia.Framework/ContentSerialization/Readers/Vector2CR.cs +++ b/src/Exomia.Framework/ContentSerialization/Readers/Vector2ContentSerializationReader.cs @@ -12,10 +12,7 @@ namespace Exomia.Framework.ContentSerialization.Readers { - /// - /// A vector 2 carriage return. This class cannot be inherited. - /// - sealed class Vector2CR : ContentSerializationReader + sealed class Vector2ContentSerializationReader : ContentSerializationReader { /// public override Vector2 ReadContext(ContentSerializationContext context) diff --git a/src/Exomia.Framework/ContentSerialization/Readers/Vector3CR.cs b/src/Exomia.Framework/ContentSerialization/Readers/Vector3ContentSerializationReader.cs similarity index 79% rename from src/Exomia.Framework/ContentSerialization/Readers/Vector3CR.cs rename to src/Exomia.Framework/ContentSerialization/Readers/Vector3ContentSerializationReader.cs index 3511e85f..a07eda88 100644 --- a/src/Exomia.Framework/ContentSerialization/Readers/Vector3CR.cs +++ b/src/Exomia.Framework/ContentSerialization/Readers/Vector3ContentSerializationReader.cs @@ -12,10 +12,7 @@ namespace Exomia.Framework.ContentSerialization.Readers { - /// - /// A vector 3 carriage return. This class cannot be inherited. - /// - sealed class Vector3CR : ContentSerializationReader + sealed class Vector3ContentSerializationReader : ContentSerializationReader { /// public override Vector3 ReadContext(ContentSerializationContext context) diff --git a/src/Exomia.Framework/ContentSerialization/Types/DictionaryType.cs b/src/Exomia.Framework/ContentSerialization/Types/DictionaryType.cs index 91cd9a7f..a04c4be7 100644 --- a/src/Exomia.Framework/ContentSerialization/Types/DictionaryType.cs +++ b/src/Exomia.Framework/ContentSerialization/Types/DictionaryType.cs @@ -142,6 +142,29 @@ public void Write(Action writeHandler, Write(writeHandler, tabSpace, key, (dynamic)content, useTypeInfo); } + /// + /// + /// + /// Type of the key. + /// Type of the value. + /// The write handler. + /// The tab space. + /// The key. + /// The content. + /// (Optional) True to use type information. + private void Write(Action writeHandler, + string tabSpace, + string key, + Dictionary content, + bool useTypeInfo = true) + { + writeHandler( + tabSpace, + $"[{key}:{(useTypeInfo ? CreateTypeInfo(content.GetType()) : string.Empty)}({content.Count})]"); + ForeachDictionaryDimension(writeHandler, tabSpace + ContentSerializer.TABSPACE, content); + writeHandler(tabSpace, $"[/{(useTypeInfo ? key : string.Empty)}]"); + } + #region WriteHelper /// @@ -179,29 +202,6 @@ private static void ForeachDictionaryDimension(Action - /// - /// - /// Type of the key. - /// Type of the value. - /// The write handler. - /// The tab space. - /// The key. - /// The content. - /// (Optional) True to use type information. - private void Write(Action writeHandler, - string tabSpace, - string key, - Dictionary content, - bool useTypeInfo = true) - { - writeHandler( - tabSpace, - $"[{key}:{(useTypeInfo ? CreateTypeInfo(content.GetType()) : string.Empty)}({content.Count})]"); - ForeachDictionaryDimension(writeHandler, tabSpace + ContentSerializer.TABSPACE, content); - writeHandler(tabSpace, $"[/{(useTypeInfo ? key : string.Empty)}]"); - } - #region ReaderHelper /// diff --git a/src/Exomia.Framework/ContentSerialization/Types/ListType.cs b/src/Exomia.Framework/ContentSerialization/Types/ListType.cs index 7d5ecc13..5e2ba99f 100644 --- a/src/Exomia.Framework/ContentSerialization/Types/ListType.cs +++ b/src/Exomia.Framework/ContentSerialization/Types/ListType.cs @@ -125,6 +125,28 @@ public void Write(Action writeHandler, Write(writeHandler, tabSpace, key, (dynamic)content, useTypeInfo); } + /// + /// Writes. + /// + /// Generic type parameter. + /// The write handler. + /// The tab space. + /// The key. + /// The content. + /// (Optional) True to use type information. + private void Write(Action writeHandler, + string tabSpace, + string key, + List content, + bool useTypeInfo = true) + { + writeHandler( + tabSpace, + $"[{key}:{(useTypeInfo ? CreateTypeInfo(content.GetType()) : string.Empty)}({content.Count})]"); + ForeachListDimension(writeHandler, tabSpace + ContentSerializer.TABSPACE, content); + writeHandler(tabSpace, $"[/{(useTypeInfo ? key : string.Empty)}]"); + } + #region WriteHelper /// @@ -155,28 +177,6 @@ private static void ForeachListDimension(Action writeHandler, #endregion - /// - /// Writes. - /// - /// Generic type parameter. - /// The write handler. - /// The tab space. - /// The key. - /// The content. - /// (Optional) True to use type information. - private void Write(Action writeHandler, - string tabSpace, - string key, - List content, - bool useTypeInfo = true) - { - writeHandler( - tabSpace, - $"[{key}:{(useTypeInfo ? CreateTypeInfo(content.GetType()) : string.Empty)}({content.Count})]"); - ForeachListDimension(writeHandler, tabSpace + ContentSerializer.TABSPACE, content); - writeHandler(tabSpace, $"[/{(useTypeInfo ? key : string.Empty)}]"); - } - #region ReaderHelper /// diff --git a/src/Exomia.Framework/ContentSerialization/Writers/ColorCW.cs b/src/Exomia.Framework/ContentSerialization/Writers/ColorContentSerializationWriter.cs similarity index 80% rename from src/Exomia.Framework/ContentSerialization/Writers/ColorCW.cs rename to src/Exomia.Framework/ContentSerialization/Writers/ColorContentSerializationWriter.cs index be18a7c7..8d2df092 100644 --- a/src/Exomia.Framework/ContentSerialization/Writers/ColorCW.cs +++ b/src/Exomia.Framework/ContentSerialization/Writers/ColorContentSerializationWriter.cs @@ -12,10 +12,7 @@ namespace Exomia.Framework.ContentSerialization.Writers { - /// - /// A color cw. This class cannot be inherited. - /// - sealed class ColorCW : ContentSerializationWriter + sealed class ColorContentSerializationWriter : ContentSerializationWriter { /// public override void WriteContext(ContentSerializationContext context, Color obj) diff --git a/src/Exomia.Framework/ContentSerialization/Writers/RectangleContentSerializationWriter.cs b/src/Exomia.Framework/ContentSerialization/Writers/RectangleContentSerializationWriter.cs new file mode 100644 index 00000000..45f6b66f --- /dev/null +++ b/src/Exomia.Framework/ContentSerialization/Writers/RectangleContentSerializationWriter.cs @@ -0,0 +1,26 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using SharpDX; + +namespace Exomia.Framework.ContentSerialization.Writers +{ + sealed class RectangleContentSerializationWriter : ContentSerializationWriter + { + /// + public override void WriteContext(ContentSerializationContext context, Rectangle obj) + { + context.Set(nameof(Rectangle.X), obj.X); + context.Set(nameof(Rectangle.Y), obj.Y); + context.Set(nameof(Rectangle.Width), obj.Width); + context.Set(nameof(Rectangle.Height), obj.Height); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/ContentSerialization/Writers/RectangleFCW.cs b/src/Exomia.Framework/ContentSerialization/Writers/RectangleFCW.cs deleted file mode 100644 index 461cf87d..00000000 --- a/src/Exomia.Framework/ContentSerialization/Writers/RectangleFCW.cs +++ /dev/null @@ -1,44 +0,0 @@ -#region License - -// Copyright (c) 2018-2020, exomia -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -#endregion - -using SharpDX; - -namespace Exomia.Framework.ContentSerialization.Writers -{ - /// - /// A rectangle fcw. This class cannot be inherited. - /// - sealed class RectangleFCW : ContentSerializationWriter - { - /// - public override void WriteContext(ContentSerializationContext context, RectangleF obj) - { - context.Set(nameof(RectangleF.X), obj.X); - context.Set(nameof(RectangleF.Y), obj.Y); - context.Set(nameof(RectangleF.Width), obj.Width); - context.Set(nameof(RectangleF.Height), obj.Height); - } - } - - /// - /// A rectangle cw. This class cannot be inherited. - /// - sealed class RectangleCW : ContentSerializationWriter - { - /// - public override void WriteContext(ContentSerializationContext context, Rectangle obj) - { - context.Set(nameof(Rectangle.X), obj.X); - context.Set(nameof(Rectangle.Y), obj.Y); - context.Set(nameof(Rectangle.Width), obj.Width); - context.Set(nameof(Rectangle.Height), obj.Height); - } - } -} \ No newline at end of file diff --git a/src/Exomia.Framework/ContentSerialization/Writers/RectangleFContentSerializationWriter.cs b/src/Exomia.Framework/ContentSerialization/Writers/RectangleFContentSerializationWriter.cs new file mode 100644 index 00000000..4bca32a3 --- /dev/null +++ b/src/Exomia.Framework/ContentSerialization/Writers/RectangleFContentSerializationWriter.cs @@ -0,0 +1,26 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using SharpDX; + +namespace Exomia.Framework.ContentSerialization.Writers +{ + sealed class RectangleFContentSerializationWriter : ContentSerializationWriter + { + /// + public override void WriteContext(ContentSerializationContext context, RectangleF obj) + { + context.Set(nameof(RectangleF.X), obj.X); + context.Set(nameof(RectangleF.Y), obj.Y); + context.Set(nameof(RectangleF.Width), obj.Width); + context.Set(nameof(RectangleF.Height), obj.Height); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/ContentSerialization/Writers/Vector2CW.cs b/src/Exomia.Framework/ContentSerialization/Writers/Vector2ContentSerializationWriter.cs similarity index 77% rename from src/Exomia.Framework/ContentSerialization/Writers/Vector2CW.cs rename to src/Exomia.Framework/ContentSerialization/Writers/Vector2ContentSerializationWriter.cs index 040c151e..748ea654 100644 --- a/src/Exomia.Framework/ContentSerialization/Writers/Vector2CW.cs +++ b/src/Exomia.Framework/ContentSerialization/Writers/Vector2ContentSerializationWriter.cs @@ -12,10 +12,7 @@ namespace Exomia.Framework.ContentSerialization.Writers { - /// - /// A vector 2 cw. This class cannot be inherited. - /// - sealed class Vector2CW : ContentSerializationWriter + sealed class Vector2ContentSerializationWriter : ContentSerializationWriter { /// public override void WriteContext(ContentSerializationContext context, Vector2 obj) diff --git a/src/Exomia.Framework/ContentSerialization/Writers/Vector3CW.cs b/src/Exomia.Framework/ContentSerialization/Writers/Vector3ContentSerializationWriter.cs similarity index 79% rename from src/Exomia.Framework/ContentSerialization/Writers/Vector3CW.cs rename to src/Exomia.Framework/ContentSerialization/Writers/Vector3ContentSerializationWriter.cs index 214e2cc9..1c20f06c 100644 --- a/src/Exomia.Framework/ContentSerialization/Writers/Vector3CW.cs +++ b/src/Exomia.Framework/ContentSerialization/Writers/Vector3ContentSerializationWriter.cs @@ -12,10 +12,7 @@ namespace Exomia.Framework.ContentSerialization.Writers { - /// - /// A vector 3 cw. This class cannot be inherited. - /// - sealed class Vector3CW : ContentSerializationWriter + sealed class Vector3ContentSerializationWriter : ContentSerializationWriter { /// public override void WriteContext(ContentSerializationContext context, Vector3 obj) diff --git a/src/Exomia.Framework/DrawableComparer.cs b/src/Exomia.Framework/DrawableComparer.cs index 1e6d1dcd..82420035 100644 --- a/src/Exomia.Framework/DrawableComparer.cs +++ b/src/Exomia.Framework/DrawableComparer.cs @@ -30,16 +30,6 @@ public int Compare(IDrawable left, IDrawable right) return 0; } - if (left == null) - { - return 1; - } - - if (right == null) - { - return -1; - } - return left.DrawOrder < right.DrawOrder ? 1 : -1; } } diff --git a/src/Exomia.Framework/Exomia.Framework.csproj b/src/Exomia.Framework/Exomia.Framework.csproj index dd0dc96a..94b6dfea 100644 --- a/src/Exomia.Framework/Exomia.Framework.csproj +++ b/src/Exomia.Framework/Exomia.Framework.csproj @@ -8,10 +8,10 @@ true enable true + TRACE;$(Platform) - 1.4.1 exomia exomia;saika01 a framework for building 2D and 3D games and more inspired by the XNA/Mono framework @@ -42,16 +42,13 @@ portable true - DEBUG;TRACE;x86 - DEBUG;TRACE;$(Platform) true + $(DefineConstants);DEBUG none false - TRACE;x86 - TRACE;$(Platform) @@ -65,7 +62,7 @@ - 1701;1702;IDE0063 + 1701;1702;IDE0063;IDE0079 NU1605 false logo_x192_Hcn_icon.ico @@ -103,6 +100,7 @@ + @@ -151,6 +149,7 @@ $(MSBuildProjectName).Resources.fonts.arial.arial_38px.e1 + @@ -162,25 +161,53 @@ - - - - - - - - - - - + + + + + + + + + + + - + RenderForm.cs + + + + + Math2.cs + + + + + + + SpriteBatch.cs + + + + + + + Canvas.cs + + + + + + + SpriteFont.cs + + diff --git a/src/Exomia.Framework/Extensions/DeconstructExtensions.cs b/src/Exomia.Framework/Extensions/DeconstructExtensions.cs new file mode 100644 index 00000000..1bd06e80 --- /dev/null +++ b/src/Exomia.Framework/Extensions/DeconstructExtensions.cs @@ -0,0 +1,37 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +#if NETSTANDARD2_0 +using System.Collections.Generic; + +namespace Exomia.Framework.Extensions +{ + + /// + /// A deconstruct extensions. + /// + public static class DeconstructExtensions + { + /// + /// A type deconstruct-or that extracts the individual members from this object. + /// + /// Type of the key. + /// Type of the value. + /// The kvp. + /// [out] The key. + /// [out] The value. + public static void Deconstruct(this KeyValuePair kvp, out TKey key, out TValue value) + { + key = kvp.Key; + value = kvp.Value; + } + } +} +#endif \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Camera/Camera3D.cs b/src/Exomia.Framework/Game/Camera/Camera3D.cs similarity index 99% rename from src/Exomia.Framework/Graphics/Camera/Camera3D.cs rename to src/Exomia.Framework/Game/Camera/Camera3D.cs index 198c97c1..86abbc5b 100644 --- a/src/Exomia.Framework/Graphics/Camera/Camera3D.cs +++ b/src/Exomia.Framework/Game/Camera/Camera3D.cs @@ -10,11 +10,10 @@ using System; using System.Collections.Generic; -using Exomia.Framework.Game; using Exomia.Framework.Input; using SharpDX; -namespace Exomia.Framework.Graphics.Camera +namespace Exomia.Framework.Game.Camera { /// /// A camera base. diff --git a/src/Exomia.Framework/Graphics/Camera/Controller/RotationController.cs b/src/Exomia.Framework/Game/Camera/Controller/RotationController.cs similarity index 95% rename from src/Exomia.Framework/Graphics/Camera/Controller/RotationController.cs rename to src/Exomia.Framework/Game/Camera/Controller/RotationController.cs index fd40f04b..7a05858d 100644 --- a/src/Exomia.Framework/Graphics/Camera/Controller/RotationController.cs +++ b/src/Exomia.Framework/Game/Camera/Controller/RotationController.cs @@ -10,12 +10,11 @@ using System; using System.Threading; -using Exomia.Framework.Game; using Exomia.Framework.Input; using Exomia.Framework.Mathematics; using SharpDX; -namespace Exomia.Framework.Graphics.Camera.Controller +namespace Exomia.Framework.Game.Camera.Controller { /// /// A controller for handling rotations. This class cannot be inherited. @@ -100,14 +99,14 @@ void IInputHandler.UnregisterInput(IInputDevice device) device.RegisterRawMouseInput(CameraOnRawMouseInput); } - private bool CameraOnRawMouseInput(in MouseEventArgs mouseEventArgs) + private EventAction CameraOnRawMouseInput(in MouseEventArgs mouseEventArgs) { if (mouseEventArgs.X != 0 || mouseEventArgs.Y != 0) { Interlocked.Add(ref _x, mouseEventArgs.X); Interlocked.Add(ref _y, mouseEventArgs.Y); } - return false; + return EventAction.Continue; } } } \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Camera/Controller/TranslationKeyboardController.cs b/src/Exomia.Framework/Game/Camera/Controller/TranslationKeyboardController.cs similarity index 91% rename from src/Exomia.Framework/Graphics/Camera/Controller/TranslationKeyboardController.cs rename to src/Exomia.Framework/Game/Camera/Controller/TranslationKeyboardController.cs index 1731984a..70c4b04d 100644 --- a/src/Exomia.Framework/Graphics/Camera/Controller/TranslationKeyboardController.cs +++ b/src/Exomia.Framework/Game/Camera/Controller/TranslationKeyboardController.cs @@ -9,11 +9,10 @@ #endregion using System.Collections.Generic; -using Exomia.Framework.Game; using Exomia.Framework.Input; using SharpDX; -namespace Exomia.Framework.Graphics.Camera.Controller +namespace Exomia.Framework.Game.Camera.Controller { /// /// A controller for handling translation keyboards. This class cannot be inherited. @@ -100,16 +99,16 @@ void IUpdateableCameraComponent.Update(GameTime gameTime, ICamera camera) } } - private bool CameraOnKeyDown(int keyValue, KeyModifier modifiers) + private EventAction CameraOnKeyDown(int keyValue, KeyModifier modifiers) { _keysDown.Add(keyValue); - return false; + return EventAction.Continue; } - private bool CameraOnKeyUp(int keyValue, KeyModifier modifiers) + private EventAction CameraOnKeyUp(int keyValue, KeyModifier modifiers) { _keysDown.Remove(keyValue); - return false; + return EventAction.Continue; } } } \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Camera/ICamera.cs b/src/Exomia.Framework/Game/Camera/ICamera.cs similarity index 98% rename from src/Exomia.Framework/Graphics/Camera/ICamera.cs rename to src/Exomia.Framework/Game/Camera/ICamera.cs index af4ce15f..c3c0ca4f 100644 --- a/src/Exomia.Framework/Graphics/Camera/ICamera.cs +++ b/src/Exomia.Framework/Game/Camera/ICamera.cs @@ -11,7 +11,7 @@ using Exomia.Framework.Input; using SharpDX; -namespace Exomia.Framework.Graphics.Camera +namespace Exomia.Framework.Game.Camera { /// /// Interface for a camera. diff --git a/src/Exomia.Framework/Graphics/Camera/ICameraComponent.cs b/src/Exomia.Framework/Game/Camera/ICameraComponent.cs similarity index 92% rename from src/Exomia.Framework/Graphics/Camera/ICameraComponent.cs rename to src/Exomia.Framework/Game/Camera/ICameraComponent.cs index fc38f35d..9df7560a 100644 --- a/src/Exomia.Framework/Graphics/Camera/ICameraComponent.cs +++ b/src/Exomia.Framework/Game/Camera/ICameraComponent.cs @@ -8,7 +8,7 @@ #endregion -namespace Exomia.Framework.Graphics.Camera +namespace Exomia.Framework.Game.Camera { /// /// Interface for camera component. diff --git a/src/Exomia.Framework/Graphics/Camera/IDisposableCameraComponent.cs b/src/Exomia.Framework/Game/Camera/IDisposableCameraComponent.cs similarity index 93% rename from src/Exomia.Framework/Graphics/Camera/IDisposableCameraComponent.cs rename to src/Exomia.Framework/Game/Camera/IDisposableCameraComponent.cs index ba602ef3..af411193 100644 --- a/src/Exomia.Framework/Graphics/Camera/IDisposableCameraComponent.cs +++ b/src/Exomia.Framework/Game/Camera/IDisposableCameraComponent.cs @@ -6,7 +6,7 @@ // LICENSE file in the root directory of this source tree. #endregion -namespace Exomia.Framework.Graphics.Camera { +namespace Exomia.Framework.Game.Camera { /// /// Interface for disposable camera component. /// diff --git a/src/Exomia.Framework/Graphics/Camera/IInitializableCameraComponent.cs b/src/Exomia.Framework/Game/Camera/IInitializableCameraComponent.cs similarity index 93% rename from src/Exomia.Framework/Graphics/Camera/IInitializableCameraComponent.cs rename to src/Exomia.Framework/Game/Camera/IInitializableCameraComponent.cs index 4298dba2..6f513c98 100644 --- a/src/Exomia.Framework/Graphics/Camera/IInitializableCameraComponent.cs +++ b/src/Exomia.Framework/Game/Camera/IInitializableCameraComponent.cs @@ -8,7 +8,7 @@ #endregion -namespace Exomia.Framework.Graphics.Camera +namespace Exomia.Framework.Game.Camera { /// /// Interface for initializable camera component. diff --git a/src/Exomia.Framework/Graphics/Camera/IUpdateableCameraComponent.cs b/src/Exomia.Framework/Game/Camera/IUpdateableCameraComponent.cs similarity index 89% rename from src/Exomia.Framework/Graphics/Camera/IUpdateableCameraComponent.cs rename to src/Exomia.Framework/Game/Camera/IUpdateableCameraComponent.cs index d811f666..ad71bbdf 100644 --- a/src/Exomia.Framework/Graphics/Camera/IUpdateableCameraComponent.cs +++ b/src/Exomia.Framework/Game/Camera/IUpdateableCameraComponent.cs @@ -8,9 +8,7 @@ #endregion -using Exomia.Framework.Game; - -namespace Exomia.Framework.Graphics.Camera +namespace Exomia.Framework.Game.Camera { /// /// Interface for updateable camera component. diff --git a/src/Exomia.Framework/Game/Desktop/GamePlatformWindows.cs b/src/Exomia.Framework/Game/Desktop/GamePlatformWindows.cs new file mode 100644 index 00000000..15d6cd4c --- /dev/null +++ b/src/Exomia.Framework/Game/Desktop/GamePlatformWindows.cs @@ -0,0 +1,29 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using Exomia.Framework.Input; + +namespace Exomia.Framework.Game.Desktop +{ + sealed class GamePlatformWindows : GamePlatform + { + /// + public GamePlatformWindows(Game game, string title) + : base(game, title) { } + + /// + private protected override IGameWindow CreateGameWindow(Game game, string title) + { + GameWindowWindows gameWindow = new GameWindowWindows(title); + game.Services.AddService(gameWindow.RenderForm); + return gameWindow; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Game/WinFormsGameWindow.cs b/src/Exomia.Framework/Game/Desktop/GameWindowWindows.cs similarity index 73% rename from src/Exomia.Framework/Game/WinFormsGameWindow.cs rename to src/Exomia.Framework/Game/Desktop/GameWindowWindows.cs index dd8f6145..8b989a2e 100644 --- a/src/Exomia.Framework/Game/WinFormsGameWindow.cs +++ b/src/Exomia.Framework/Game/Desktop/GameWindowWindows.cs @@ -11,59 +11,43 @@ using System; using Exomia.Framework.Win32; -namespace Exomia.Framework.Game +namespace Exomia.Framework.Game.Desktop { /// /// Form for viewing the window forms game. This class cannot be inherited. /// - sealed class WinFormsGameWindow : IWinFormsGameWindow, IGameWindowInitialize + sealed class GameWindowWindows : IGameWindow { - /// - /// Occurs when the form is about to close. - /// + /// public event RefEventHandler FormClosing { add { _renderForm.FormClosing += value; } remove { _renderForm.FormClosing -= value; } } + /// + public event EventHandler FormClosed + { + add { _renderForm.FormClosed += value; } + remove { _renderForm.FormClosed -= value; } + } + private readonly RenderForm _renderForm; private bool _isInitialized; - /// - /// Gets the width. - /// - /// - /// The width. - /// + /// public int Width { get { return _renderForm.Size.X; } } - /// - /// Gets the height. - /// - /// - /// The height. - /// + /// public int Height { get { return _renderForm.Size.Y; } } /// - public RenderForm RenderForm - { - get { return _renderForm; } - } - - /// - /// Gets or sets the title. - /// - /// - /// The title. - /// public string Title { get { return _renderForm.WindowTitle; } @@ -71,40 +55,33 @@ public string Title } /// - /// Gets a value indicating whether this object is initialized. + /// Gets the render form. /// /// - /// True if this object is initialized, false if not. + /// The render form. /// - bool IGameWindowInitialize.IsInitialized + public RenderForm RenderForm { - get { return _isInitialized; } + get { return _renderForm; } } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The title. - public WinFormsGameWindow(string title) + public GameWindowWindows(string title) { _renderForm = new RenderForm(title); } - /// - /// Resizes. - /// - /// The width. - /// The height. + /// public void Resize(int width, int height) { _renderForm.Resize(width, height); } - /// - /// Initializes this object. - /// - /// [in,out] Options for controlling the operation. - void IGameWindowInitialize.Initialize(ref GameGraphicsParameters parameters) + /// + public void Initialize(ref GameGraphicsParameters parameters) { if (_isInitialized) { return; } @@ -166,16 +143,14 @@ void IGameWindowInitialize.Initialize(ref GameGraphicsParameters parameters) _isInitialized = true; } - void IGameWindowInitialize.Show() + /// + public void Show() { _renderForm.Show(); } #region IDisposable Support - /// - /// True if disposed. - /// private bool _disposed; /// @@ -199,9 +174,9 @@ private void Dispose(bool disposing) } /// - /// Finalizes an instance of the class. + /// Finalizes an instance of the class. /// - ~WinFormsGameWindow() + ~GameWindowWindows() { Dispose(false); } diff --git a/src/Exomia.Framework/Game/Game.cs b/src/Exomia.Framework/Game/Game.cs index dcd3270e..dc6d0996 100644 --- a/src/Exomia.Framework/Game/Game.cs +++ b/src/Exomia.Framework/Game/Game.cs @@ -19,9 +19,6 @@ using Exomia.Framework.Tools; using Exomia.Framework.Win32; using SharpDX; -using SharpDX.Direct3D; -using SharpDX.Direct3D11; -using SharpDX.DXGI; namespace Exomia.Framework.Game { @@ -44,10 +41,9 @@ public abstract class Game : IRunnable private readonly List _updateableComponent; private readonly IServiceRegistry _serviceRegistry; private readonly DisposeCollector _collector; - private readonly IGameWindowInitialize _gameWindowInitialize; private readonly IInputDevice _inputDevice; private readonly IContentManager _contentManager; - private readonly IGameWindow _gameWindow; + private readonly GamePlatform _platform; private readonly GraphicsDevice _graphicsDevice; private bool _isRunning, _isInitialized, _isContentLoaded, _shutdown; @@ -81,7 +77,7 @@ public IContentManager Content /// public IGameWindow GameWindow { - get { return _gameWindow; } + get { return _platform.MainWindow; } } /// @@ -126,10 +122,10 @@ public bool IsRunning } /// - /// Gets or sets the target elapsed time. + /// Gets or sets the target elapsed time in ms. /// /// - /// The target elapsed time. + /// The target elapsed time in ms. /// public double TargetElapsedTime { get; set; } = 1000.0 / 60.0; @@ -157,16 +153,14 @@ protected Game(string title = "Exomia.Framework", _collector = new DisposeCollector(); _serviceRegistry = new ServiceRegistry(); - // TODO: use a factory? - WinFormsGameWindow gameWindow = new WinFormsGameWindow(title); - gameWindow.FormClosing += (ref bool cancel) => { Shutdown(); }; - - _gameWindowInitialize = gameWindow; - _serviceRegistry.AddService(_gameWindow = gameWindow); - _serviceRegistry.AddService(_graphicsDevice = new GraphicsDevice()); _serviceRegistry.AddService(_contentManager = new ContentManager(_serviceRegistry)); - _serviceRegistry.AddService(_inputDevice = gameWindow.RenderForm); + + _platform = GamePlatform.Create(this, title); + _serviceRegistry.AddService(_platform.MainWindow); + + // NOTE: The input device should be registered during the platform creation! + _inputDevice = _serviceRegistry.GetService(); } /// @@ -178,12 +172,12 @@ protected Game(string title = "Exomia.Framework", } /// - /// add a new game system. + /// Adds an item to the game. /// /// T. /// item. /// - /// true if successfully added; false otherwise. + /// The item. /// public T Add(T item) { @@ -281,12 +275,12 @@ public T Add(T item) } /// - /// add a new game system. + /// Remove an item from the game. /// - /// T. + /// Generic type parameter. /// item. /// - /// true if successfully added; false otherwise. + /// The item. /// public T Remove(T item) { @@ -343,7 +337,7 @@ public T Remove(T item) } /// - /// get a game system by name. + /// Get a game component by its name. /// /// the game system name. /// [out] out found game system. @@ -364,7 +358,7 @@ public void Run() { if (_isRunning) { - throw new InvalidOperationException("Can't run this instance while it is already running."); + throw new InvalidOperationException("The instance is already running!"); } _isRunning = true; @@ -373,6 +367,9 @@ public void Run() { Initialize(); LoadContent(); + + _platform.ShowMainWindow(); + Renderloop(); UnloadContent(); } @@ -425,6 +422,7 @@ void OnIsRunningChanged(Game s, bool v) } Update(gameTime); + if (BeginFrame()) { Draw(gameTime); @@ -434,9 +432,9 @@ void OnIsRunningChanged(Game s, bool v) if (IsFixedTimeStep) { //SLEEP - while (TargetElapsedTime - stopwatch.Elapsed.TotalMilliseconds > FIXED_TIMESTAMP_THRESHOLD) + while (TargetElapsedTime - FIXED_TIMESTAMP_THRESHOLD > stopwatch.Elapsed.TotalMilliseconds) { - Thread.Sleep(1); + Thread.Yield(); } //IDLE @@ -482,39 +480,11 @@ protected virtual void OnAfterInitialize() { } /// private void InitializeGameGraphicsParameters() { - GameGraphicsParameters parameters = new GameGraphicsParameters - { - BufferCount = 1, -#if DEBUG - DeviceCreationFlags = - DeviceCreationFlags.BgraSupport | - DeviceCreationFlags.Debug, -#else - DeviceCreationFlags = - DeviceCreationFlags.BgraSupport, -#endif - DriverType = DriverType.Hardware, - Format = Format.B8G8R8A8_UNorm, - Width = 1024, - Height = 768, - DisplayType = DisplayType.Window, - IsMouseVisible = false, - Rational = new Rational(60, 1), - SwapChainFlags = SwapChainFlags.AllowModeSwitch, - SwapEffect = SwapEffect.Discard, - Usage = Usage.RenderTargetOutput, - UseVSync = false, - WindowAssociationFlags = WindowAssociationFlags.IgnoreAll, - EnableMultiSampling = false, - MultiSampleCount = MultiSampleCount.None, - AdapterLuid = -1, - OutputIndex = -1, - ClipCursor = false - }; + GameGraphicsParameters parameters = GameGraphicsParameters.Create(IntPtr.Zero); OnInitializeGameGraphicsParameters(ref parameters); - _gameWindowInitialize.Initialize(ref parameters); + _platform.Initialize(ref parameters); _graphicsDevice.Initialize(ref parameters); GameGraphicsParameters = parameters; @@ -544,8 +514,8 @@ private void Initialize() OnInitialize(); InitializePendingInitializations(); _isInitialized = true; + OnAfterInitialize(); - _gameWindowInitialize.Show(); } } @@ -570,7 +540,6 @@ private void LoadContent() { if (!_isContentLoaded) { - _isContentLoaded = true; OnLoadContent(); lock (_contentableComponent) @@ -584,6 +553,8 @@ private void LoadContent() } _currentlyContentableComponent.Clear(); + + _isContentLoaded = true; } } @@ -607,11 +578,12 @@ private void UnloadContent() _currentlyContentableComponent.Clear(); OnUnloadContent(); + _isContentLoaded = false; } } - #endregion Content + #endregion #region Update @@ -789,10 +761,10 @@ public Timer2 AddTimer(float tick, #region IDisposable Support /// - /// adds a IDisposable object to the dispose collector. + /// Adds a object to the dispose collector. /// - /// . - /// . + /// Generic type parameter. + /// The object. /// /// Obj as a T. /// @@ -801,9 +773,6 @@ public T ToDispose(T obj) where T : IDisposable return _collector.Collect(obj); } - /// - /// True if disposed. - /// private bool _disposed; /// @@ -845,9 +814,10 @@ private void Dispose(bool disposing) _gameComponents.Clear(); _pendingInitializables.Clear(); + _platform.Dispose(); + _contentManager.Dispose(); _graphicsDevice.Dispose(); - _gameWindow.Dispose(); } _collector.DisposeAndClear(disposing); diff --git a/src/Exomia.Framework/Game/GameGraphicsParameters.cs b/src/Exomia.Framework/Game/GameGraphicsParameters.cs index 2ba51426..e0aadb7c 100644 --- a/src/Exomia.Framework/Game/GameGraphicsParameters.cs +++ b/src/Exomia.Framework/Game/GameGraphicsParameters.cs @@ -124,5 +124,54 @@ public struct GameGraphicsParameters /// The output index. /// public int OutputIndex; + + /// + /// Creates a new object with default settings. + /// + /// The handle. + /// + /// (Optional) + /// Define the width of the . + /// + /// + /// (Optional) + /// Define the height of the . + /// + /// + /// The . + /// + public static GameGraphicsParameters Create(IntPtr handle, int width = 1024, int height = 768) + { + return new GameGraphicsParameters + { + Handle = handle, + BufferCount = 1, +#if DEBUG + DeviceCreationFlags = + DeviceCreationFlags.BgraSupport | + DeviceCreationFlags.Debug, +#else + DeviceCreationFlags = + DeviceCreationFlags.BgraSupport, +#endif + DriverType = DriverType.Hardware, + Format = Format.B8G8R8A8_UNorm, + Width = width, + Height = height, + DisplayType = DisplayType.Window, + IsMouseVisible = false, + Rational = new Rational(60, 1), + SwapChainFlags = SwapChainFlags.AllowModeSwitch, + SwapEffect = SwapEffect.Discard, + Usage = Usage.RenderTargetOutput, + UseVSync = false, + WindowAssociationFlags = WindowAssociationFlags.IgnoreAll, + EnableMultiSampling = false, + MultiSampleCount = MultiSampleCount.None, + AdapterLuid = -1, + OutputIndex = -1, + ClipCursor = false + }; + } } } \ No newline at end of file diff --git a/src/Exomia.Framework/Game/GamePlatform.cs b/src/Exomia.Framework/Game/GamePlatform.cs new file mode 100644 index 00000000..f2331233 --- /dev/null +++ b/src/Exomia.Framework/Game/GamePlatform.cs @@ -0,0 +1,104 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using Exomia.Framework.Game.Desktop; + +namespace Exomia.Framework.Game +{ + abstract class GamePlatform : IDisposable + { + private readonly IGameWindow _mainWindow; + + public IGameWindow MainWindow + { + get { return _mainWindow; } + } + + protected GamePlatform(Game game, string title) + { + // ReSharper disable once VirtualMemberCallInConstructor + _mainWindow = CreateGameWindow(game, title); + _mainWindow.FormClosed += game.Shutdown; + } + + /// + /// Initializes the . + /// + /// [in,out] Options for controlling the operation. + public void Initialize(ref GameGraphicsParameters parameters) + { + _mainWindow.Initialize(ref parameters); + } + + /// + /// Shows the main window. + /// + public void ShowMainWindow() + { + _mainWindow.Show(); + } + + private protected abstract IGameWindow CreateGameWindow(Game game, string title); + + /// + /// Creates a new . + /// + /// The game. + /// The title. + /// + /// A . + /// + public static GamePlatform Create(Game game, string title) + { + return new GamePlatformWindows(game, title); + } + + #region IDisposable Support + + private bool _disposed; + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + OnDispose(disposing); + if (disposing) + { + _mainWindow.Dispose(); + } + _disposed = true; + } + } + + /// + ~GamePlatform() + { + Dispose(false); + } + + /// + /// called then the instance is disposing + /// + /// true if user code; false called by finalizer + protected virtual void OnDispose(bool disposing) { } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Game/GameTime.cs b/src/Exomia.Framework/Game/GameTime.cs index 4f42fc82..fa9f7655 100644 --- a/src/Exomia.Framework/Game/GameTime.cs +++ b/src/Exomia.Framework/Game/GameTime.cs @@ -99,17 +99,6 @@ private GameTime() _prevTime = _baseTime = Stopwatch.GetTimestamp(); } - /// - /// Starts a new. - /// - /// - /// A GameTime. - /// - public static GameTime StartNew() - { - return new GameTime(); - } - /// /// reset the game time. /// @@ -172,5 +161,16 @@ public void Tick() _prevTime = _currTime; } + + /// + /// Starts a new. + /// + /// + /// A GameTime. + /// + public static GameTime StartNew() + { + return new GameTime(); + } } } \ No newline at end of file diff --git a/src/Exomia.Framework/Game/IGameWindow.cs b/src/Exomia.Framework/Game/IGameWindow.cs index 95ef3a4e..bad7fcb8 100644 --- a/src/Exomia.Framework/Game/IGameWindow.cs +++ b/src/Exomia.Framework/Game/IGameWindow.cs @@ -12,30 +12,8 @@ namespace Exomia.Framework.Game { - interface IGameWindowInitialize - { - /// - /// Gets a value indicating whether this object is initialized. - /// - /// - /// True if this object is initialized, false if not. - /// - bool IsInitialized { get; } - - /// - /// Initializes this object. - /// - /// [in,out] Options for controlling the operation. - void Initialize(ref GameGraphicsParameters parameters); - - /// - /// Shows this object. - /// - void Show(); - } - /// - /// IGameWindow interface. + /// Interface for game window. /// public interface IGameWindow : IDisposable { @@ -45,12 +23,9 @@ public interface IGameWindow : IDisposable event RefEventHandler FormClosing; /// - /// Gets the height. + /// Occurs when the form is closed. /// - /// - /// The height. - /// - int Height { get; } + event EventHandler FormClosed; /// /// Gets or sets the title. @@ -60,6 +35,14 @@ public interface IGameWindow : IDisposable /// string Title { get; set; } + /// + /// Gets the height. + /// + /// + /// The height. + /// + int Height { get; } + /// /// Gets the width. /// @@ -69,10 +52,21 @@ public interface IGameWindow : IDisposable int Width { get; } /// - /// Resizes. + /// Resizes the game window. /// /// The width. /// The height. void Resize(int width, int height); + + /// + /// Initializes this object. + /// + /// [in,out] Options for controlling the operation. + void Initialize(ref GameGraphicsParameters parameters); + + /// + /// Shows the game window. + /// + void Show(); } } \ No newline at end of file diff --git a/src/Exomia.Framework/Game/IWinFormsGameWindow.cs b/src/Exomia.Framework/Game/IWinFormsGameWindow.cs deleted file mode 100644 index 1e61703b..00000000 --- a/src/Exomia.Framework/Game/IWinFormsGameWindow.cs +++ /dev/null @@ -1,26 +0,0 @@ -#region License - -// Copyright (c) 2018-2020, exomia -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -#endregion - -namespace Exomia.Framework.Game -{ - /// - /// IWinFormsGameWindow interface. - /// - public interface IWinFormsGameWindow : IGameWindow - { - /// - /// Gets the render form. - /// - /// - /// The render form. - /// - RenderForm RenderForm { get; } - } -} \ No newline at end of file diff --git a/src/Exomia.Framework/Game/RenderForm.Events.cs b/src/Exomia.Framework/Game/RenderForm.Events.cs index 7ddcbd5c..ef7437dc 100644 --- a/src/Exomia.Framework/Game/RenderForm.Events.cs +++ b/src/Exomia.Framework/Game/RenderForm.Events.cs @@ -46,6 +46,11 @@ sealed partial class RenderForm /// public event RefEventHandler? FormClosing; + /// + /// Occurs when the form is closed. + /// + public event EventHandler? FormClosed; + private unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { Message m; @@ -96,6 +101,7 @@ private unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lPara FormClosing?.Invoke(ref cancel); if (!cancel) { + FormClosed?.Invoke(); User32.DestroyWindow(_hWnd); } return IntPtr.Zero; @@ -161,7 +167,11 @@ private unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lPara MouseButtons mouseButtons = (MouseButtons)LowWord(m.wParam); for (int i = 0; i < _mouseMovePipe.Count; i++) { - if (_mouseMovePipe[i].Invoke(new MouseEventArgs(x, y, mouseButtons, 0, 0))) { break; } + if (_mouseMovePipe[i].Invoke(new MouseEventArgs(x, y, mouseButtons, 0, 0)) == + EventAction.StopPropagation) + { + break; + } } return IntPtr.Zero; } @@ -173,7 +183,8 @@ private unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lPara int wheelDelta = HighWord(m.wParam); for (int i = 0; i < _mouseWheelPipe.Count; i++) { - if (_mouseWheelPipe[i].Invoke(new MouseEventArgs(x, y, mouseButtons, 2, wheelDelta))) + if (_mouseWheelPipe[i].Invoke(new MouseEventArgs(x, y, mouseButtons, 2, wheelDelta)) == + EventAction.StopPropagation) { break; } @@ -191,7 +202,11 @@ private unsafe IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lPara MouseButtons mouseButtons = (MouseButtons)LowWord(m.wParam); for (int i = 0; i < _mouseClickPipe.Count; i++) { - if (_mouseClickPipe[i].Invoke(new MouseEventArgs(x, y, mouseButtons, 2, 0))) { break; } + if (_mouseClickPipe[i].Invoke(new MouseEventArgs(x, y, mouseButtons, 2, 0)) == + EventAction.StopPropagation) + { + break; + } } return IntPtr.Zero; } @@ -204,7 +219,7 @@ private void RawKeyMessage(ref Message m) { for (int i = 0; i < _rawKeyPipe.Count; i++) { - if (_rawKeyPipe[i].Invoke(m)) + if (_rawKeyPipe[i].Invoke(m) == EventAction.StopPropagation) { break; } @@ -230,7 +245,7 @@ private void RawKeyMessage(ref Message m) } for (int i = 0; i < _keyDownPipe.Count; i++) { - if (_keyDownPipe[i].Invoke(vKey, _keyModifier)) + if (_keyDownPipe[i].Invoke(vKey, _keyModifier) == EventAction.StopPropagation) { break; } @@ -252,7 +267,7 @@ private void RawKeyMessage(ref Message m) } for (int i = 0; i < _keyUpPipe.Count; i++) { - if (_keyUpPipe[i].Invoke(vKey, _keyModifier)) + if (_keyUpPipe[i].Invoke(vKey, _keyModifier) == EventAction.StopPropagation) { break; } @@ -262,7 +277,7 @@ private void RawKeyMessage(ref Message m) case WM.CHAR: for (int i = 0; i < _keyPressPipe.Count; i++) { - if (_keyPressPipe[i].Invoke((char)vKey)) + if (_keyPressPipe[i].Invoke((char)vKey) == EventAction.StopPropagation) { break; } @@ -279,7 +294,8 @@ private void RawMouseDown(ref Message m, MouseButtons buttons) int high = HighWord(m.lParam); for (int i = 0; i < _mouseDownPipe.Count; i++) { - if (_mouseDownPipe[i].Invoke(new MouseEventArgs(low, high, buttons, 1, 0))) + if (_mouseDownPipe[i].Invoke(new MouseEventArgs(low, high, buttons, 1, 0)) == + EventAction.StopPropagation) { break; } @@ -296,7 +312,8 @@ private void RawMouseUp(ref Message m, MouseButtons buttons) int clicks = (_state & 0x4000000) == 0x4000000 ? 2 : 1; for (int i = 0; i < _mouseClickPipe.Count; i++) { - if (_mouseClickPipe[i].Invoke(new MouseEventArgs(low, high, buttons, clicks, 0))) + if (_mouseClickPipe[i].Invoke(new MouseEventArgs(low, high, buttons, clicks, 0)) == + EventAction.StopPropagation) { break; } @@ -305,7 +322,10 @@ private void RawMouseUp(ref Message m, MouseButtons buttons) _state &= ~0xC000000; for (int i = 0; i < _mouseUpPipe.Count; i++) { - if (_mouseUpPipe[i].Invoke(new MouseEventArgs(low, high, buttons, 1, 0))) { break; } + if (_mouseUpPipe[i].Invoke(new MouseEventArgs(low, high, buttons, 1, 0)) == EventAction.StopPropagation) + { + break; + } } } @@ -340,7 +360,8 @@ private void RawMouseInput(in RAWINPUTMOUSE e) } for (int i = 0; i < _mouseRawInputPipe.Count; i++) { - if (_mouseRawInputPipe[i].Invoke(new MouseEventArgs(e.LastX, e.LastY, buttons, clicks, e.ButtonData))) + if (_mouseRawInputPipe[i].Invoke(new MouseEventArgs(e.LastX, e.LastY, buttons, clicks, e.ButtonData)) == + EventAction.StopPropagation) { break; } diff --git a/src/Exomia.Framework/Graphics/BlendStates.cs b/src/Exomia.Framework/Graphics/BlendStates.cs new file mode 100644 index 00000000..f0a802d1 --- /dev/null +++ b/src/Exomia.Framework/Graphics/BlendStates.cs @@ -0,0 +1,137 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using SharpDX.Direct3D11; + +namespace Exomia.Framework.Graphics +{ + /// + /// The built-in blend states. This class cannot be inherited. + /// + public sealed class BlendStates : IDisposable + { + /// + /// A built-in state object with settings for additive blend, that is adding the destination data to the source data + /// without using alpha. + /// + public readonly BlendState Additive; + + /// + /// A built-in state object with settings for alpha blend, that is blending the source and destination data using + /// alpha. + /// + public readonly BlendState AlphaBlend; + + /// + /// A built-in state object with settings for blending with non-premultiplied alpha, that is blending source and + /// destination data using alpha while assuming the color data contains no alpha information. + /// + public readonly BlendState NonPremultiplied; + + /// + /// A built-in state object with settings for opaque blend, that is overwriting the source with the destination data. + /// + public readonly BlendState Opaque; + + /// + /// A built-in default state object (no blending). + /// + public readonly BlendState Default; + + /// + /// Initializes a new instance of the class. + /// + /// The graphics device. + internal BlendStates(IGraphicsDevice graphicsDevice) + { + Additive = Create(graphicsDevice.Device, nameof(Additive), BlendOption.SourceAlpha, BlendOption.One, true); + AlphaBlend = Create( + graphicsDevice.Device, nameof(AlphaBlend), BlendOption.One, BlendOption.InverseSourceAlpha, true); + NonPremultiplied = Create( + graphicsDevice.Device, nameof(NonPremultiplied), BlendOption.SourceAlpha, + BlendOption.InverseSourceAlpha, true); + Opaque = Create(graphicsDevice.Device, nameof(Opaque), BlendOption.One, BlendOption.Zero, true); + Default = Create(graphicsDevice.Device, nameof(Default), BlendStateDescription.Default()); + } + + private static BlendState Create(Device5 device, string name, BlendStateDescription description) + { + return new BlendState(device, description) { DebugName = name }; + } + + private static BlendState Create(Device5 device, + string name, + BlendOption sourceBlend, + BlendOption destinationBlend, + bool blendEnabled) + { + return Create( + device, + name, + new BlendStateDescription + { + AlphaToCoverageEnable = false, + IndependentBlendEnable = false, + RenderTarget = + { + [0] = new RenderTargetBlendDescription + { + IsBlendEnabled = blendEnabled, + SourceBlend = sourceBlend, + DestinationBlend = destinationBlend, + SourceAlphaBlend = sourceBlend, + DestinationAlphaBlend = destinationBlend, + BlendOperation = BlendOperation.Add, + AlphaBlendOperation = BlendOperation.Add, + RenderTargetWriteMask = ColorWriteMaskFlags.All + } + } + }); + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + Additive.Dispose(); + AlphaBlend.Dispose(); + NonPremultiplied.Dispose(); + Opaque.Dispose(); + Default.Dispose(); + } + _disposed = true; + } + } + + /// + ~BlendStates() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Buffers/ConstantBuffer.cs b/src/Exomia.Framework/Graphics/Buffers/ConstantBuffer.cs new file mode 100644 index 00000000..7bf4a03a --- /dev/null +++ b/src/Exomia.Framework/Graphics/Buffers/ConstantBuffer.cs @@ -0,0 +1,130 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using Exomia.Framework.Mathematics; +using SharpDX.Direct3D11; +using Buffer = SharpDX.Direct3D11.Buffer; + +namespace Exomia.Framework.Graphics.Buffers +{ + /// + /// A constant buffer. This class cannot be inherited. + /// + public sealed class ConstantBuffer : IDisposable + { + private readonly Buffer _buffer; + + private ConstantBuffer(Buffer buffer) + { + _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } + + /// + /// Creates a new . + /// + /// Generic type parameter. + /// The graphics device. + /// (Optional) The resource usage. + /// (Optional) The CPU access flags. + /// + /// A . + /// + public static unsafe ConstantBuffer Create(IGraphicsDevice graphicsDevice, + ResourceUsage resourceUsage = ResourceUsage.Default, + CpuAccessFlags cpuAccessFlags = CpuAccessFlags.None) + where T : unmanaged + { + return Create(graphicsDevice, sizeof(T), resourceUsage, cpuAccessFlags); + } + + /// + /// Creates a new with the specified size in bytes. + /// + /// The graphics device. + /// The size in bytes. + /// (Optional) The resource usage. + /// (Optional) The CPU access flags. + /// + /// A . + /// + /// + /// The must be a multiple of 16, + /// if not it will be calculated to the minimum multiple of 16 fitting it in. + /// e.g. + /// - a size in bytes of 11 will be 16. + /// - a size in bytes of 23 will be 32. + /// - a size in bytes of 80 will be 80. + /// > see https://docs.microsoft.com/de-de/windows/win32/api/d3d11/ns-d3d11-d3d11_buffer_desc#remarks + /// + public static ConstantBuffer Create(IGraphicsDevice graphicsDevice, + int sizeInBytes, + ResourceUsage resourceUsage = ResourceUsage.Default, + CpuAccessFlags cpuAccessFlags = CpuAccessFlags.None) + { + return new ConstantBuffer( + new Buffer( + graphicsDevice.Device, + Math2.Ceiling(sizeInBytes / 16.0f) * 16, + resourceUsage, + BindFlags.ConstantBuffer, + cpuAccessFlags, + ResourceOptionFlags.None, + 0)); + } + + /// + /// Implicit cast that converts the given to a . + /// + /// Buffer for constant data. + /// + /// The result of the operation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Buffer(ConstantBuffer buffer) + { + return buffer._buffer; + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _buffer.Dispose(); + } + _disposed = true; + } + } + + /// + ~ConstantBuffer() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Buffers/IndexBuffer.cs b/src/Exomia.Framework/Graphics/Buffers/IndexBuffer.cs new file mode 100644 index 00000000..cef484e1 --- /dev/null +++ b/src/Exomia.Framework/Graphics/Buffers/IndexBuffer.cs @@ -0,0 +1,129 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using SharpDX; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using Buffer = SharpDX.Direct3D11.Buffer; + +namespace Exomia.Framework.Graphics.Buffers +{ + /// + /// A index buffer. This class cannot be inherited. + /// + public sealed class IndexBuffer : IDisposable + { + /// + /// Describes the format of the index buffer. + /// + public readonly Format Format; + + private readonly Buffer _buffer; + + private IndexBuffer(Buffer buffer, Format format) + { + _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + Format = format; + } + + /// + /// Creates a new . + /// + /// Generic type parameter. + /// The graphics device. + /// The data. + /// (Optional) The resource usage. + /// (Optional) The CPU access flags. + /// + /// A . + /// + public static unsafe IndexBuffer Create(IGraphicsDevice graphicsDevice, + in T[] data, + ResourceUsage resourceUsage = ResourceUsage.Immutable, + CpuAccessFlags cpuAccessFlags = CpuAccessFlags.None) + where T : unmanaged + + { + using (DataStream dataStream = new DataStream(sizeof(T) * data.Length, true, true)) + { + dataStream.WriteRange(data, 0, data.Length); + dataStream.Position = 0; + + Buffer buffer = new Buffer( + graphicsDevice.Device, + dataStream, + sizeof(T) * data.Length, + resourceUsage, + BindFlags.IndexBuffer, + cpuAccessFlags, + ResourceOptionFlags.None, + 0); + + return new IndexBuffer( + buffer, Type.GetTypeCode(typeof(T)) switch + { + TypeCode.Int16 => Format.R16_SInt, + TypeCode.Int32 => Format.R32_SInt, + TypeCode.UInt16 => Format.R16_UInt, + TypeCode.UInt32 => Format.R32_UInt, + _ => Format.Unknown + }); + } + } + + /// + /// Implicit cast that converts the given to a . + /// + /// Buffer for index data. + /// + /// The result of the operation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Buffer(IndexBuffer buffer) + { + return buffer._buffer; + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _buffer.Dispose(); + } + _disposed = true; + } + } + + /// + ~IndexBuffer() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Buffers/VertexBuffer.cs b/src/Exomia.Framework/Graphics/Buffers/VertexBuffer.cs new file mode 100644 index 00000000..60f85a4b --- /dev/null +++ b/src/Exomia.Framework/Graphics/Buffers/VertexBuffer.cs @@ -0,0 +1,209 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using SharpDX; +using SharpDX.Direct3D11; +using Buffer = SharpDX.Direct3D11.Buffer; + +namespace Exomia.Framework.Graphics.Buffers +{ + /// + /// A vertex buffer. This class cannot be inherited. + /// + public sealed class VertexBuffer : IDisposable + { + private readonly Buffer _buffer; + private readonly VertexBufferBinding _vertexBufferBinding; + + private VertexBuffer(Buffer buffer, int stride) + { + _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + _vertexBufferBinding = new VertexBufferBinding(_buffer, stride, 0); + } + + /// + /// Map the data. + /// + /// Generic type parameter. + /// The context4. + /// The data. + /// (Optional) The map mode. + /// (Optional) The map flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Map(DeviceContext4 context4, + T[] data, + MapMode mapMode = MapMode.WriteDiscard, + MapFlags mapFlags = MapFlags.None) + where T : unmanaged + { + Map(context4, 0, data, 0, data.Length, mapMode, mapFlags); + } + + /// + /// Map the data. + /// + /// Generic type parameter. + /// The context4. + /// The offset. + /// The data. + /// (Optional) The map mode. + /// (Optional) The map flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Map(DeviceContext4 context4, + int offset, + T[] data, + MapMode mapMode = MapMode.WriteDiscard, + MapFlags mapFlags = MapFlags.None) + where T : unmanaged + { + Map(context4, offset, data, 0, data.Length, mapMode, mapFlags); + } + + /// + /// Map the data. + /// + /// Generic type parameter. + /// The context4. + /// The data. + /// The data offset. + /// The data length. + /// (Optional) The map mode. + /// (Optional) The map flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Map(DeviceContext4 context4, + T[] data, + int dataOffset, + int dataLength, + MapMode mapMode = MapMode.WriteDiscard, + MapFlags mapFlags = MapFlags.None) + where T : unmanaged + { + Map(context4, 0, data, dataOffset, dataLength, mapMode, mapFlags); + } + + /// + /// Map the data. + /// + /// Generic type parameter. + /// The context4. + /// The offset. + /// The data. + /// The data offset. + /// The data length. + /// (Optional) The map mode. + /// (Optional) The map flags. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe void Map(DeviceContext4 context4, + int offset, + T[] data, + int dataOffset, + int dataLength, + MapMode mapMode = MapMode.WriteDiscard, + MapFlags mapFlags = MapFlags.None) + where T : unmanaged + { + DataBox box = context4.MapSubresource(_buffer, 0, mapMode, mapFlags); + T* vpctPtr = (T*)box.DataPointer; + for (int i = 0; i < dataLength; i++) + { + *(vpctPtr + offset + i) = data[i + dataOffset]; + } + context4.UnmapSubresource(_buffer, 0); + } + + /// + /// Creates a new . + /// + /// Generic type parameter. + /// The graphics device. + /// The count of the vertices to store in this vertex buffer. + /// (Optional) The resource usage. + /// (Optional) The CPU access flags. + /// + /// A . + /// + public static unsafe VertexBuffer Create(IGraphicsDevice graphicsDevice, + int vertices, + ResourceUsage resourceUsage = ResourceUsage.Dynamic, + CpuAccessFlags cpuAccessFlags = CpuAccessFlags.Write) + where T : unmanaged + { + return new VertexBuffer( + new Buffer( + graphicsDevice.Device, + sizeof(T) * vertices, + resourceUsage, + BindFlags.VertexBuffer, + cpuAccessFlags, + ResourceOptionFlags.None, + 0), + sizeof(T)); + } + + /// + /// Implicit cast that converts the given to a . + /// + /// Buffer for vertex data. + /// + /// The result of the operation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator Buffer(VertexBuffer buffer) + { + return buffer._buffer; + } + + /// + /// Implicit cast that converts the given to a . + /// + /// Buffer for vertex data. + /// + /// The result of the operation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator VertexBufferBinding(VertexBuffer buffer) + { + return buffer._vertexBufferBinding; + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _buffer.Dispose(); + } + _disposed = true; + } + } + + /// + ~VertexBuffer() + { + Dispose(false); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.Arc.cs b/src/Exomia.Framework/Graphics/Canvas.Arc.cs new file mode 100644 index 00000000..e0881c96 --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.Arc.cs @@ -0,0 +1,345 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using Exomia.Framework.Mathematics; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed unsafe partial class Canvas + { + private static readonly Vector2[] + s_arcCornerOffsets = { new Vector2(-1, -1), new Vector2(1, -1), new Vector2(1, 1), new Vector2(-1, 1) }; + + /// + /// Draws an arc. + /// + /// The center. + /// The radius. + /// The start. + /// The end. + /// The color. + /// The width of the line. + /// The rotation. + /// The origin. + /// The opacity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawArc(in Vector2 center, + float radius, + float start, + float end, + in Color color, + float lineWidth, + float rotation, + in Vector2 origin, + float opacity) + { + DrawArc(new Arc2(center, radius, start, end), in color, lineWidth, rotation, in origin, opacity); + } + + /// + /// Draws an arc. + /// + /// The arc. + /// The color. + /// The width of the line. + /// The rotation. + /// The origin. + /// The opacity. + public void DrawArc(in Arc2 arc, + in Color color, + float lineWidth, + float rotation, + in Vector2 origin, + float opacity) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (arc.Start == arc.End) { return; } + + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + float r = arc.Radius; + float rh = (arc.Radius - lineWidth) * 0.685f; + float o = ((uint)(arc.Radius * 10.0f) << 16) | (uint)((arc.Radius - lineWidth) * 10.0f); + + float u = arc.Start; + float v = arc.End; + + if (u > MathUtil.TwoPi) + { + float times = (float)Math.Floor(u / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + else if (u < -MathUtil.TwoPi) + { + float times = (float)Math.Floor((u + MathUtil.TwoPi) / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + + if (v > MathUtil.TwoPi) + { + float times = (float)Math.Floor(v / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + else if (v < -MathUtil.TwoPi) + { + float times = (float)Math.Floor((v + MathUtil.TwoPi) / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + + if (v < u) + { + float t = u; + u = v; + v = t; + } + + if (u < 0 && v < 0) + { + u += MathUtil.TwoPi; + v += MathUtil.TwoPi; + } + + float x; + float y; + if (rotation == 0.0f) + { + x = arc.X; + y = arc.Y; + } + else + { + float cos = (float)Math.Cos(rotation); + float sin = (float)Math.Sin(rotation); + float dx = arc.X - origin.X; + float dy = arc.Y - origin.Y; + x = ((cos * dx) - (sin * dy)) + origin.X; + y = (sin * dx) + (cos * dy) + origin.Y; + } + + Item* ptr = Reserve(4); + DrawArcRect( + ptr + 0, + new Line2(x - r, y - r, x + r, y - r), + new Line2(x - rh, y - rh, x + rh, y - rh), + scaledColor, x, y, u, v, o); + + DrawArcRect( + ptr + 1, + new Line2(x + r, y - r, x + r, y + r), + new Line2(x + rh, y - rh, x + rh, y + rh), + scaledColor, x, y, u, v, o); + + DrawArcRect( + ptr + 2, + new Line2(x + r, y + r, x - r, y + r), + new Line2(x + rh, y + rh, x - rh, y + rh), + scaledColor, x, y, u, v, o); + + DrawArcRect( + ptr + 3, + new Line2(x - r, arc.Y + r, x - r, y - r), + new Line2(x - rh, arc.Y + rh, x - rh, y - rh), + scaledColor, x, y, u, v, o); + } + + /// + /// Draws a filled arc. + /// + /// The center. + /// The radius. + /// The start. + /// The end. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawFillArc(in Vector2 center, + float radius, + float start, + float end, + in Color color, + float rotation, + in Vector2 origin, + float opacity) + { + DrawFillArc(new Arc2(center, radius, start, end), in color, rotation, in origin, opacity); + } + + /// + /// Draws a filled arc. + /// + /// The arc. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + public void DrawFillArc(in Arc2 arc, + in Color color, + float rotation, + in Vector2 origin, + float opacity) + { + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (arc.Start == arc.End) { return; } + + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + float u = arc.Start; + float v = arc.End; + + if (u > MathUtil.TwoPi) + { + float times = (float)Math.Floor(u / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + else if (u < -MathUtil.TwoPi) + { + float times = (float)Math.Floor((u + MathUtil.TwoPi) / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + + if (v > MathUtil.TwoPi) + { + float times = (float)Math.Floor(v / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + else if (v < -MathUtil.TwoPi) + { + float times = (float)Math.Floor((v + MathUtil.TwoPi) / MathUtil.TwoPi); + u -= times * MathUtil.TwoPi; + v -= times * MathUtil.TwoPi; + } + + if (v < u) + { + float t = u; + u = v; + v = t; + } + + if (u < 0 && v < 0) + { + u += MathUtil.TwoPi; + v += MathUtil.TwoPi; + } + + float x; + float y; + if (rotation == 0.0f) + { + x = arc.X; + y = arc.Y; + } + else + { + float cos = (float)Math.Cos(rotation); + float sin = (float)Math.Sin(rotation); + float dx = arc.X - origin.X; + float dy = arc.Y - origin.Y; + x = ((cos * dx) - (sin * dy)) + origin.X; + y = (sin * dx) + (cos * dy) + origin.Y; + } + + // ReSharper disable CompareOfFloatsByEqualityOperator + float m = u == 0.0f && v == MathUtil.TwoPi ? FILL_CIRCLE_MODE : FILL_CIRCLE_ARC_MODE; + + // ReSharper enable CompareOfFloatsByEqualityOperator + + Item* ptr = Reserve(1); + for (int i = 0; i < 4; i++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + i; + + Vector2 corner = s_arcCornerOffsets[i]; + + vertex->X = x + (corner.X * arc.Radius); + vertex->Y = y + (corner.Y * arc.Radius); + vertex->Z = x; + vertex->W = y; + vertex->RGBA = scaledColor; + vertex->U = u; + vertex->V = v; + vertex->M = m; + vertex->O = arc.Radius; + } + } + + private static void DrawArcRect(Item* ptr, + in Line2 lineA, + in Line2 lineB, + in Vector4 c, + float z, + float w, + float u, + float v, + float o) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + float m = u == 0.0f && v == MathUtil.TwoPi ? BORDER_CIRCLE_MODE : BORDER_CIRCLE_ARC_MODE; + + // ReSharper enable CompareOfFloatsByEqualityOperator + + for (int i = 0; i < 2; i++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + i; + fixed (Line2* t = &lineA) + { + Vector2* lf = (Vector2*)t; + vertex->XY = *(lf + i); + } + + vertex->Z = z; + vertex->W = w; + vertex->RGBA = c; + vertex->U = u; + vertex->V = v; + vertex->M = m; + vertex->O = o; + } + + for (int i = 1; i >= 0; i--) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + 2 + (1 - i); + fixed (Line2* t = &lineB) + { + Vector2* lf = (Vector2*)t; + vertex->XY = *(lf + i); + } + + vertex->Z = z; + vertex->W = w; + vertex->RGBA = c; + vertex->U = u; + vertex->V = v; + vertex->M = m; + vertex->O = o; + } + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.Line.cs b/src/Exomia.Framework/Graphics/Canvas.Line.cs new file mode 100644 index 00000000..5cd0e4bf --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.Line.cs @@ -0,0 +1,105 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using Exomia.Framework.Mathematics; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed unsafe partial class Canvas + { + /// + /// Draw a line from to . + /// + /// The first point. + /// The second point. + /// The color. + /// The width of the line. + /// The opacity. + /// The rotation. + /// The origin. + /// (Optional) The length factor. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawLine(in Vector2 point1, + in Vector2 point2, + in Color color, + float lineWidth, + float opacity, + float rotation, + in Vector2 origin, + float lengthFactor = 1.0f) + { + DrawLine(new Line2(in point1, in point2), color, lineWidth, opacity, rotation, origin, lengthFactor); + } + + /// + /// Draw a line. + /// + /// The line. + /// The color. + /// The width of the line. + /// The opacity. + /// The rotation. + /// The origin. + /// (Optional) The length factor. + public void DrawLine(in Line2 line, + in Color color, + float lineWidth, + float opacity, + float rotation, + in Vector2 origin, + float lengthFactor = 1.0f) + { + Line2 l = rotation == 0.0f ? line : Line2.RotateAround(in line, rotation, origin); + + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + float dx = l.X2 - l.X1; + float dy = l.Y2 - l.Y1; + + double dl = Math.Sqrt((dx * dx) + (dy * dy)); + float nx = (float)((dy / dl) * lineWidth); + float ny = (float)((dx / dl) * lineWidth); + + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)Reserve(1); + + // p1 + vertex->XY = l.XY1; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + // p2 + vertex->XY = l.XY2; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + // p2' + vertex->X = l.X2 - nx; + vertex->Y = l.Y2 + ny; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + // p1' + vertex->X = l.X1 - nx; + vertex->Y = l.Y1 + ny; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.Polygon.cs b/src/Exomia.Framework/Graphics/Canvas.Polygon.cs new file mode 100644 index 00000000..21857be5 --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.Polygon.cs @@ -0,0 +1,211 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using Exomia.Framework.Mathematics; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed unsafe partial class Canvas + { + /// + /// Draws a polygon. + /// + /// The vertices. + /// The color. + /// The width of the line. + /// The rotation. + /// The origin. + /// The opacity. + /// Thrown when one or more arguments are outside the required range. + public void DrawPolygon(Vector2[] vertices, + in Color color, + float lineWidth, + float rotation, + in Vector2 origin, + float opacity) + { + if (vertices.Length < 2) { throw new ArgumentOutOfRangeException(nameof(vertices.Length)); } + + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + Item* ptr = Reserve(vertices.Length); + + if (rotation != 0.0f) + { + Vector2[] vs = new Vector2[vertices.Length]; + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + for (int i = 0; i < vertices.Length; i++) + { + ref Vector2 v = ref vertices[i]; + float x = v.X - origin.X; + float y = v.Y - origin.Y; + vs[i] = new Vector2( + (float)(((cos * x) - (sin * y)) + origin.X), (float)((sin * x) + (cos * y) + origin.Y)); + } + vertices = vs; + } + + Line2 previous = Line2.CreateWithPerpendicular( + ref vertices[vertices.Length - 1], ref vertices[0], lineWidth, out Line2 perpendicularPrevious); + + Line2 current = Line2.CreateWithPerpendicular( + ref vertices[0], ref vertices[1], lineWidth, out Line2 perpendicularCurrent); + + if (!perpendicularPrevious.IntersectWith(perpendicularCurrent, out Vector2 ipE)) + { + ipE = new Vector2(perpendicularCurrent.X1, perpendicularCurrent.Y1); + } + + Vector2 ip1 = ipE; + for (int i = 1; i < vertices.Length - 1; i++) + { + Line2 next = Line2.CreateWithPerpendicular( + ref vertices[i], ref vertices[i + 1], lineWidth, out Line2 perpendicularNext); + + if (!perpendicularCurrent.IntersectWith(perpendicularNext, out Vector2 ip2)) + { + ip2 = new Vector2(perpendicularNext.X1, perpendicularNext.Y1); + } + + DrawRect(ptr + i, current, new Line2(in ip1, in ip2), in scaledColor); + + current = next; + perpendicularCurrent = perpendicularNext; + ip1 = ip2; + } + + if (!perpendicularCurrent.IntersectWith(perpendicularPrevious, out Vector2 ip3)) + { + ip3 = new Vector2(perpendicularPrevious.X1, perpendicularPrevious.Y1); + } + + DrawRect(ptr, current, new Line2(in ip1, in ip3), in scaledColor); + DrawRect(ptr + (vertices.Length - 1), previous, new Line2(in ip3, in ipE), in scaledColor); + } + + /// + /// Draws a filled polygon. + /// + /// The vertices. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// Thrown when one or more arguments are outside the required range. + /// + /// Attention: + /// - The must be declared in a clockwise orientation. + /// - The triangulation used to fill the polygon may not work for concave polygons at the moment! + /// - Complex polygons may not work at all! + /// + public void DrawFillPolygon(Vector2[] vertices, + in Color color, + float rotation, + in Vector2 origin, + float opacity) + { + if (vertices.Length < 3) { throw new ArgumentOutOfRangeException(nameof(vertices.Length)); } + + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)Reserve(vertices.Length - 2); + + if (rotation == 0.0f) + { + for (int i = 1; i < vertices.Length - 1; i += 2) + { + vertex->XY = vertices[0]; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + vertex->XY = vertices[i]; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + vertex->XY = vertices[i + 1]; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + if (i + 2 < vertices.Length) + { + vertex->XY = vertices[i + 2]; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + } + else + { + // INFO: currently we need 4 vertices (rectangle) and can't draw triangles directly so just use the first vertex as the last vertex too. + *vertex = *(vertex - 3); + } + } + } + else + { + Vector2* vs = stackalloc Vector2[vertices.Length]; + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + for (int i = 0; i < vertices.Length; i++) + { + ref Vector2 v = ref vertices[i]; + float x = v.X - origin.X; + float y = v.Y - origin.Y; + *(vs + i) = new Vector2( + (float)(((cos * x) - (sin * y)) + origin.X), (float)((sin * x) + (cos * y) + origin.Y)); + } + + for (int i = 1; i < vertices.Length - 1; i += 2) + { + vertex->XY = *vs; + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + vertex->XY = *(vs + i); + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + vertex->XY = *(vs + i + 1); + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + + if (i + 2 < vertices.Length) + { + vertex->XY = *(vs + i + 2); + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + vertex++; + } + else + { + // INFO: currently we need 4 vertices (rectangle) and can't draw triangles directly so just use the first vertex as the last vertex too. + *vertex = *(vertex - 3); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.Rectangle.cs b/src/Exomia.Framework/Graphics/Canvas.Rectangle.cs new file mode 100644 index 00000000..afca8d24 --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.Rectangle.cs @@ -0,0 +1,213 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using Exomia.Framework.Mathematics; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed unsafe partial class Canvas + { + private static readonly Vector2[] + s_rectangleCornerOffsets = { Vector2.Zero, Vector2.UnitX, Vector2.One, Vector2.UnitY }; + + /// + /// Draw rectangle. + /// + /// The destination rectangle. + /// The color. + /// The width of the line. + /// The rotation. + /// The origin. + /// The opacity. + public void DrawRectangle(in RectangleF destination, + in Color color, + float lineWidth, + float rotation, + in Vector2 origin, + float opacity) + { + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + Vector2 tl = new Vector2(destination.Left + lineWidth, destination.Top + lineWidth); + Vector2 tr = new Vector2(destination.Right - lineWidth, destination.Top + lineWidth); + Vector2 br = new Vector2(destination.Right - lineWidth, destination.Bottom - lineWidth); + Vector2 bl = new Vector2(destination.Left + lineWidth, destination.Bottom - lineWidth); + + Item* ptr = Reserve(4); + + if (rotation == 0f) + { + DrawRect( + ptr + 0, + new Line2(destination.TopLeft, destination.TopRight), + new Line2(in tl, in tr), + in scaledColor); + + DrawRect( + ptr + 1, + new Line2(destination.TopRight, destination.BottomRight), + new Line2(in tr, in br), + in scaledColor); + + DrawRect( + ptr + 2, + new Line2(destination.BottomRight, destination.BottomLeft), + new Line2(in br, in bl), + in scaledColor); + + DrawRect( + ptr + 3, + new Line2(destination.BottomLeft, destination.TopLeft), + new Line2(in bl, in tl), + in scaledColor); + } + else + { + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + + float tlx1 = destination.Left - origin.X; + float tly1 = destination.Top - origin.Y; + + float tlx2 = tl.X - origin.X; + float tly2 = tl.Y - origin.Y; + + float trx1 = destination.Right - origin.X; + float try1 = destination.Top - origin.Y; + + float trx2 = tr.X - origin.X; + float try2 = tr.Y - origin.Y; + + float brx1 = destination.Right - origin.X; + float bry1 = destination.Bottom - origin.Y; + + float brx2 = br.X - origin.X; + float bry2 = br.Y - origin.Y; + + float blx1 = destination.Left - origin.X; + float bly1 = destination.Bottom - origin.Y; + + float blx2 = bl.X - origin.X; + float bly2 = bl.Y - origin.Y; + + Vector2 tl1 = new Vector2( + (float)((tlx1 * cos) - (tly1 * sin)) + origin.X, (float)((tlx1 * sin) + (tly1 * cos)) + origin.Y); + Vector2 tl2 = new Vector2( + (float)((tlx2 * cos) - (tly2 * sin)) + origin.X, (float)((tlx2 * sin) + (tly2 * cos)) + origin.Y); + + Vector2 tr1 = new Vector2( + (float)((trx1 * cos) - (try1 * sin)) + origin.X, (float)((trx1 * sin) + (try1 * cos)) + origin.Y); + Vector2 tr2 = new Vector2( + (float)((trx2 * cos) - (try2 * sin)) + origin.X, (float)((trx2 * sin) + (try2 * cos)) + origin.Y); + + Vector2 br1 = new Vector2( + (float)((brx1 * cos) - (bry1 * sin)) + origin.X, (float)((brx1 * sin) + (bry1 * cos)) + origin.Y); + Vector2 br2 = new Vector2( + (float)((brx2 * cos) - (bry2 * sin)) + origin.X, (float)((brx2 * sin) + (bry2 * cos)) + origin.Y); + + Vector2 bl1 = new Vector2( + (float)((blx1 * cos) - (bly1 * sin)) + origin.X, (float)((blx1 * sin) + (bly1 * cos)) + origin.Y); + Vector2 bl2 = new Vector2( + (float)((blx2 * cos) - (bly2 * sin)) + origin.X, (float)((blx2 * sin) + (bly2 * cos)) + origin.Y); + + DrawRect(ptr + 0, new Line2(in tl1, in tr1), new Line2(in tl2, in tr2), in scaledColor); + DrawRect(ptr + 1, new Line2(in tr1, in br1), new Line2(in tr2, in br2), in scaledColor); + DrawRect(ptr + 2, new Line2(in br1, in bl1), new Line2(in br2, in bl2), in scaledColor); + DrawRect(ptr + 3, new Line2(in bl1, in tl1), new Line2(in bl2, in tl2), in scaledColor); + } + } + + /// + /// Draw fill rectangle. + /// + /// The destination rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + public void DrawFillRectangle(in RectangleF destination, + in Color color, + float rotation, + in Vector2 origin, + float opacity) + { + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + Item* ptr = Reserve(1); + + if (rotation == 0f) + { + for (int j = 0; j < 4; j++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + j; + + Vector2 corner = s_rectangleCornerOffsets[j]; + + vertex->X = destination.X + (corner.X * destination.Width); + vertex->Y = destination.Y + (corner.Y * destination.Height); + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + } + } + else + { + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + + for (int j = 0; j < 4; j++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + j; + + Vector2 corner = s_rectangleCornerOffsets[j]; + float posX = (destination.X - origin.X) + (corner.X * destination.Width); + float posY = (destination.Y - origin.Y) + (corner.Y * destination.Height); + + vertex->X = (float)((origin.X + (posX * cos)) - (posY * sin)); + vertex->Y = (float)(origin.Y + (posX * sin) + (posY * cos)); + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + } + } + } + + private static void DrawRect(Item* ptr, in Line2 lineA, in Line2 lineB, in Vector4 c) + { + // p1 + ptr->V1.XY = lineA.XY1; + ptr->V1.RGBA = c; + ptr->V1.M = COLOR_MODE; + + // p2 + ptr->V2.XY = lineA.XY2; + ptr->V2.RGBA = c; + ptr->V2.M = COLOR_MODE; + + // p2' + ptr->V3.XY = lineB.XY2; + ptr->V3.RGBA = c; + ptr->V3.M = COLOR_MODE; + + // p1' + ptr->V4.XY = lineB.XY1; + ptr->V4.RGBA = c; + ptr->V4.M = COLOR_MODE; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.SpriteFont.cs b/src/Exomia.Framework/Graphics/Canvas.SpriteFont.cs new file mode 100644 index 00000000..abd398cf --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.SpriteFont.cs @@ -0,0 +1,205 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System.Runtime.CompilerServices; +using SharpDX; + +#if NETSTANDARD2_1 +using System; + +#endif + +namespace Exomia.Framework.Graphics +{ + public sealed partial class Canvas + { + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The position. + /// The color. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, ReadOnlySpan text, in Vector2 position, in Color color) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, string text, in Vector2 position, in Color color) +#endif + { + font.Draw(DrawTextInternal, text, position, color, 0f, Vector2.Zero, 1.0f, TextureEffects.None, 0f); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The position. + /// The color. + /// The rotation. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + in Vector2 position, + in Color color, + float rotation) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, string text, in Vector2 position, in Color color, float rotation) +#endif + { + font.Draw(DrawTextInternal, text, position, color, rotation, Vector2.Zero, 1.0f, TextureEffects.None, 0f); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The position. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + string text, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects) +#endif + { + font.Draw(DrawTextInternal, text, position, color, rotation, origin, opacity, effects, 0f); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The start. + /// The end. + /// The position. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + int start, + int end, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + string text, + int start, + int end, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects) +#endif + { + font.Draw(DrawTextInternal, text, start, end, position, color, rotation, origin, opacity, effects, 0f); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The start. + /// The end. + /// The position. + /// The dimension. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + int start, + int end, + in Vector2 position, + in Size2F dimension, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + string text, + int start, + int end, + in Vector2 position, + in Size2F dimension, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects) +#endif + { + font.Draw( + DrawTextInternal, text, start, end, position, dimension, color, rotation, origin, opacity, effects, 0f); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void DrawTextInternal(Texture texture, + in Vector2 position, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float scale, + float opacity, + TextureEffects effects, + float layerDepth) + { + DrawTexture( + texture, new RectangleF(position.X, position.Y, scale, scale), true, sourceRectangle, color, + rotation, origin, opacity, effects, FONT_TEXTURE_MODE); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.Structs.cs b/src/Exomia.Framework/Graphics/Canvas.Structs.cs new file mode 100644 index 00000000..1e795fd1 --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.Structs.cs @@ -0,0 +1,83 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System.Runtime.InteropServices; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class Canvas + { + [StructLayout(LayoutKind.Explicit, Size = VERTEX_STRIDE * 4)] + private struct Item + { + [FieldOffset(VERTEX_STRIDE * 0)] + public VertexPositionColorTextureMode V1; + + [FieldOffset(VERTEX_STRIDE * 1)] + public VertexPositionColorTextureMode V2; + + [FieldOffset(VERTEX_STRIDE * 2)] + public VertexPositionColorTextureMode V3; + + [FieldOffset(VERTEX_STRIDE * 3)] + public VertexPositionColorTextureMode V4; + } + + [StructLayout(LayoutKind.Explicit, Size = VERTEX_STRIDE)] + private struct VertexPositionColorTextureMode + { + [FieldOffset(0)] + public float X; + + [FieldOffset(4)] + public float Y; + + [FieldOffset(0)] + public Vector2 XY; + + [FieldOffset(8)] + public float Z; + + [FieldOffset(12)] + public float W; + + [FieldOffset(8)] + public long ZW; + + [FieldOffset(16)] + public readonly float R; + + [FieldOffset(20)] + public readonly float G; + + [FieldOffset(24)] + public readonly float B; + + [FieldOffset(28)] + public readonly float A; + + [FieldOffset(16)] + public Vector4 RGBA; + + [FieldOffset(32)] + public float U; + + [FieldOffset(36)] + public float V; + + [FieldOffset(40)] + public float M; + + [FieldOffset(44)] + public float O; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.Texture.cs b/src/Exomia.Framework/Graphics/Canvas.Texture.cs new file mode 100644 index 00000000..6368ae73 --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.Texture.cs @@ -0,0 +1,270 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed unsafe partial class Canvas + { + /// + /// Draws a texture. + /// + /// The texture. + /// The position. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, in Vector2 position, in Color color) + { + DrawTexture( + texture, new RectangleF(position.X, position.Y, 1f, 1f), true, + s_nullRectangle, color, 0f, s_vector2Zero, 1.0f, TextureEffects.None); + } + + /// + /// Draws a texture. + /// + /// The texture. + /// The destination rectangle. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, in RectangleF destinationRectangle, in Color color) + { + DrawTexture( + texture, destinationRectangle, false, + s_nullRectangle, color, 0f, s_vector2Zero, 1.0f, TextureEffects.None); + } + + /// + /// Draws a texture. + /// + /// The texture. + /// The position. + /// The source rectangle. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, in Vector2 position, in Rectangle? sourceRectangle, in Color color) + { + DrawTexture( + texture, new RectangleF(position.X, position.Y, 1f, 1f), true, + sourceRectangle, color, 0f, s_vector2Zero, 1.0f, TextureEffects.None); + } + + /// + /// Draws a texture. + /// + /// The texture. + /// The destination rectangle. + /// The source rectangle. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in RectangleF destinationRectangle, + in Rectangle? sourceRectangle, + in Color color) + { + DrawTexture( + texture, destinationRectangle, false, + sourceRectangle, color, 0f, s_vector2Zero, 1.0f, TextureEffects.None); + } + + /// + /// Draws a texture. + /// + /// The texture. + /// The destination rectangle. + /// The source rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in RectangleF destinationRectangle, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects) + { + DrawTexture( + texture, destinationRectangle, false, + sourceRectangle, color, rotation, origin, opacity, effects); + } + + /// + /// Draws a texture. + /// + /// The texture. + /// The position. + /// The source rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The scale. + /// The opacity. + /// The effects. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in Vector2 position, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float scale, + float opacity, + TextureEffects effects) + { + DrawTexture( + texture, new RectangleF(position.X, position.Y, scale, scale), true, + sourceRectangle, color, rotation, origin, opacity, effects); + } + + /// + /// Draws a texture. + /// + /// The texture. + /// The position. + /// The source rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The scale. + /// The opacity. + /// The effects. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in Vector2 position, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + in Vector2 scale, + float opacity, + TextureEffects effects) + { + DrawTexture( + texture, new RectangleF(position.X, position.Y, scale.X, scale.Y), true, + sourceRectangle, color, rotation, origin, opacity, effects); + } + + private void DrawTexture(Texture texture, + in RectangleF destination, + bool scaleDestination, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float mode = TEXTURE_MODE) + + { + long tp = texture.TexturePointer.ToInt64(); + if (!_textures.ContainsKey(tp)) + { + bool lockTaken = false; + try + { + _spinLock.Enter(ref lockTaken); + if (!_textures.ContainsKey(tp)) + { + _textures.Add(tp, texture); + } + } + finally + { + if (lockTaken) + { + _spinLock.Exit(false); + } + } + } + + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + Rectangle s = sourceRectangle ?? new Rectangle(0, 0, texture.Width, texture.Height); + RectangleF d = destination; + if (scaleDestination) + { + d.Width *= s.Width; + d.Height *= s.Height; + } + + if (d.Width < 0) + { + d.X += d.Width; + d.Width = -d.Width; + } + + if (d.Height < 0) + { + d.Y += d.Height; + d.Height = -d.Height; + } + + float deltaX = 1.0f / texture.Width; + float deltaY = 1.0f / texture.Height; + + Item* ptr = Reserve(1); + if (rotation == 0f) + { + for (int j = 0; j < 4; j++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + j; + + Vector2 corner = s_rectangleCornerOffsets[j]; + + vertex->X = d.X + ((corner.X - origin.X) * d.Width); + vertex->Y = d.Y + ((corner.Y - origin.Y) * d.Height); + vertex->ZW = tp; + vertex->RGBA = scaledColor; + corner = s_rectangleCornerOffsets[j ^ (int)effects]; + vertex->U = (s.X + (corner.X * s.Width)) * deltaX; + vertex->V = (s.Y + (corner.Y * s.Height)) * deltaY; + + vertex->M = mode; + } + } + else + { + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + + for (int j = 0; j < 4; j++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + j; + + Vector2 corner = s_rectangleCornerOffsets[j]; + float posX = (corner.X - origin.X) * d.Width; + float posY = (corner.Y - origin.Y) * d.Height; + + vertex->X = (float)((d.X + (posX * cos)) - (posY * sin)); + vertex->Y = (float)(d.Y + (posX * sin) + (posY * cos)); + vertex->ZW = tp; + vertex->RGBA = scaledColor; + corner = s_rectangleCornerOffsets[j ^ (int)effects]; + vertex->U = (s.X + (corner.X * s.Width)) * deltaX; + vertex->V = (s.Y + (corner.Y * s.Height)) * deltaY; + + vertex->M = mode; + } + } + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.Triangle.cs b/src/Exomia.Framework/Graphics/Canvas.Triangle.cs new file mode 100644 index 00000000..c5765ae0 --- /dev/null +++ b/src/Exomia.Framework/Graphics/Canvas.Triangle.cs @@ -0,0 +1,228 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using Exomia.Framework.Mathematics; +using SharpDX; +using SharpDX.Direct3D11; + +namespace Exomia.Framework.Graphics +{ + public sealed unsafe partial class Canvas + { + /// + /// Draws a triangle. + /// + /// The first point. + /// The second point. + /// The third point. + /// The color. + /// The width of the line. + /// The rotation. + /// The origin. + /// The opacity. + /// + /// The points 1 to 3 have to match the , + /// so for default, with is set, + /// the points have to use one of the following winding order: + /// p1 p2 p3 + /// /\ /\ /\ + /// / \ / \ / \ + /// /____\ /____\ /____\ + /// p3 p2 p1 p3 p2 p1. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawTriangle(in Vector2 point1, + in Vector2 point2, + in Vector2 point3, + in Color color, + float lineWidth, + float rotation, + in Vector2 origin, + float opacity) + { + DrawTriangle( + new Triangle2(in point1, in point2, in point3), color, lineWidth, rotation, in origin, opacity); + } + + /// + /// Draws a triangle. + /// + /// The triangle. + /// The color. + /// The width of the line. + /// The rotation. + /// The origin. + /// The opacity. + /// Thrown when one or more arguments have unsupported or illegal values. + /// + /// The points 1 to 3 have to match the , + /// so for default, with is set, + /// the points have to use one of the following winding order: + /// p1 p2 p3 + /// /\ /\ /\ + /// / \ / \ / \ + /// /____\ /____\ /____\ + /// p3 p2 p1 p3 p2 p1. + /// + public void DrawTriangle(in Triangle2 triangle, + in Color color, + float lineWidth, + float rotation, + in Vector2 origin, + float opacity) + { + Triangle2 t = rotation == 0.0 ? triangle : Triangle2.RotateAround(in triangle, rotation, in origin); + + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + Line2 a = new Line2(in t.XY1, in t.XY2); + Line2 perpendicularA = a.GetPerpendicular(lineWidth); + + Line2 b = new Line2(in t.XY2, in t.XY3); + Line2 perpendicularB = b.GetPerpendicular(lineWidth); + + Line2 c = new Line2(in t.XY3, in t.XY1); + Line2 perpendicularC = c.GetPerpendicular(lineWidth); + + if (!perpendicularA.IntersectWith(perpendicularB, out Vector2 ipAb)) + { + throw new ArgumentException("The lines a and b are parallel to each other! Check the triangle points!"); + } + + if (!perpendicularB.IntersectWith(perpendicularC, out Vector2 ipBc)) + { + throw new ArgumentException("The lines b and c are parallel to each other! Check the triangle points!"); + } + + if (!perpendicularC.IntersectWith(perpendicularA, out Vector2 ipCa)) + { + throw new ArgumentException("The lines c and a are parallel to each other! Check the triangle points!"); + } + + Item* ptr = Reserve(3); + DrawRect(ptr + 0, a, new Line2(in ipCa, in ipAb), in scaledColor); + DrawRect(ptr + 1, b, new Line2(in ipAb, in ipBc), in scaledColor); + DrawRect(ptr + 2, c, new Line2(in ipBc, in ipCa), in scaledColor); + } + + /// + /// Draws a filled triangle. + /// + /// The first point. + /// The second point. + /// The third point. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// + /// The points 1 to 3 have to match the , + /// so for default, with is set, + /// the points have to use one of the following winding order: + /// p1 p2 p3 + /// /\ /\ /\ + /// / \ / \ / \ + /// /____\ /____\ /____\ + /// p3 p2 p1 p3 p2 p1. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawFillTriangle(in Vector2 point1, + in Vector2 point2, + in Vector2 point3, + in Color color, + float rotation, + in Vector2 origin, + float opacity) + { + DrawFillTriangle(new Triangle2(in point1, in point2, in point3), color, rotation, origin, opacity); + } + + /// + /// Draws a filled triangle. + /// + /// The triangle. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// + /// The points 1 to 3 from the have to match the + /// , + /// so for default, with is set, + /// the points have to use one of the following winding order: + /// p1 p2 p3 + /// /\ /\ /\ + /// / \ / \ / \ + /// /____\ /____\ /____\ + /// p3 p2 p1 p3 p2 p1. + /// + public void DrawFillTriangle(in Triangle2 triangle, + in Color color, + float rotation, + in Vector2 origin, + float opacity) + { + Vector4 scaledColor; + scaledColor.X = color.R * opacity; + scaledColor.Y = color.G * opacity; + scaledColor.Z = color.B * opacity; + scaledColor.W = color.A * opacity; + + Item* ptr = Reserve(1); + + if (rotation == 0.0f) + { + for (int i = 0; i < 3; i++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + i; + + fixed (Triangle2* t = &triangle) + { + Vector2* tf = (Vector2*)t; + vertex->XY = *(tf + i); + } + + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + } + } + else + { + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + + for (int i = 0; i < 3; i++) + { + VertexPositionColorTextureMode* vertex = (VertexPositionColorTextureMode*)ptr + i; + + fixed (Triangle2* t = &triangle) + { + Vector2* tf = (Vector2*)t; + Vector2 v = *(tf + i) - origin; + vertex->X = (float)(((cos * v.X) - (sin * v.Y)) + origin.X); + vertex->Y = (float)((sin * v.X) + (cos * v.Y) + origin.Y); + } + + vertex->RGBA = scaledColor; + vertex->M = COLOR_MODE; + } + } + + // INFO: currently we need 4 vertices (rectangle) and can't draw triangles directly so just use the first vertex as the last vertex too. + *((VertexPositionColorTextureMode*)ptr + 3) = *(VertexPositionColorTextureMode*)ptr; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Canvas.cs b/src/Exomia.Framework/Graphics/Canvas.cs index 6d6a4912..d76ae830 100644 --- a/src/Exomia.Framework/Graphics/Canvas.cs +++ b/src/Exomia.Framework/Graphics/Canvas.cs @@ -9,8 +9,18 @@ #endregion using System; -using System.Text; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using Exomia.Framework.Graphics.Buffers; +using Exomia.Framework.Graphics.Shader; +using Exomia.Framework.Resources; +using Exomia.Framework.Win32; using SharpDX; +using SharpDX.Direct3D; using SharpDX.Direct3D11; namespace Exomia.Framework.Graphics @@ -18,615 +28,412 @@ namespace Exomia.Framework.Graphics /// /// A canvas. This class cannot be inherited. /// - public sealed class Canvas : IDisposable + public sealed unsafe partial class Canvas : IDisposable { - private static readonly Vector2 s_vector2Zero = Vector2.Zero; - private static readonly Rectangle? s_nullRectangle = null; - private readonly Device5 _device; - private readonly DeviceContext4 _context; - - /// - /// Initializes a new instance of the class. - /// - /// Zero-based index of the device. - public Canvas(IGraphicsDevice iDevice) + private const int MAX_BATCH_SIZE = 1 << 13; + private const int INITIAL_QUEUE_SIZE = 1 << 7; + private const int MAX_VERTEX_COUNT = MAX_BATCH_SIZE * 4; + private const int MAX_INDEX_COUNT = MAX_BATCH_SIZE * 6; + private const int BATCH_SEQUENTIAL_THRESHOLD = 1 << 9; + private const int VERTEX_STRIDE = sizeof(float) * 12; + private const int MAX_TEXTURE_SLOTS = 8; + private const int MAX_FONT_TEXTURE_SLOTS = 4; + + private const float COLOR_MODE = 0.0f; + private const float TEXTURE_MODE = 1.0f; + private const float FONT_TEXTURE_MODE = 2.0f; + private const float FILL_CIRCLE_MODE = 3.0f; + private const float FILL_CIRCLE_ARC_MODE = 4.0f; + private const float BORDER_CIRCLE_MODE = 5.0f; + private const float BORDER_CIRCLE_ARC_MODE = 6.0f; + + private static readonly ushort[] s_indices; + private static readonly Vector2 s_vector2Zero = Vector2.Zero; + private static readonly Rectangle? s_nullRectangle = null; + + private readonly DeviceContext4 _context; + + private readonly InputLayout _vertexInputLayout; + + private readonly IndexBuffer _indexBuffer; + private readonly VertexBuffer _vertexBuffer; + private readonly ConstantBuffer _perFrameBuffer; + + private readonly Shader.Shader _shader; + private readonly PixelShader _pixelShader; + private readonly VertexShader _vertexShader; + + private readonly BlendState _defaultBlendState; + private readonly DepthStencilState _defaultDepthStencilState; + private readonly RasterizerState _defaultRasterizerState; + private readonly RasterizerState _defaultRasterizerScissorEnabledState; + private readonly SamplerState _defaultSamplerState; + private BlendState? _blendState; + private DepthStencilState? _depthStencilState; + private RasterizerState? _rasterizerState; + private SamplerState? _samplerState; + + private bool _isBeginCalled, _isScissorEnabled; + private Rectangle _scissorRectangle; + + private Matrix _projectionMatrix, _viewMatrix, _transformMatrix; + + private Item* _itemQueue; + private int _itemQueueLength; + private int _itemQueueCount; + + private SpinLock _spinLock = new SpinLock(Debugger.IsAttached); + + private readonly Dictionary _textures = new Dictionary(INITIAL_QUEUE_SIZE); + private readonly Dictionary _textureSlotMap = new Dictionary(MAX_TEXTURE_SLOTS); + private readonly Dictionary _fontTextureSlotMap = new Dictionary(MAX_FONT_TEXTURE_SLOTS); + + /// + /// Initializes static members of the class. + /// + /// + /// Thrown when one or more arguments are outside + /// the required range. + /// + static Canvas() { - _device = iDevice.Device; - _context = iDevice.DeviceContext; + if (MAX_INDEX_COUNT > ushort.MaxValue) +#pragma warning disable 162 + { +#pragma warning restore IDE0079 // Remove unnecessary suppression - iDevice.ResizeFinished += IDevice_onResizeFinished; + // ReSharper disable once NotResolvedInText + throw new ArgumentOutOfRangeException("MAX_INDEX_COUNT->MAX_BATCH_SIZE"); + } +#pragma warning restore 162 + s_indices = new ushort[MAX_INDEX_COUNT]; + for (int i = 0, k = 0; i < MAX_INDEX_COUNT; i += 6, k += 4) + { + s_indices[i + 0] = (ushort)(k + 0); + s_indices[i + 1] = (ushort)(k + 1); + s_indices[i + 2] = (ushort)(k + 2); + s_indices[i + 3] = (ushort)(k + 0); + s_indices[i + 4] = (ushort)(k + 2); + s_indices[i + 5] = (ushort)(k + 3); + } } - #region Line - /// - /// Draw line. + /// Initializes a new instance of the class. /// - /// The first point. - /// The second point. - /// The color. - /// Width of the line. - /// The opacity. - /// (Optional) The length factor. - public void DrawLine(in Vector2 point1, - in Vector2 point2, - in Color color, - float lineWidth, - float opacity, - float lengthFactor = 1.0f) + /// The graphics device. + /// Thrown when a value was unexpectedly null. + public Canvas(IGraphicsDevice graphicsDevice) { - DrawFillRectangle( - new RectangleF(point1.X, point1.Y, Vector2.Distance(point1, point2) * lengthFactor, lineWidth), color, - (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X), s_vector2Zero, opacity); - } + _context = graphicsDevice.DeviceContext; - #endregion + _defaultBlendState = graphicsDevice.BlendStates.AlphaBlend; + _defaultSamplerState = graphicsDevice.SamplerStates.LinearWrap; + _defaultDepthStencilState = graphicsDevice.DepthStencilStates.None; - /// - /// Device on resize finished. - /// - /// The viewport. - private static void IDevice_onResizeFinished(ViewportF viewport) { } + _defaultRasterizerState = graphicsDevice.RasterizerStates.CullBackDepthClipOff; + _defaultRasterizerScissorEnabledState = graphicsDevice.RasterizerStates.CullBackDepthClipOffScissorEnabled; - #region Triangle + _indexBuffer = IndexBuffer.Create(graphicsDevice, s_indices); - /// - /// Draw triangle. - /// - /// The first point. - /// The second point. - /// The third point. - /// The color. - /// Width of the line. - /// The opacity. - public void DrawTriangle(in Vector2 point1, - in Vector2 point2, - in Vector2 point3, - in Color color, - float lineWidth, - float opacity) { } - - /// - /// Draw fill triangle. - /// - /// The first point. - /// The second point. - /// The third point. - /// The color. - /// Width of the line. - /// The opacity. - public void DrawFillTriangle(in Vector2 point1, - in Vector2 point2, - in Vector2 point3, - in Color color, - float lineWidth, - float opacity) { } - - #endregion + Assembly assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = + assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{Shaders.CANVAS}") ?? + throw new NullReferenceException($"{assembly.GetName().Name}.{Shaders.CANVAS}")) + { + Shader.Shader.Group group = + (_shader = ShaderFileLoader.FromStream(graphicsDevice, stream) ?? + throw new NullReferenceException(nameof(ShaderFileLoader.FromStream)))["DEFAULT"]; - #region Rectangle + _vertexShader = group; + _pixelShader = group; - /// - /// Draw rectangle. - /// - /// Destination rectangle. - /// The color. - /// Width of the line. - /// The rotation. - /// The origin. - /// The opacity. - public void DrawRectangle(in RectangleF destinationRectangle, - in Color color, - float lineWidth, - float rotation, - in Vector2 origin, - float opacity) { } + _vertexInputLayout = group.CreateInputLayout(graphicsDevice, Shader.Shader.Type.VertexShader); + } - /// - /// Draw fill rectangle. - /// - /// Destination rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - public void DrawFillRectangle(in RectangleF destinationRectangle, - in Color color, - float rotation, - in Vector2 origin, - float opacity) { } + _vertexBuffer = VertexBuffer.Create(graphicsDevice, MAX_VERTEX_COUNT); + _perFrameBuffer = ConstantBuffer.Create(graphicsDevice); - #endregion + _itemQueue = (Item*)Marshal.AllocHGlobal(sizeof(Item) * (_itemQueueLength = MAX_BATCH_SIZE)); - #region Arc + graphicsDevice.ResizeFinished += GraphicsDeviceOnResizeFinished; + Resize(graphicsDevice.Viewport); + } /// - /// Draw arc. + /// Resizes. /// - /// The center. - /// The radius. - /// The start. - /// The end. - /// The width. - /// The height. - /// The color. - /// Width of the line. - /// The opacity. - /// The segments. - public void DrawArc(in Vector2 center, - float radius, - float start, - float end, - float width, - float height, - in Color color, - float lineWidth, - float opacity, - int segments) { } + /// The size. + public void Resize(Size2F size) + { + Resize(size.Width, size.Height); + } /// - /// Draw fill arc. + /// Resizes. /// - /// The center. - /// The radius. - /// The start. - /// The end. - /// The width. - /// The height. - /// The color. - /// Width of the line. - /// The opacity. - /// The segments. - public void DrawFillArc(in Vector2 center, - float radius, - float start, - float end, - float width, - float height, - in Color color, - float lineWidth, - float opacity, - int segments) { } - - #endregion - - #region Polygon + /// The viewport. + public void Resize(ViewportF viewport) + { + Resize(viewport.Width, viewport.Height); + } /// - /// Draw polygon. + /// Resizes. /// - /// The vertex. - /// The color. - /// Width of the line. - /// The opacity. - public void DrawPolygon(Vector2[] vertex, in Color color, float lineWidth, float opacity) + /// The width. + /// The height. + public void Resize(float width, float height) { - if (vertex.Length > 1) + float xRatio = width > 0 ? 1f / width : 0f; + float yRatio = height > 0 ? -1f / height : 0f; + + _projectionMatrix = new Matrix { - int l = vertex.Length - 1; - for (int i = 0; i < l; i++) - { - DrawLine(vertex[i], vertex[i + 1], color, lineWidth, opacity); - } - DrawLine(vertex[l], vertex[0], color, lineWidth, opacity); - } + M11 = xRatio * 2f, + M22 = yRatio * 2f, + M33 = 1f, + M44 = 1f, + M41 = -1f, + M42 = 1f + }; } /// - /// Draw fill polygon. - /// - /// The vertex. - /// The color. - /// Width of the line. - /// The opacity. - public void DrawFillPolygon(Vector2[] vertex, in Color color, float lineWidth, float opacity) { } + /// Begins a new batch. + /// + /// Thrown when the requested operation is invalid. + /// (Optional) State of the blend. + /// (Optional) State of the sampler. + /// (Optional) State of the depth stencil. + /// (Optional) State of the rasterizer. + /// (Optional) The transform matrix. + /// (Optional) The view matrix. + /// (Optional) The scissor rectangle. + public void Begin(BlendState? blendState = null, + SamplerState? samplerState = null, + DepthStencilState? depthStencilState = null, + RasterizerState? rasterizerState = null, + Matrix? transformMatrix = null, + Matrix? viewMatrix = null, + Rectangle? scissorRectangle = null) + { + if (_isBeginCalled) + { + throw new InvalidOperationException("End must be called before begin"); + } - #endregion + _blendState = blendState; + _samplerState = samplerState; + _depthStencilState = depthStencilState; + _rasterizerState = rasterizerState; + _transformMatrix = transformMatrix ?? Matrix.Identity; + _viewMatrix = viewMatrix ?? Matrix.Identity; - #region Texture + _isScissorEnabled = scissorRectangle.HasValue; + _scissorRectangle = scissorRectangle ?? Rectangle.Empty; - /// - /// Draws. - /// - /// The texture. - /// The position. - /// The color. - public void Draw(Texture texture, in Vector2 position, in Color color) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, 1f, 1f), true, - s_nullRectangle, color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None); + _isBeginCalled = true; } /// - /// Draws. + /// Ends the current batch. /// - /// The texture. - /// Destination rectangle. - /// The color. - public void Draw(Texture texture, in RectangleF destinationRectangle, in Color color) + /// Thrown when the requested operation is invalid. + public void End() { - DrawSprite( - texture, destinationRectangle, false, - s_nullRectangle, color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None); - } + if (!_isBeginCalled) + { + throw new InvalidOperationException("Begin must be called before End"); + } - /// - /// Draws. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - public void Draw(Texture texture, in Vector2 position, in Rectangle? sourceRectangle, in Color color) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, 1f, 1f), true, - sourceRectangle, color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None); - } + if (_itemQueueCount > 0) + { + PrepareForRendering(); + FlushBatch(); + } - /// - /// Draws. - /// - /// The texture. - /// Destination rectangle. - /// Source rectangle. - /// The color. - public void Draw(Texture texture, - in RectangleF destinationRectangle, - in Rectangle? sourceRectangle, - in Color color) - { - DrawSprite( - texture, destinationRectangle, false, - sourceRectangle, color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None); + _isBeginCalled = false; } - /// - /// Draws. - /// - /// The texture. - /// Destination rectangle. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - public void Draw(Texture texture, - in RectangleF destinationRectangle, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) + private void GraphicsDeviceOnResizeFinished(ViewportF viewport) { - DrawSprite( - texture, destinationRectangle, false, - sourceRectangle, color, rotation, origin, opacity, effects); + Resize(viewport); } - /// - /// Draws. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The scale. - /// The opacity. - /// The effects. - public void Draw(Texture texture, - in Vector2 position, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float scale, - float opacity, - SpriteEffects effects) + private void PrepareForRendering() { - DrawSprite( - texture, new RectangleF(position.X, position.Y, scale, scale), true, - sourceRectangle, color, rotation, origin, opacity, effects); - } + _context.VertexShader.Set(_vertexShader); + _context.PixelShader.Set(_pixelShader); - /// - /// Draws. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The scale. - /// The opacity. - /// The effects. - public void Draw(Texture texture, - in Vector2 position, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - in Vector2 scale, - float opacity, - SpriteEffects effects) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, scale.X, scale.Y), true, - sourceRectangle, color, rotation, origin, opacity, effects); - } + _context.OutputMerger.SetBlendState(_blendState ?? _defaultBlendState); + _context.OutputMerger.SetDepthStencilState(_depthStencilState ?? _defaultDepthStencilState); - /// - /// Draw sprite. - /// - /// The texture. - /// Destination for the. - /// True to scale destination. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - private void DrawSprite(Texture texture, - in RectangleF destination, - bool scaleDestination, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) { } + _context.Rasterizer.State = _rasterizerState ?? _defaultRasterizerState; - #endregion + if (_isScissorEnabled) + { + _context.Rasterizer.State = _rasterizerState ?? _defaultRasterizerScissorEnabledState; + _context.Rasterizer.SetScissorRectangle( + _scissorRectangle.Left, _scissorRectangle.Top, + _scissorRectangle.Right, _scissorRectangle.Bottom); + } - #region SpiteFont + _context.PixelShader.SetSampler(0, _samplerState ?? _defaultSamplerState); - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - public void DrawText(SpriteFont font, string text, in Vector2 position, in Color color) - { - font.Draw(DrawTextInternal, text, position, color, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0f); - } + _context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; + _context.InputAssembler.InputLayout = _vertexInputLayout; - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - public void DrawText(SpriteFont font, string text, in Vector2 position, in Color color, float rotation) - { - font.Draw(DrawTextInternal, text, position, color, rotation, Vector2.Zero, 1.0f, SpriteEffects.None, 0f); - } + Matrix worldViewProjection = Matrix.Transpose(_transformMatrix * _viewMatrix * _projectionMatrix); + _context.UpdateSubresource(ref worldViewProjection, _perFrameBuffer); + _context.VertexShader.SetConstantBuffer(0, _perFrameBuffer); + _context.PixelShader.SetConstantBuffer(0, _perFrameBuffer); - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - public void DrawText(SpriteFont font, - string text, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) - { - font.Draw(DrawTextInternal, text, position, color, rotation, origin, opacity, effects, 0f); + _context.InputAssembler.SetIndexBuffer(_indexBuffer, _indexBuffer.Format, 0); + _context.InputAssembler.SetVertexBuffers(0, _vertexBuffer); } - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - public void DrawText(SpriteFont font, - string text, - int start, - int end, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) + private void FlushBatch() { - font.Draw(DrawTextInternal, text, start, end, position, color, rotation, origin, opacity, effects, 0f); - } + int offset = 0; - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The dimension. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - public void DrawText(SpriteFont font, - string text, - int start, - int end, - in Vector2 position, - in Size2F dimension, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) - { - font.Draw( - DrawTextInternal, text, start, end, position, dimension, color, rotation, origin, opacity, effects, 0f); - } + for (int i = 0; i < _itemQueueCount; i++) + { + ref Item item = ref _itemQueue[i]; - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - public void DrawText(SpriteFont font, StringBuilder text, in Vector2 position, in Color color) - { - font.Draw(DrawTextInternal, text, position, color, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, 0f); - } + switch (item.V1.M) + { + case TEXTURE_MODE: + { + long tp = item.V1.ZW; + if (!_textures.TryGetValue(tp, out Texture texture)) + { + throw new KeyNotFoundException("The looked up texture wasn't found!"); + } + + if (!_textureSlotMap.TryGetValue(tp, out int tSlot)) + { + _context.PixelShader.SetShaderResource(_textureSlotMap.Count, texture.TextureView); + _textureSlotMap.Add(tp, tSlot = _textureSlotMap.Count); + } + + item.V1.O = tSlot; + item.V2.O = tSlot; + item.V3.O = tSlot; + item.V4.O = tSlot; + + if (_textureSlotMap.Count > MAX_TEXTURE_SLOTS) + { + if (i > offset) + { + DrawBatch(offset, i - offset); + } + + offset = i; + _textureSlotMap.Clear(); + _fontTextureSlotMap.Clear(); + } + break; + } + case FONT_TEXTURE_MODE: + { + long tp = item.V1.ZW; + if (!_textures.TryGetValue(tp, out Texture texture)) + { + throw new KeyNotFoundException("The looked up texture wasn't found!"); + } + + if (!_fontTextureSlotMap.TryGetValue(tp, out int tSlot)) + { + _context.PixelShader.SetShaderResource( + MAX_TEXTURE_SLOTS + _fontTextureSlotMap.Count, texture.TextureView); + _fontTextureSlotMap.Add(tp, tSlot = _fontTextureSlotMap.Count); + } + + item.V1.O = tSlot; + item.V2.O = tSlot; + item.V3.O = tSlot; + item.V4.O = tSlot; + + if (_fontTextureSlotMap.Count > MAX_FONT_TEXTURE_SLOTS) + { + if (i > offset) + { + DrawBatch(offset, i - offset); + } + + offset = i; + _fontTextureSlotMap.Clear(); + _textureSlotMap.Clear(); + } + break; + } + } + } - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - public void DrawText(SpriteFont font, StringBuilder text, in Vector2 position, in Color color, float rotation) - { - font.Draw(DrawTextInternal, text, position, color, rotation, Vector2.Zero, 1.0f, SpriteEffects.None, 0f); - } + DrawBatch(offset, _itemQueueCount - offset); - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - public void DrawText(SpriteFont font, - StringBuilder text, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) - { - font.Draw(DrawTextInternal, text, position, color, rotation, origin, opacity, effects, 0f); + _itemQueueCount = 0; + _textureSlotMap.Clear(); + _fontTextureSlotMap.Clear(); } - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - public void DrawText(SpriteFont font, - StringBuilder text, - int start, - int end, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) + private void DrawBatch(int offset, int count) { - font.Draw(DrawTextInternal, text, start, end, position, color, rotation, origin, opacity, effects, 0f); - } + while (count > 0) + { + int batchSize = count; + if (batchSize > MAX_BATCH_SIZE) + { + batchSize = MAX_BATCH_SIZE; + } - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The dimension. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - public void DrawText(SpriteFont font, - StringBuilder text, - int start, - int end, - in Vector2 position, - in Size2F dimension, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects) - { - font.Draw( - DrawTextInternal, text, start, end, position, dimension, color, rotation, origin, opacity, effects, 0f); + DataBox box = _context.MapSubresource( + _vertexBuffer, 0, MapMode.WriteDiscard, MapFlags.None); + VertexPositionColorTextureMode* vpctPtr = (VertexPositionColorTextureMode*)box.DataPointer; + + for (int i = 0; i < batchSize; i++) + { + ref Item item = ref _itemQueue[i + offset]; + VertexPositionColorTextureMode* v = vpctPtr + (i << 2); + *(v + 0) = item.V1; + *(v + 1) = item.V2; + *(v + 2) = item.V3; + *(v + 3) = item.V4; + } + + _context.UnmapSubresource(_vertexBuffer, 0); + _context.DrawIndexed(6 * batchSize, 0, 0); + + offset += batchSize; + count -= batchSize; + } } - /// - /// Draw text internal. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The scale. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void DrawTextInternal(Texture texture, - in Vector2 position, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float scale, - float opacity, - SpriteEffects effects, - float layerDepth) + private Item* Reserve(int itemCount) { - DrawSprite( - texture, new RectangleF(position.X, position.Y, scale, scale), true, sourceRectangle, color, - rotation, origin, opacity, effects); - } + if (_itemQueueCount >= _itemQueueLength) + { + bool lockTaken = false; + try + { + _spinLock.Enter(ref lockTaken); + if (_itemQueueCount >= _itemQueueLength) + { + Mem.Resize(ref _itemQueue, ref _itemQueueLength, _itemQueueLength * 2); + } + } + finally + { + if (lockTaken) + { + _spinLock.Exit(false); + } + } + } - #endregion + return _itemQueue + (Interlocked.Add(ref _itemQueueCount, itemCount) - itemCount); + } #region IDisposable Support @@ -655,7 +462,22 @@ private void Dispose(bool disposing) { if (!_disposed) { - if (disposing) { } + if (disposing) + { + Utilities.Dispose(ref _blendState); + Utilities.Dispose(ref _rasterizerState); + Utilities.Dispose(ref _samplerState); + Utilities.Dispose(ref _depthStencilState); + + _vertexBuffer.Dispose(); + _indexBuffer.Dispose(); + _perFrameBuffer.Dispose(); + + _shader.Dispose(); + _vertexInputLayout.Dispose(); + } + + Marshal.FreeHGlobal(new IntPtr(_itemQueue)); _disposed = true; } diff --git a/src/Exomia.Framework/Graphics/DefaultTextures.cs b/src/Exomia.Framework/Graphics/DefaultTextures.cs deleted file mode 100644 index 76ea0dcb..00000000 --- a/src/Exomia.Framework/Graphics/DefaultTextures.cs +++ /dev/null @@ -1,98 +0,0 @@ -#region License - -// Copyright (c) 2018-2020, exomia -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -#endregion - -using System; -using System.IO; -using SharpDX; -using SharpDX.Direct3D11; - -namespace Exomia.Framework.Graphics -{ - /// - /// A default textures. - /// - public static class DefaultTextures - { - private const string WHITE_TEXTURE_BASE64 = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII="; - - private static Texture? s_whiteTexture; - private static bool s_isInitialized; - - /// - /// Gets the white texture. - /// - /// - /// The white texture. - /// - public static Texture WhiteTexture - { - get { return s_whiteTexture!; } - } - - /// - /// Initializes the textures. - /// - /// The device. - internal static void InitializeTextures(Device5 device) - { - if (!s_isInitialized) - { - s_isInitialized = true; - s_disposedValue = false; - - using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(WHITE_TEXTURE_BASE64))) - { - s_whiteTexture = Texture.Load(device, ms) ?? - throw new NullReferenceException($"{nameof(WhiteTexture)}"); - } - } - } - - #region IDisposable Support - - /// - /// True to disposed value. - /// - private static bool s_disposedValue; - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting - /// unmanaged resources. - /// - /// - /// True to release both managed and unmanaged resources; false to - /// release only unmanaged resources. - /// - public static void Dispose(bool disposing) - { - if (!s_disposedValue) - { - if (disposing) - { - Utilities.Dispose(ref s_whiteTexture); - } - s_isInitialized = false; - s_disposedValue = true; - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting - /// unmanaged resources. - /// - public static void Dispose() - { - Dispose(true); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/DepthStencilStates.cs b/src/Exomia.Framework/Graphics/DepthStencilStates.cs new file mode 100644 index 00000000..93472f6c --- /dev/null +++ b/src/Exomia.Framework/Graphics/DepthStencilStates.cs @@ -0,0 +1,122 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using SharpDX.Direct3D11; + +namespace Exomia.Framework.Graphics +{ + /// + /// The built-in depth stencil states. This class cannot be inherited. + /// + public sealed class DepthStencilStates : IDisposable + { + /// + /// A built-in state object with default settings for using a depth stencil buffer. + /// + public readonly DepthStencilState Default; + + /// + /// A built-in state object with settings for enabling a read-only depth stencil buffer. + /// + public readonly DepthStencilState DepthRead; + + /// + /// A built-in state object with settings for not using a depth stencil buffer. + /// + public readonly DepthStencilState None; + + /// + /// A built-in state object with default settings for using a depth stencil buffer with stencil enabled. + /// + public readonly DepthStencilState DefaultStencilEnabled; + + /// + /// Initializes a new instance of the class. + /// + /// The graphics device. + internal DepthStencilStates(IGraphicsDevice graphicsDevice) + { + Default = Create(graphicsDevice.Device, nameof(Default), true, true, false); + DepthRead = Create(graphicsDevice.Device, nameof(DepthRead), true, false, false); + None = Create(graphicsDevice.Device, nameof(None), false, false, false); + DefaultStencilEnabled = Create(graphicsDevice.Device, nameof(DefaultStencilEnabled), true, true, true); + } + + private static DepthStencilState Create(Device5 device, + string name, + bool depthEnable, + bool depthWriteEnable, + bool stencilEnabled) + { + return new DepthStencilState( + device, + new DepthStencilStateDescription + { + IsDepthEnabled = depthEnable, + DepthWriteMask = depthWriteEnable ? DepthWriteMask.All : DepthWriteMask.Zero, + DepthComparison = Comparison.LessEqual, + IsStencilEnabled = stencilEnabled, + StencilReadMask = 0xFF, + StencilWriteMask = 0xFF, + FrontFace = new DepthStencilOperationDescription + { + FailOperation = StencilOperation.Keep, + DepthFailOperation = StencilOperation.Keep, + PassOperation = StencilOperation.Keep, + Comparison = Comparison.Always + }, + BackFace = new DepthStencilOperationDescription + { + FailOperation = StencilOperation.Keep, + DepthFailOperation = StencilOperation.Keep, + PassOperation = StencilOperation.Keep, + Comparison = Comparison.Always + } + }) { DebugName = name }; + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + Default.Dispose(); + DepthRead.Dispose(); + None.Dispose(); + DefaultStencilEnabled.Dispose(); + } + _disposed = true; + } + } + + /// + ~DepthStencilStates() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/GraphicsDevice.cs b/src/Exomia.Framework/Graphics/GraphicsDevice.cs index c1661523..63d721c0 100644 --- a/src/Exomia.Framework/Graphics/GraphicsDevice.cs +++ b/src/Exomia.Framework/Graphics/GraphicsDevice.cs @@ -41,47 +41,52 @@ public sealed class GraphicsDevice : IGraphicsDevice public event EventHandler? ResizeFinished; private Adapter4? _adapter4; - private RenderTargetView1? _currentRenderView; - private Device5? _d3DDevice5; - private DeviceContext4? _d3DDeviceContext; - private DepthStencilView? _depthStencilView; - private Device4? _dxgiDevice4; - private Factory5? _dxgiFactory; - private bool _needResize; - private Output6? _output6; - private RenderTargetView1? _renderView1; + private RenderTargetView1 _currentRenderView = null!; + private Device5 _d3DDevice5 = null!; + private DeviceContext4 _d3DDeviceContext = null!; + private DepthStencilView _depthStencilView = null!; + private Device4 _dxgiDevice4 = null!; + private Factory5 _dxgiFactory = null!; + private Output6 _output6 = null!; + private RenderTargetView1 _renderView1 = null!; + private SwapChain4 _swapChain4 = null!; + private BlendStates _blendStates = null!; + private DepthStencilStates _depthStencilStates = null!; + private RasterizerStates _rasterizerStates = null!; + private SamplerStates _samplerStates = null!; + private Textures _textures = null!; private ResizeParameters _resizeParameters; - private SwapChain4? _swapChain4; + private bool _needResize; private int _vSync; /// - public Adapter4 Adapter + public Adapter4? Adapter { - get { return _adapter4!; } + get { return _adapter4; } } /// public Device5 Device { - get { return _d3DDevice5!; } + get { return _d3DDevice5; } } /// public DeviceContext4 DeviceContext { - get { return _d3DDeviceContext!; } + get { return _d3DDeviceContext; } } /// public Device4 DxgiDevice { - get { return _dxgiDevice4!; } + get { return _dxgiDevice4; } } /// public Factory5 Factory { - get { return _dxgiFactory!; } + get { return _dxgiFactory; } } /// @@ -90,13 +95,13 @@ public Factory5 Factory /// public RenderTargetView1 RenderView { - get { return _renderView1!; } + get { return _renderView1; } } /// public SwapChain4 SwapChain { - get { return _swapChain4!; } + get { return _swapChain4; } } /// @@ -109,6 +114,36 @@ public bool VSync set { _vSync = value ? 1 : 0; } } + /// + public BlendStates BlendStates + { + get { return _blendStates; } + } + + /// + public DepthStencilStates DepthStencilStates + { + get { return _depthStencilStates; } + } + + /// + public RasterizerStates RasterizerStates + { + get { return _rasterizerStates; } + } + + /// + public SamplerStates SamplerStates + { + get { return _samplerStates; } + } + + /// + public Textures Textures + { + get { return _textures; } + } + /// public void Clear() { @@ -118,9 +153,9 @@ public void Clear() /// public void Clear(Color color) { - _d3DDeviceContext!.ClearDepthStencilView( - _depthStencilView!, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1f, 0); - _d3DDeviceContext!.ClearRenderTargetView(_currentRenderView!, color); + _d3DDeviceContext.ClearDepthStencilView( + _depthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1f, 0); + _d3DDeviceContext!.ClearRenderTargetView(_currentRenderView, color); } /// @@ -141,7 +176,7 @@ public void Resize(int width, int height) { _resizeParameters = new ResizeParameters { - BufferCount = _swapChain4!.Description1.BufferCount, + BufferCount = _swapChain4.Description1.BufferCount, Width = width, Height = height, SwapChainFlags = _swapChain4.Description1.Flags @@ -154,21 +189,21 @@ public void SetFullscreenState(bool state, Output? output = null) { if (GetFullscreenState() != state) { - _swapChain4!.SetFullscreenState(state, output); + _swapChain4.SetFullscreenState(state, output); } } /// public bool GetFullscreenState() { - return _swapChain4!.IsFullScreen; + return _swapChain4.IsFullScreen; } /// public void SetRenderTarget(RenderTargetView1? target) { _currentRenderView = target ?? _renderView1; - _d3DDeviceContext!.OutputMerger.SetRenderTargets(_depthStencilView, _currentRenderView); + _d3DDeviceContext.OutputMerger.SetRenderTargets(_depthStencilView, _currentRenderView); } /// @@ -192,11 +227,11 @@ public bool BeginFrame() /// public void EndFrame() { - _swapChain4!.Present(_vSync, PresentFlags.None, s_defaultPresentParameters); + _swapChain4.Present(_vSync, PresentFlags.None, s_defaultPresentParameters); } /// - /// Initializes this object. + /// Initializes the . /// /// [in,out] Options for controlling the operation. public void Initialize(ref GameGraphicsParameters parameters) @@ -242,10 +277,10 @@ public void Initialize(ref GameGraphicsParameters parameters) featureLevel: SharpDX.Direct3D11.Device.GetSupportedFeatureLevel(a))) .GroupBy(t => t.featureLevel) .OrderByDescending(t => t.Key) - .FirstOrDefault() + .First() .Select(k => k.adapter); - _adapter4 = null!; + _adapter4 = null; foreach (Adapter adapter in adapters) { using (adapter) @@ -299,7 +334,7 @@ Device CreateDevice(in GameGraphicsParameters parameters) Console.WriteLine($"Scaling:\t\t{modeDescription.Scaling}"); Console.WriteLine($"ScanlineOrdering:\t{modeDescription.ScanlineOrdering}"); Console.WriteLine(); - Console.WriteLine($"DeviceName:\t\t{_output6!.Description.DeviceName}"); + Console.WriteLine($"DeviceName:\t\t{_output6.Description.DeviceName}"); Console.WriteLine( $"DesktopBounds:\t\t{_output6.Description.DesktopBounds.Left};{_output6.Description.DesktopBounds.Top};{_output6.Description.DesktopBounds.Right};{_output6.Description.DesktopBounds.Bottom}"); Console.WriteLine($"MonitorHandle:\t\t{_output6.Description.MonitorHandle}"); @@ -351,7 +386,7 @@ Device CreateDevice(in GameGraphicsParameters parameters) _dxgiFactory.MakeWindowAssociation(parameters.Handle, parameters.WindowAssociationFlags); - _swapChain4!.ResizeTarget(ref modeDescription); + _swapChain4.ResizeTarget(ref modeDescription); SetFullscreenState(parameters.DisplayType == DisplayType.Fullscreen); @@ -365,6 +400,12 @@ Device CreateDevice(in GameGraphicsParameters parameters) Resize(_resizeParameters); + _blendStates = new BlendStates(this); + _depthStencilStates = new DepthStencilStates(this); + _rasterizerStates = new RasterizerStates(this); + _samplerStates = new SamplerStates(this); + _textures = new Textures(this); + IsInitialized = true; } @@ -373,9 +414,9 @@ private void Resize(ResizeParameters args) Utilities.Dispose(ref _renderView1); Utilities.Dispose(ref _depthStencilView); - _d3DDeviceContext!.ClearState(); + _d3DDeviceContext.ClearState(); - _swapChain4!.ResizeBuffers( + _swapChain4.ResizeBuffers( args.BufferCount, args.Width, args.Height, Format.Unknown, args.SwapChainFlags); using (Texture2D backBuffer = _swapChain4.GetBackBuffer(0)) @@ -410,7 +451,7 @@ private void Resize(ResizeParameters args) }); } - _d3DDeviceContext!.Rasterizer.SetViewport(Viewport = new Viewport(0, 0, args.Width, args.Height)); + _d3DDeviceContext.Rasterizer.SetViewport(Viewport = new Viewport(0, 0, args.Width, args.Height)); SetRenderTarget(null); @@ -453,6 +494,13 @@ private void Dispose(bool disposing) { if (disposing) { + _blendStates.Dispose(); + _depthStencilStates.Dispose(); + _rasterizerStates.Dispose(); + _samplerStates.Dispose(); + + _textures.Dispose(); + Utilities.Dispose(ref _depthStencilView); Utilities.Dispose(ref _renderView1); Utilities.Dispose(ref _dxgiDevice4); diff --git a/src/Exomia.Framework/Graphics/IGraphicsDevice.cs b/src/Exomia.Framework/Graphics/IGraphicsDevice.cs index 0b71c9b5..618f274d 100644 --- a/src/Exomia.Framework/Graphics/IGraphicsDevice.cs +++ b/src/Exomia.Framework/Graphics/IGraphicsDevice.cs @@ -41,7 +41,7 @@ public interface IGraphicsDevice : IDisposable /// /// The adapter. /// - Adapter4 Adapter { get; } + Adapter4? Adapter { get; } /// /// Gets the device. @@ -107,6 +107,46 @@ public interface IGraphicsDevice : IDisposable /// bool VSync { get; set; } + /// + /// Gets the for this graphics device. + /// + /// + /// The blend states. + /// + BlendStates BlendStates { get; } + + /// + /// Gets the for this graphics device. + /// + /// + /// The depth stencil states. + /// + DepthStencilStates DepthStencilStates { get; } + + /// + /// Gets the for this graphics device. + /// + /// + /// The rasterizer states. + /// + RasterizerStates RasterizerStates { get; } + + /// + /// Gets the for this graphics device. + /// + /// + /// The sample states. + /// + SamplerStates SamplerStates { get; } + + /// + /// Gets the for this graphics device. + /// + /// + /// The textures. + /// + Textures Textures { get; } + /// /// Clears this object to its blank/initial state. /// diff --git a/src/Exomia.Framework/Graphics/RasterizerStates.cs b/src/Exomia.Framework/Graphics/RasterizerStates.cs new file mode 100644 index 00000000..ad6899fc --- /dev/null +++ b/src/Exomia.Framework/Graphics/RasterizerStates.cs @@ -0,0 +1,162 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using SharpDX.Direct3D11; + +namespace Exomia.Framework.Graphics +{ + /// + /// The built-in rasterizer states. This class cannot be inherited. + /// + public sealed class RasterizerStates : IDisposable + { + /// + /// Built-in rasterizer state object with settings for wireframe rendering. + /// + public readonly RasterizerState WireFrame; + + /// + /// Built-in rasterizer state object with settings for wireframe rendering. + /// + public readonly RasterizerState WireFrameCullNone; + + /// + /// Built-in rasterizer state object with settings for culling primitives with clockwise winding order (front facing). + /// + public readonly RasterizerState CullFront; + + /// + /// Built-in rasterizer state object with settings for culling primitives with counter-clockwise winding order (back + /// facing). + /// + public readonly RasterizerState CullBack; + + /// + /// Built-in rasterizer state object with settings for not culling any primitives. + /// + public readonly RasterizerState CullNone; + + /// + /// Built-in rasterizer state object with settings and depth clip off. + /// + public readonly RasterizerState CullBackDepthClipOff; + + /// + /// Built-in rasterizer state object with settings and scissor enabled. + /// + public readonly RasterizerState CullBackScissorEnabled; + + /// + /// Built-in rasterizer state object with settings and + /// . + /// + public readonly RasterizerState CullBackDepthClipOffScissorEnabled; + + /// + /// Built-in rasterizer state object with settings and multi sample enabled. + /// + public readonly RasterizerState CullBackDepthClipOffMultiSampleOn; + + /// + /// Initializes a new instance of the class. + /// + /// The graphics device. + internal RasterizerStates(IGraphicsDevice graphicsDevice) + { + WireFrame = Create( + graphicsDevice.Device, nameof(WireFrame), FillMode.Wireframe, CullMode.Back, true, false, false); + WireFrameCullNone = Create( + graphicsDevice.Device, nameof(WireFrameCullNone), FillMode.Wireframe, CullMode.None, true, false, + false); + CullFront = Create( + graphicsDevice.Device, nameof(CullFront), FillMode.Solid, CullMode.Front, true, false, false); + CullBack = Create( + graphicsDevice.Device, nameof(CullBack), FillMode.Solid, CullMode.Back, true, false, false); + CullNone = Create( + graphicsDevice.Device, nameof(CullNone), FillMode.Solid, CullMode.None, true, false, false); + CullBackDepthClipOff = Create( + graphicsDevice.Device, nameof(CullBack), FillMode.Solid, CullMode.Back, false, false, false); + CullBackScissorEnabled = Create( + graphicsDevice.Device, nameof(CullBack), FillMode.Solid, CullMode.Back, true, true, false); + CullBackDepthClipOffScissorEnabled = Create( + graphicsDevice.Device, nameof(CullBack), FillMode.Solid, CullMode.Back, true, true, false); + CullBackDepthClipOffMultiSampleOn = Create( + graphicsDevice.Device, nameof(CullBack), FillMode.Solid, CullMode.Back, false, false, true); + } + + private static RasterizerState Create(Device5 device, + string name, + FillMode fillMode, + CullMode cullMode, + bool depthClipEnabled, + bool scissorEnabled, + bool multiSampleEnabled) + { + return new RasterizerState( + device, + new RasterizerStateDescription + { + FillMode = fillMode, + CullMode = cullMode, + IsFrontCounterClockwise = false, + DepthBias = 0, + DepthBiasClamp = 0, + SlopeScaledDepthBias = 0, + IsDepthClipEnabled = depthClipEnabled, + IsScissorEnabled = scissorEnabled, + IsMultisampleEnabled = multiSampleEnabled, + IsAntialiasedLineEnabled = multiSampleEnabled + }) { DebugName = name }; + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + WireFrame.Dispose(); + WireFrameCullNone.Dispose(); + CullFront.Dispose(); + CullBack.Dispose(); + CullNone.Dispose(); + CullBackDepthClipOff.Dispose(); + CullBackScissorEnabled.Dispose(); + CullBackDepthClipOffScissorEnabled.Dispose(); + CullBackDepthClipOffMultiSampleOn.Dispose(); + } + + _disposed = true; + } + } + + /// + ~RasterizerStates() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SamplerStates.cs b/src/Exomia.Framework/Graphics/SamplerStates.cs new file mode 100644 index 00000000..b4bb3763 --- /dev/null +++ b/src/Exomia.Framework/Graphics/SamplerStates.cs @@ -0,0 +1,156 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using SharpDX; +using SharpDX.Direct3D11; + +namespace Exomia.Framework.Graphics +{ + /// + /// The built-in sampler states. This class cannot be inherited. + /// + public sealed class SamplerStates : IDisposable + { + /// + /// Point filtering with texture coordinate wrapping. + /// + public readonly SamplerState PointWrap; + + /// + /// Point filtering with texture coordinate clamping. + /// + public readonly SamplerState PointClamp; + + /// + /// Point filtering with texture coordinate mirroring. + /// + public readonly SamplerState PointMirror; + + /// + /// Linear filtering with texture coordinate wrapping. + /// + public readonly SamplerState LinearWrap; + + /// + /// Linear filtering with texture coordinate clamping. + /// + public readonly SamplerState LinearClamp; + + /// + /// Linear filtering with texture coordinate mirroring. + /// + public readonly SamplerState LinearMirror; + + /// + /// Anisotropic filtering with texture coordinate wrapping. + /// + public readonly SamplerState AnisotropicWrap; + + /// + /// Anisotropic filtering with texture coordinate clamping. + /// + public readonly SamplerState AnisotropicClamp; + + /// + /// Anisotropic filtering with texture coordinate mirroring. + /// + public readonly SamplerState AnisotropicMirror; + + /// + /// Initializes a new instance of the class. + /// + /// The graphics device. + internal SamplerStates(IGraphicsDevice graphicsDevice) + { + PointWrap = Create( + graphicsDevice.Device, nameof(PointWrap), Filter.MinMagMipPoint, TextureAddressMode.Wrap); + PointClamp = Create( + graphicsDevice.Device, nameof(PointClamp), Filter.MinMagMipPoint, TextureAddressMode.Clamp); + PointMirror = Create( + graphicsDevice.Device, nameof(PointMirror), Filter.MinMagMipPoint, TextureAddressMode.Mirror); + LinearWrap = Create( + graphicsDevice.Device, nameof(LinearWrap), Filter.MinMagMipLinear, TextureAddressMode.Wrap); + LinearClamp = Create( + graphicsDevice.Device, nameof(LinearClamp), Filter.MinMagMipLinear, TextureAddressMode.Clamp); + LinearMirror = Create( + graphicsDevice.Device, nameof(LinearMirror), Filter.MinMagMipLinear, TextureAddressMode.Mirror); + AnisotropicWrap = Create( + graphicsDevice.Device, nameof(AnisotropicWrap), Filter.Anisotropic, TextureAddressMode.Wrap); + AnisotropicClamp = Create( + graphicsDevice.Device, nameof(AnisotropicClamp), Filter.Anisotropic, TextureAddressMode.Clamp); + AnisotropicMirror = Create( + graphicsDevice.Device, nameof(AnisotropicMirror), Filter.Anisotropic, TextureAddressMode.Mirror); + } + + private static SamplerState Create(Device5 device, + string name, + Filter filter, + TextureAddressMode textureAddressMode) + { + return new SamplerState( + device, + new SamplerStateDescription + { + AddressU = textureAddressMode, + AddressV = textureAddressMode, + AddressW = textureAddressMode, + BorderColor = Color.White, + ComparisonFunction = Comparison.Never, + Filter = filter, + MaximumAnisotropy = 16, + MaximumLod = float.MaxValue, + MinimumLod = float.MinValue, + MipLodBias = 0.0f + }) { DebugName = name }; + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + PointWrap.Dispose(); + PointClamp.Dispose(); + PointMirror.Dispose(); + LinearWrap.Dispose(); + LinearClamp.Dispose(); + LinearMirror.Dispose(); + AnisotropicWrap.Dispose(); + AnisotropicClamp.Dispose(); + AnisotropicMirror.Dispose(); + } + _disposed = true; + } + } + + /// + ~SamplerStates() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/Shader/Shader.cs b/src/Exomia.Framework/Graphics/Shader/Shader.cs index 09a7c58d..271a1b66 100644 --- a/src/Exomia.Framework/Graphics/Shader/Shader.cs +++ b/src/Exomia.Framework/Graphics/Shader/Shader.cs @@ -13,9 +13,11 @@ using System.Linq; using System.Runtime.CompilerServices; using Exomia.Framework.Content; +using Exomia.Framework.Mathematics; using SharpDX; using SharpDX.D3DCompiler; using SharpDX.Direct3D11; +using SharpDX.DXGI; namespace Exomia.Framework.Graphics.Shader { @@ -61,254 +63,354 @@ public enum Type ComputeShader } - private readonly Dictionary _techniques; + private readonly Dictionary _groups; /// - /// Specify the technique to get + /// Specify the group to get. /// - /// The technique name. + /// The group name. /// - /// The . + /// The . /// - public Technique this[string name] + public Group this[string name] { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return GetTechnique(name); } + get { return GetGroup(name); } } /// /// Initializes a new instance of the class. /// - /// The techniques. - internal Shader( - IEnumerable<(string technique, IEnumerable<(Type, ComObject, ShaderSignature)> passes)> techniques) + /// The shader groups. + internal Shader(IEnumerable<(string name, IEnumerable<(Type, ComObject, ShaderSignature, ShaderReflection)>)> + groups) { - _techniques = new Dictionary(StringComparer.InvariantCultureIgnoreCase); - foreach ((string technique, IEnumerable<(Type, ComObject, ShaderSignature)> passes) in techniques) + _groups = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + foreach ((string name, + IEnumerable<(Type, ComObject, ShaderSignature, ShaderReflection)> entries) in groups) { - _techniques.Add(technique, new Technique(passes)); + _groups.Add(name, new Group(entries)); } } /// - /// Gets all technique names from this shader instance. + /// Gets all group names from this shader instance. /// /// - /// An array of technique names. + /// An array of group names. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string[] GetTechniqueNames() + public string[] GetGroupNames() { - return _techniques.Keys.ToArray(); + return _groups.Keys.ToArray(); } /// - /// Attempts to get a from the given . + /// Attempts to get a from the given . /// - /// The technique name. - /// [out] The technique. + /// The group name. + /// [out] The . /// /// True if it succeeds, false if it fails. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryGetTechnique(string name, out Technique technique) + public bool TryGetGroup(string name, out Group group) { - return _techniques.TryGetValue(name, out technique); + return _groups.TryGetValue(name, out group); } /// - /// Attempts to get a from the given . + /// Attempts to get a from the given . /// - /// The technique name. + /// The group name. /// - /// The . + /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Technique GetTechnique(string name) + public Group GetGroup(string name) { - return _techniques[name]; + return _groups[name]; } /// - /// A technique. This class cannot be inherited. + /// A shader group collection. This class cannot be inherited. /// - public sealed class Technique + public sealed class Group { - private readonly Dictionary _passes; + private readonly + Dictionary _entries; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The passes. - internal Technique(IEnumerable<(Type type, ComObject comObject, ShaderSignature signature)> passes) + /// The entries. + internal Group(IEnumerable<(Type, ComObject, ShaderSignature, ShaderReflection)> entries) { - _passes = new Dictionary(); - foreach ((Type type, ComObject comObject, ShaderSignature signature) in passes) + _entries = new Dictionary(); + foreach ((Type type, ComObject comObject, ShaderSignature signature, + ShaderReflection reflection) in entries) { - _passes.Add(type, (comObject, signature)); + _entries.Add(type, (comObject, signature, reflection)); } } /// - /// Implicit converts the given Shader to a . + /// Gets a . /// - /// The technique. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator VertexShader(Technique technique) + public VertexShader GetVertexShader() { - return technique.GetVertexShader(); + return (VertexShader)_entries[Type.VertexShader].shader; } /// - /// Implicit converts the given Shader to a . + /// Gets a . /// - /// The technique. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator PixelShader(Technique technique) + public PixelShader GetPixelShader() { - return technique.GetPixelShader(); + return (PixelShader)_entries[Type.PixelShader].shader; } /// - /// Implicit converts the given Shader to a . + /// Gets a . /// - /// The technique. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator DomainShader(Technique technique) + public DomainShader GetDomainShader() { - return technique.GetDomainShader(); + return (DomainShader)_entries[Type.DomainShader].shader; } /// - /// Implicit converts the given Shader to a . + /// Gets a . /// - /// The technique. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator GeometryShader(Technique technique) + public GeometryShader GetGeometryShader() { - return technique.GetGeometryShader(); + return (GeometryShader)_entries[Type.GeometryShader].shader; } /// - /// Implicit converts the given Shader to a . + /// Gets a . /// - /// The technique. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator HullShader(Technique technique) + public HullShader GetHullShader() { - return technique.GetHullShader(); + return (HullShader)_entries[Type.HullShader].shader; } /// - /// Implicit converts the given Shader to a . + /// Gets a . /// - /// The technique. /// /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator ComputeShader(Technique technique) + public ComputeShader GetComputeShader() { - return technique.GetComputeShader(); + return (ComputeShader)_entries[Type.ComputeShader].shader; } /// - /// Gets a . + /// Gets the . /// + /// The type. /// - /// The . + /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public VertexShader GetVertexShader() + public ShaderSignature GetShaderSignature(Type type) { - return (VertexShader)_passes[Type.VertexShader].shader; + return _entries[type].signature; } /// - /// Gets a . + /// Gets the . /// + /// The type. /// - /// The . + /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public PixelShader GetPixelShader() + public ShaderReflection GetShaderReflection(Type type) { - return (PixelShader)_passes[Type.PixelShader].shader; + return _entries[type].reflection; } /// - /// Gets a . + /// Creates input layout for the specified . /// + /// The graphics device. + /// The type. /// - /// The . + /// The new input layout. /// + /// Thrown when one or more required arguments are null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DomainShader GetDomainShader() + public InputLayout CreateInputLayout(IGraphicsDevice graphicsDevice, Type type) { - return (DomainShader)_passes[Type.DomainShader].shader; + return new InputLayout( + (graphicsDevice ?? throw new ArgumentNullException(nameof(graphicsDevice))).Device, + GetShaderSignature(type), + CreateInputElements(type)); } /// - /// Gets a . + /// Creates input elements for the specified . /// + /// The type. /// - /// The . + /// A new array of input element. + /// + /// Thrown when one or more arguments are outside the required range. + public InputElement[] CreateInputElements(Type type) + { + ShaderReflection shaderReflection = GetShaderReflection(type); + + InputElement[] elements = new InputElement[shaderReflection.Description.InputParameters]; + for (int i = 0; i < shaderReflection.Description.InputParameters; i++) + { + ShaderParameterDescription description = shaderReflection.GetInputParameterDescription(i); + + Format format = Math2.CountOnes((int)description.UsageMask) switch + { + // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault + 1 => description.ComponentType switch + { + RegisterComponentType.UInt32 => Format.R32_UInt, + RegisterComponentType.SInt32 => Format.R32_SInt, + RegisterComponentType.Float32 => Format.R32_Float, + _ => throw new ArgumentOutOfRangeException(nameof(description.ComponentType)) + }, + + // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault + 2 => description.ComponentType switch + { + RegisterComponentType.UInt32 => Format.R32G32_UInt, + RegisterComponentType.SInt32 => Format.R32G32_SInt, + RegisterComponentType.Float32 => Format.R32G32_Float, + _ => throw new ArgumentOutOfRangeException(nameof(description.ComponentType)) + }, + + // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault + 3 => description.ComponentType switch + { + RegisterComponentType.UInt32 => Format.R32G32B32_UInt, + RegisterComponentType.SInt32 => Format.R32G32B32_SInt, + RegisterComponentType.Float32 => Format.R32G32B32_Float, + _ => throw new ArgumentOutOfRangeException(nameof(description.ComponentType)) + }, + + // ReSharper disable once SwitchExpressionHandlesSomeKnownEnumValuesWithExceptionInDefault + 4 => description.ComponentType switch + { + RegisterComponentType.UInt32 => Format.R32G32B32A32_UInt, + RegisterComponentType.SInt32 => Format.R32G32B32A32_SInt, + RegisterComponentType.Float32 => Format.R32G32B32A32_Float, + _ => throw new ArgumentOutOfRangeException(nameof(description.ComponentType)) + }, + _ => throw new ArgumentOutOfRangeException(nameof(description.UsageMask)) + }; + + elements[i] = new InputElement( + description.SemanticName, description.SemanticIndex, format, + InputElement.AppendAligned, 0, InputClassification.PerVertexData, 0); + } + + return elements; + } + + /// + /// Implicit converts the given Shader to a . + /// + /// The . + /// + /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GeometryShader GetGeometryShader() + public static implicit operator VertexShader(Group group) { - return (GeometryShader)_passes[Type.GeometryShader].shader; + return group.GetVertexShader(); } /// - /// Gets a . + /// Implicit converts the given Shader to a . /// + /// The . /// - /// The . + /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public HullShader GetHullShader() + public static implicit operator PixelShader(Group group) { - return (HullShader)_passes[Type.HullShader].shader; + return group.GetPixelShader(); } /// - /// Gets a . + /// Implicit converts the given Shader to a . /// + /// The . /// - /// The . + /// The . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ComputeShader GetComputeShader() + public static implicit operator DomainShader(Group group) { - return (ComputeShader)_passes[Type.ComputeShader].shader; + return group.GetDomainShader(); } /// - /// Gets the . + /// Implicit converts the given Shader to a . /// - /// The type. + /// The . /// - /// The . + /// The . /// - public ShaderSignature GetShaderSignature(Type type) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator GeometryShader(Group group) { - return _passes[type].signature; + return group.GetGeometryShader(); + } + + /// + /// Implicit converts the given Shader to a . + /// + /// The . + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator HullShader(Group group) + { + return group.GetHullShader(); + } + + /// + /// Implicit converts the given Shader to a . + /// + /// The . + /// + /// The . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator ComputeShader(Group group) + { + return group.GetComputeShader(); } #region IDisposable Support @@ -319,21 +421,22 @@ private void Dispose(bool disposing) { if (!_disposed) { - foreach (KeyValuePair keyValuePair in _passes) - { - keyValuePair.Value.shader.Dispose(); - keyValuePair.Value.signature.Dispose(); - } if (disposing) { - _passes.Clear(); + foreach (var (shader, signature, reflection) in _entries.Values) + { + reflection.Dispose(); + signature.Dispose(); + shader.Dispose(); + } + _entries.Clear(); } _disposed = true; } } /// - ~Technique() + ~Group() { Dispose(false); } @@ -358,13 +461,13 @@ private void Dispose(bool disposing) { if (!_disposed) { - foreach (KeyValuePair keyValuePair in _techniques) - { - keyValuePair.Value.Dispose(); - } if (disposing) { - _techniques.Clear(); + foreach (Group group in _groups.Values) + { + group.Dispose(); + } + _groups.Clear(); } _disposed = true; } diff --git a/src/Exomia.Framework/Graphics/Shader/ShaderFileLoader.cs b/src/Exomia.Framework/Graphics/Shader/ShaderFileLoader.cs index f7af2147..5aab1a52 100644 --- a/src/Exomia.Framework/Graphics/Shader/ShaderFileLoader.cs +++ b/src/Exomia.Framework/Graphics/Shader/ShaderFileLoader.cs @@ -29,8 +29,8 @@ static class ShaderFileLoader "^\\s*\\**\\s*$", RegexOptions.Compiled | RegexOptions.Singleline); - private static readonly Regex s_techniqueRegex = new Regex( - "^\\s*\\*\\s*technique\\s*(.*)$", + private static readonly Regex s_groupRegex = new Regex( + "^\\s*\\*\\s*group\\s*(.*)$", RegexOptions.Compiled | RegexOptions.Singleline); private static readonly Regex s_shaderInfoRegex = new Regex( @@ -45,10 +45,10 @@ static class ShaderFileLoader return null; } - IList techniques = new List(1); + IList groups = new List(1); - Technique? currentTechnique = null; - string? line; + Group? currentGroup = null; + string? line; while ((line = sr.ReadLine()?.Trim()) != null) { if (SHADER_DEFINITION_END.Equals(line, StringComparison.InvariantCultureIgnoreCase)) @@ -58,10 +58,10 @@ static class ShaderFileLoader if (!s_emptyLineRegex.IsMatch(line)) { - Match techniqueMatch = s_techniqueRegex.Match(line); - if (techniqueMatch.Success) + Match groupMatch = s_groupRegex.Match(line); + if (groupMatch.Success) { - techniques.Add(currentTechnique = new Technique(techniqueMatch.Groups[1].Value)); + groups.Add(currentGroup = new Group(groupMatch.Groups[1].Value)); continue; } @@ -71,9 +71,9 @@ static class ShaderFileLoader if (!Enum.TryParse(shaderInfoMatch.Groups[4].Value, out ShaderFlags flags)) { throw new InvalidDataException( - $"pass shader flags '{shaderInfoMatch.Groups[4].Value}' one or more flags are invalid or unsupported."); + $"shader flags '{shaderInfoMatch.Groups[4].Value}' one or more flags are invalid or unsupported."); } - currentTechnique?.Add( + currentGroup?.Add( new ShaderInfo( shaderInfoMatch.Groups[1].Value, shaderInfoMatch.Groups[2].Value, @@ -86,7 +86,7 @@ static class ShaderFileLoader string shaderSource = sr.ReadToEnd(); return new Shader( - techniques.Select( + groups.Select( t => { return (t.Name, @@ -103,24 +103,30 @@ static class ShaderFileLoader { "vs" => (Shader.Type.VertexShader, (ComObject)new VertexShader(graphicsDevice.Device, cr), - ShaderSignature.GetInputSignature(cr)), + ShaderSignature.GetInputSignature(cr), + new ShaderReflection(cr)), "ps" => (Shader.Type.PixelShader, (ComObject)new PixelShader(graphicsDevice.Device, cr), - ShaderSignature.GetInputSignature(cr)), + ShaderSignature.GetInputSignature(cr), + new ShaderReflection(cr)), "ds" => (Shader.Type.DomainShader, (ComObject)new DomainShader(graphicsDevice.Device, cr), - ShaderSignature.GetInputSignature(cr)), + ShaderSignature.GetInputSignature(cr), + new ShaderReflection(cr)), "gs" => (Shader.Type.GeometryShader, (ComObject)new GeometryShader(graphicsDevice.Device, cr), - ShaderSignature.GetInputSignature(cr)), + ShaderSignature.GetInputSignature(cr), + new ShaderReflection(cr)), "hs" => (Shader.Type.HullShader, (ComObject)new HullShader(graphicsDevice.Device, cr), - ShaderSignature.GetInputSignature(cr)), + ShaderSignature.GetInputSignature(cr), + new ShaderReflection(cr)), "cs" => (Shader.Type.ComputeShader, (ComObject)new ComputeShader(graphicsDevice.Device, cr), - ShaderSignature.GetInputSignature(cr)), + ShaderSignature.GetInputSignature(cr), + new ShaderReflection(cr)), _ => throw new InvalidDataException( - $"pass shader type '{s.Type}' doesn't exists or is unsupported.") + $"shader type '{s.Type}' doesn't exists or is unsupported.") }; }) ); @@ -128,9 +134,9 @@ static class ShaderFileLoader } /// - /// A technique. This class cannot be inherited. + /// A group. This class cannot be inherited. /// - private sealed class Technique + private sealed class Group { /// /// Gets the name. @@ -149,10 +155,10 @@ private sealed class Technique public IList ShaderInfos { get; } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The name. - public Technique(string name) + public Group(string name) { Name = name; ShaderInfos = new List(2); diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.Arc.cs b/src/Exomia.Framework/Graphics/SpriteBatch.Arc.cs new file mode 100644 index 00000000..3933d096 --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteBatch.Arc.cs @@ -0,0 +1,119 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using Exomia.Framework.Mathematics; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteBatch + { + /// + /// Draws a circle. + /// + /// The center. + /// The radius. + /// The color. + /// The width of the line. + /// The opacity. + /// The segments. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawCircle(in Vector2 center, + float radius, + in Color color, + float lineWidth, + float opacity, + int segments, + float layerDepth) + { + DrawArc(new Arc2(center, radius), color, lineWidth, opacity, segments, layerDepth); + } + + /// + /// Draws a circle. + /// + /// The circle. + /// The color. + /// The width of the line. + /// The opacity. + /// The segments. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawCircle(in Circle2 circle, + in Color color, + float lineWidth, + float opacity, + int segments, + float layerDepth) + { + DrawArc(new Arc2(circle.X, circle.Y, circle.Radius), color, lineWidth, opacity, segments, layerDepth); + } + + /// + /// Draws a circle. + /// + /// The center. + /// The radius. + /// The start. + /// The end. + /// The color. + /// The width of the line. + /// The opacity. + /// The segments. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawArc(in Vector2 center, + float radius, + float start, + float end, + in Color color, + float lineWidth, + float opacity, + int segments, + float layerDepth) + { + DrawArc(new Arc2(center, radius, start, end), in color, lineWidth, opacity, segments, layerDepth); + } + + /// + /// Draws a circle. + /// + /// The arc. + /// The color. + /// The width of the line. + /// The opacity. + /// The segments. + /// The depth of the layer. + public void DrawArc(in Arc2 arc, + in Color color, + float lineWidth, + float opacity, + int segments, + float layerDepth) + { + Vector2[] vertex = new Vector2[segments]; + + float increment = (arc.End - arc.Start) / segments; + float theta = arc.Start; + + for (int i = 0; i < segments; i++) + { + vertex[i].X = arc.X + (arc.Radius * (float)Math.Cos(theta)); + vertex[i].Y = arc.Y + (arc.Radius * (float)Math.Sin(theta)); + theta += increment; + } + + DrawPolygon(vertex, color, lineWidth, opacity, layerDepth); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.Line.cs b/src/Exomia.Framework/Graphics/SpriteBatch.Line.cs new file mode 100644 index 00000000..a7e1128a --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteBatch.Line.cs @@ -0,0 +1,106 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using Exomia.Framework.Mathematics; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteBatch + { + /// + /// Draw line. + /// + /// The first point. + /// The second point. + /// The color. + /// The width of the line. + /// The opacity. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawLine(in Vector2 point1, + in Vector2 point2, + in Color color, + float lineWidth, + float opacity, + float layerDepth) + { + DrawLine(point1, point2, color, lineWidth, opacity, 1.0f, layerDepth); + } + + /// + /// Draw line. + /// + /// The line. + /// The color. + /// The width of the line. + /// The opacity. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawLine(in Line2 line, + in Color color, + float lineWidth, + float opacity, + float layerDepth) + { + DrawLine(in line, color, lineWidth, opacity, 1.0f, layerDepth); + } + + /// + /// Draw line. + /// + /// The first point. + /// The second point. + /// The color. + /// The width of the line. + /// The opacity. + /// The length factor. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawLine(in Vector2 point1, + in Vector2 point2, + in Color color, + float lineWidth, + float opacity, + float lengthFactor, + float layerDepth) + { + DrawLine(new Line2(in point1, in point2), color, lineWidth, opacity, lengthFactor, layerDepth); + } + + /// + /// Draw line. + /// + /// The line. + /// The color. + /// The width of the line. + /// The opacity. + /// The length factor. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawLine(in Line2 line, + in Color color, + float lineWidth, + float opacity, + float lengthFactor, + float layerDepth) + { + float dx = line.X2 - line.X1; + float dy = line.Y2 - line.Y1; + DrawSprite( + _whiteTexture, new RectangleF( + line.X1, line.Y1, (float)Math.Sqrt((dx * dx) + (dy * dy)) * lengthFactor, lineWidth), false, + s_nullRectangle, color, (float)Math.Atan2(dy, dx), + s_vector2Zero, opacity, TextureEffects.None, layerDepth); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.Polygon.cs b/src/Exomia.Framework/Graphics/SpriteBatch.Polygon.cs new file mode 100644 index 00000000..48ce9c1b --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteBatch.Polygon.cs @@ -0,0 +1,38 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteBatch + { + /// + /// Draw polygon. + /// + /// The vertex. + /// The color. + /// The width of the line. + /// The opacity. + /// The depth of the layer. + public void DrawPolygon(Vector2[] vertex, in Color color, float lineWidth, float opacity, float layerDepth) + { + if (vertex.Length > 1) + { + int l = vertex.Length - 1; + for (int i = 0; i < l; i++) + { + DrawLine(vertex[i], vertex[i + 1], color, lineWidth, opacity, layerDepth); + } + DrawLine(vertex[l], vertex[0], color, lineWidth, opacity, layerDepth); + } + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.Rectangle.cs b/src/Exomia.Framework/Graphics/SpriteBatch.Rectangle.cs new file mode 100644 index 00000000..f3c6a70e --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteBatch.Rectangle.cs @@ -0,0 +1,157 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteBatch + { + /// + /// Draw rectangle. + /// + /// The destination rectangle. + /// The color. + /// The width of the line. + /// The rotation. + /// The opacity. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawRectangle(in RectangleF destinationRectangle, + in Color color, + float lineWidth, + float rotation, + float opacity, + float layerDepth) + { + DrawRectangle(destinationRectangle, color, lineWidth, rotation, s_vector2Zero, opacity, layerDepth); + } + + /// + /// Draw rectangle. + /// + /// The destination rectangle. + /// The color. + /// The width of the line. + /// The rotation. + /// The origin. + /// The opacity. + /// The depth of the layer. + public void DrawRectangle(in RectangleF destinationRectangle, + in Color color, + float lineWidth, + float rotation, + in Vector2 origin, + float opacity, + float layerDepth) + { + Vector2[] vertex; + + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (rotation == 0.0f) + { + vertex = new[] + { + destinationRectangle.TopLeft, destinationRectangle.TopRight, destinationRectangle.BottomRight, + destinationRectangle.BottomLeft + }; + } + else + { + vertex = new Vector2[4]; + + Vector2 o = origin; + + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (destinationRectangle.Width != 0f) + { + o.X /= destinationRectangle.Width; + } + + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (destinationRectangle.Height != 0f) + { + o.Y /= destinationRectangle.Height; + } + + float cos = (float)Math.Cos(rotation); + float sin = (float)Math.Sin(rotation); + for (int j = 0; j < VERTICES_PER_SPRITE; j++) + { + Vector2 corner = s_cornerOffsets[j]; + float posX = (corner.X - o.X) * destinationRectangle.Width; + float posY = (corner.Y - o.Y) * destinationRectangle.Height; + + vertex[j] = new Vector2( + (destinationRectangle.X + (posX * cos)) - (posY * sin), + destinationRectangle.Y + (posX * sin) + (posY * cos)); + } + } + + DrawPolygon(vertex, color, lineWidth, opacity, layerDepth); + } + + /// + /// Draw fill rectangle. + /// + /// The destination rectangle. + /// The color. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawFillRectangle(in RectangleF destinationRectangle, in Color color, float layerDepth) + { + DrawSprite( + _whiteTexture, destinationRectangle, false, s_nullRectangle, + color, 0.0f, s_vector2Zero, 1.0f, TextureEffects.None, layerDepth); + } + + /// + /// Draw fill rectangle. + /// + /// The destination rectangle. + /// The color. + /// The opacity. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawFillRectangle(in RectangleF destinationRectangle, + in Color color, + float opacity, + float layerDepth) + { + DrawSprite( + _whiteTexture, destinationRectangle, false, s_nullRectangle, + color, 0.0f, s_vector2Zero, opacity, TextureEffects.None, layerDepth); + } + + /// + /// Draw fill rectangle. + /// + /// The destination rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawFillRectangle(in RectangleF destinationRectangle, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + float layerDepth) + { + DrawSprite( + _whiteTexture, destinationRectangle, false, s_nullRectangle, + color, rotation, origin, opacity, TextureEffects.None, layerDepth); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.SpriteFont.cs b/src/Exomia.Framework/Graphics/SpriteBatch.SpriteFont.cs new file mode 100644 index 00000000..b407a234 --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteBatch.SpriteFont.cs @@ -0,0 +1,229 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System.Runtime.CompilerServices; +using SharpDX; + +#if NETSTANDARD2_1 +using System; + +#endif + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteBatch + { + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The position. + /// The color. + /// The depth of the layer. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + in Vector2 position, + in Color color, + float layerDepth = 1.0f) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, string text, in Vector2 position, in Color color, float layerDepth = 1.0f) +#endif + { + font.Draw(DrawTextInternal, text, position, color, 0f, Vector2.Zero, 1.0f, TextureEffects.None, layerDepth); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The position. + /// The color. + /// The rotation. + /// The depth of the layer. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + in Vector2 position, + in Color color, + float rotation, + float layerDepth = 1.0f) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + string text, + in Vector2 position, + in Color color, + float rotation, + float layerDepth = 1.0f) +#endif + { + font.Draw( + DrawTextInternal, text, position, color, rotation, Vector2.Zero, 1.0f, TextureEffects.None, layerDepth); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The position. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. + /// The depth of the layer. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth = 1.0f) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + string text, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth = 1.0f) +#endif + { + font.Draw(DrawTextInternal, text, position, color, rotation, origin, opacity, effects, layerDepth); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The start. + /// The end. + /// The position. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. + /// The depth of the layer. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + int start, + int end, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth = 1.0f) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + string text, + int start, + int end, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth = 1.0f) +#endif + { + font.Draw( + DrawTextInternal, text, start, end, position, color, rotation, origin, opacity, effects, layerDepth); + } + + /// + /// Draw text. + /// + /// The font. + /// The text. + /// The start. + /// The end. + /// The position. + /// The dimension. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. + /// The depth of the layer. +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + ReadOnlySpan text, + int start, + int end, + in Vector2 position, + in Size2F dimension, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth = 1.0f) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void DrawText(SpriteFont font, + string text, + int start, + int end, + in Vector2 position, + in Size2F dimension, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth = 1.0f) +#endif + { + font.Draw( + DrawTextInternal, text, start, end, position, dimension, color, rotation, origin, opacity, effects, + layerDepth); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void DrawTextInternal(Texture texture, + in Vector2 position, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float scale, + float opacity, + TextureEffects effects, + float layerDepth) + { + DrawSprite( + texture, new RectangleF(position.X, position.Y, scale, scale), true, sourceRectangle, + color, rotation, origin, opacity, effects, layerDepth); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.Structs.cs b/src/Exomia.Framework/Graphics/SpriteBatch.Structs.cs new file mode 100644 index 00000000..43f4c94a --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteBatch.Structs.cs @@ -0,0 +1,81 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System.Runtime.InteropServices; +using SharpDX; +using SharpDX.Direct3D11; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteBatch + { + internal struct SpriteInfo + { + public RectangleF Source; + public RectangleF Destination; + public Vector2 Origin; + public float Rotation; + public float Depth; + public TextureEffects SpriteEffects; + public Color Color; + public float Opacity; + } + + internal readonly struct TextureInfo + { + public readonly ShaderResourceView View; + public readonly int Width; + public readonly int Height; + public readonly long Ptr64; + + public TextureInfo(ShaderResourceView view, int width, int height) + { + View = view; + Width = width; + Height = height; + Ptr64 = view.NativePointer.ToInt64(); + } + } + + [StructLayout(LayoutKind.Explicit, Size = VERTEX_STRIDE)] + private struct VertexPositionColorTexture + { + [FieldOffset(0)] + public float X; + + [FieldOffset(4)] + public float Y; + + [FieldOffset(8)] + public float Z; + + [FieldOffset(12)] + public float W; + + [FieldOffset(16)] + public float R; + + [FieldOffset(20)] + public float G; + + [FieldOffset(24)] + public float B; + + [FieldOffset(28)] + public float A; + + [FieldOffset(32)] + public float U; + + [FieldOffset(36)] + public float V; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.Texture.cs b/src/Exomia.Framework/Graphics/SpriteBatch.Texture.cs new file mode 100644 index 00000000..dcb506a7 --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteBatch.Texture.cs @@ -0,0 +1,338 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteBatch + { + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The position. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, in Vector2 position, in Color color) + { + DrawSprite( + texture, new RectangleF(position.X, position.Y, 1f, 1f), true, s_nullRectangle, + color, 0f, s_vector2Zero, 1.0f, TextureEffects.None, 0f); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The destination rectangle. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, in RectangleF destinationRectangle, in Color color) + { + DrawSprite( + texture, destinationRectangle, false, s_nullRectangle, + color, 0f, s_vector2Zero, 1.0f, TextureEffects.None, 0f); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The position. + /// The source rectangle. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, in Vector2 position, in Rectangle? sourceRectangle, in Color color) + { + DrawSprite( + texture, new RectangleF(position.X, position.Y, 1f, 1f), true, sourceRectangle, + color, 0f, s_vector2Zero, 1.0f, TextureEffects.None, 0f); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The destination rectangle. + /// The source rectangle. + /// The color. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in RectangleF destinationRectangle, + in Rectangle? sourceRectangle, + in Color color) + { + DrawSprite( + texture, destinationRectangle, false, sourceRectangle, + color, 0f, s_vector2Zero, 1.0f, TextureEffects.None, 0f); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The position. + /// The color. + /// The rotation. + /// The origin. + /// (Optional) The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float layerDepth = 0f) + { + DrawSprite( + texture, new RectangleF(position.X, position.Y, 1f, 1f), true, s_nullRectangle, + color, rotation, origin, 1.0f, TextureEffects.None, layerDepth); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The destination rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in RectangleF destinationRectangle, + in Color color, + float rotation, + in Vector2 origin, + float layerDepth = 0f) + { + DrawSprite( + texture, destinationRectangle, false, s_nullRectangle, + color, rotation, origin, 1.0f, TextureEffects.None, layerDepth); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The destination rectangle. + /// The source rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The opacity. + /// The effects. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in RectangleF destinationRectangle, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth) + { + DrawSprite( + texture, destinationRectangle, false, sourceRectangle, + color, rotation, origin, opacity, effects, layerDepth); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The position. + /// The source rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The scale. + /// The opacity. + /// The effects. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in Vector2 position, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float scale, + float opacity, + TextureEffects effects, + float layerDepth) + { + DrawSprite( + texture, new RectangleF(position.X, position.Y, scale, scale), true, sourceRectangle, + color, rotation, origin, opacity, effects, layerDepth); + } + + /// + /// Draws a texture to the screen. + /// + /// The texture. + /// The position. + /// The source rectangle. + /// The color. + /// The rotation. + /// The origin. + /// The scale. + /// The opacity. + /// The effects. + /// The depth of the layer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Draw(Texture texture, + in Vector2 position, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + in Vector2 scale, + float opacity, + TextureEffects effects, + float layerDepth) + { + DrawSprite( + texture, new RectangleF(position.X, position.Y, scale.X, scale.Y), true, sourceRectangle, + color, rotation, origin, opacity, effects, layerDepth); + } + + private unsafe void DrawSprite(Texture texture, + in RectangleF destination, + bool scaleDestination, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float depth) + { + if (!_isBeginCalled) + { + throw new InvalidOperationException("Begin must be called before draw"); + } + + if (texture.TexturePointer == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(texture)); + } + + if (_spriteQueueCount >= _spriteQueue.Length) + { + bool lockTaken = false; + try + { + _spinLock.Enter(ref lockTaken); + + int size = _spriteQueue.Length * 2; + _sortIndices = new int[size]; + _sortedSprites = new SpriteInfo[size]; + Array.Resize(ref _spriteQueue, size); + Array.Resize(ref _spriteTextures, size); + } + finally + { + if (lockTaken) + { + _spinLock.Exit(false); + } + } + } + + if (!_textureInfos.TryGetValue(texture.TexturePointer, out TextureInfo textureInfo)) + { + bool lockTaken = false; + try + { + _spinLock.Enter(ref lockTaken); + if (!_textureInfos.TryGetValue(texture.TexturePointer, out textureInfo)) + { + textureInfo = new TextureInfo(texture.TextureView, texture.Width, texture.Height); + _textureInfos.Add(texture.TexturePointer, textureInfo); + } + } + finally + { + if (lockTaken) + { + _spinLock.Exit(false); + } + } + } + + int spriteQueueCount = Interlocked.Increment(ref _spriteQueueCount) - 1; + fixed (SpriteInfo* spriteInfo = &_spriteQueue[spriteQueueCount]) + { + float width; + float height; + if (sourceRectangle.HasValue) + { + Rectangle rectangle = sourceRectangle.Value; + spriteInfo->Source.X = rectangle.X; + spriteInfo->Source.Y = rectangle.Y; + width = rectangle.Width; + height = rectangle.Height; + } + else + { + spriteInfo->Source.X = 0; + spriteInfo->Source.Y = 0; + width = texture.Width; + height = texture.Height; + } + + spriteInfo->Source.Width = width; + spriteInfo->Source.Height = height; + + spriteInfo->Destination.X = destination.X; + spriteInfo->Destination.Y = destination.Y; + + if (scaleDestination) + { + spriteInfo->Destination.Width = destination.Width * width; + spriteInfo->Destination.Height = destination.Height * height; + } + else + { + spriteInfo->Destination.Width = destination.Width; + spriteInfo->Destination.Height = destination.Height; + } + + if (spriteInfo->Destination.Width < 0) + { + spriteInfo->Destination.X += spriteInfo->Destination.Width; + spriteInfo->Destination.Width = -spriteInfo->Destination.Width; + } + + if (spriteInfo->Destination.Height < 0) + { + spriteInfo->Destination.Y += spriteInfo->Destination.Height; + spriteInfo->Destination.Height = -spriteInfo->Destination.Height; + } + + spriteInfo->Origin = origin; + spriteInfo->Rotation = rotation; + spriteInfo->Depth = depth; + spriteInfo->SpriteEffects = effects; + spriteInfo->Color = color; + spriteInfo->Opacity = opacity; + } + + _spriteTextures[spriteQueueCount] = textureInfo; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteBatch.cs b/src/Exomia.Framework/Graphics/SpriteBatch.cs index 1b95f6cc..35fa7e99 100644 --- a/src/Exomia.Framework/Graphics/SpriteBatch.cs +++ b/src/Exomia.Framework/Graphics/SpriteBatch.cs @@ -14,26 +14,22 @@ using System.Diagnostics; using System.IO; using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; using System.Threading; using System.Threading.Tasks; +using Exomia.Framework.Graphics.Buffers; using Exomia.Framework.Graphics.Shader; using Exomia.Framework.Graphics.SpriteSort; using Exomia.Framework.Resources; using SharpDX; using SharpDX.Direct3D; using SharpDX.Direct3D11; -using SharpDX.DXGI; -using Buffer = SharpDX.Direct3D11.Buffer; -using MapFlags = SharpDX.Direct3D11.MapFlags; namespace Exomia.Framework.Graphics { /// /// A sprite batch. This class cannot be inherited. /// - public sealed class SpriteBatch : IDisposable + public sealed partial class SpriteBatch : IDisposable { private const int MAX_BATCH_SIZE = 1 << 13; private const int INITIAL_QUEUE_SIZE = 1 << 7; @@ -58,24 +54,29 @@ private static readonly Vector2[] private readonly Dictionary _textureInfos = new Dictionary(INITIAL_QUEUE_SIZE); - private readonly VertexBufferBinding _vertexBufferBinding; - private readonly InputLayout _vertexInputLayout; + private readonly InputLayout _vertexInputLayout; - private readonly Buffer _vertexBuffer, _indexBuffer, _perFrameBuffer; + private readonly IndexBuffer _indexBuffer; + private readonly VertexBuffer _vertexBuffer; + private readonly ConstantBuffer _perFrameBuffer; private readonly Shader.Shader _shader; private readonly PixelShader _pixelShader; private readonly VertexShader _vertexShader; - - private BlendState? _defaultBlendState, _blendState; - private DepthStencilState? _defaultDepthStencilState, _depthStencilState; - - private RasterizerState? _defaultRasterizerState, - _defaultRasterizerScissorEnabledState, - _rasterizerState; - - private SamplerState? _defaultSamplerState, _samplerState; - private bool _isBeginCalled, _isScissorEnabled; + private readonly Texture _whiteTexture; + private readonly bool _center; + + private readonly BlendState _defaultBlendState; + private readonly DepthStencilState _defaultDepthStencilState; + private readonly RasterizerState _defaultRasterizerState; + private readonly RasterizerState _defaultRasterizerScissorEnabledState; + private readonly SamplerState _defaultSamplerState; + private BlendState? _blendState; + private DepthStencilState? _depthStencilState; + private RasterizerState? _rasterizerState; + private SamplerState? _samplerState; + + private bool _isBeginCalled, _isScissorEnabled; private Rectangle _scissorRectangle; private SpriteSortMode _spriteSortMode; private int[] _sortIndices; @@ -117,16 +118,22 @@ static SpriteBatch() /// /// Initializes a new instance of the class. /// + /// The graphics device. + /// (Optional) True to center the coordinate system in the viewport. + /// (Optional) The sort algorithm. /// /// Thrown when one or more arguments have unsupported or /// illegal values. /// - /// Zero-based index of the device. - /// (Optional) The sort algorithm. - public SpriteBatch(IGraphicsDevice iDevice, SpriteSortAlgorithm sortAlgorithm = SpriteSortAlgorithm.MergeSort) + /// Thrown when a value was unexpectedly null. + public SpriteBatch(IGraphicsDevice graphicsDevice, + bool center = false, + SpriteSortAlgorithm sortAlgorithm = SpriteSortAlgorithm.MergeSort) { - _device = iDevice.Device; - _context = iDevice.DeviceContext; + _device = graphicsDevice.Device; + _context = graphicsDevice.DeviceContext; + + _center = center; _spriteSort = sortAlgorithm switch { @@ -134,41 +141,33 @@ public SpriteBatch(IGraphicsDevice iDevice, SpriteSortAlgorithm sortAlgorithm = _ => throw new ArgumentException($"invalid sort algorithm ({sortAlgorithm})", nameof(sortAlgorithm)) }; - InitializeStates(iDevice.Device); - DefaultTextures.InitializeTextures(iDevice.Device); + _defaultBlendState = graphicsDevice.BlendStates.AlphaBlend; + _defaultSamplerState = graphicsDevice.SamplerStates.LinearWrap; + _defaultDepthStencilState = graphicsDevice.DepthStencilStates.None; + _defaultRasterizerState = graphicsDevice.RasterizerStates.CullBackDepthClipOff; + _defaultRasterizerScissorEnabledState = graphicsDevice.RasterizerStates.CullBackDepthClipOffScissorEnabled; - _indexBuffer = Buffer.Create( - _device, BindFlags.IndexBuffer, s_indices, 0, ResourceUsage.Immutable); + _whiteTexture = graphicsDevice.Textures.White; + + _indexBuffer = IndexBuffer.Create(graphicsDevice, s_indices); Assembly assembly = Assembly.GetExecutingAssembly(); - using (Stream stream = assembly.GetManifestResourceStream( - $"{assembly.GetName().Name}.{Shaders.POSITION_COLOR_TEXTURE}")) + using (Stream stream = + assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{Shaders.POSITION_COLOR_TEXTURE}") ?? + throw new NullReferenceException($"{assembly.GetName().Name}.{Shaders.POSITION_COLOR_TEXTURE}")) { - Shader.Shader.Technique technique = - (_shader = ShaderFileLoader.FromStream(iDevice, stream) ?? + Shader.Shader.Group group = + (_shader = ShaderFileLoader.FromStream(graphicsDevice, stream) ?? throw new NullReferenceException(nameof(ShaderFileLoader.FromStream)))["DEFAULT"]; - _vertexShader = technique; - _pixelShader = technique; + _vertexShader = group; + _pixelShader = group; - _vertexInputLayout = new InputLayout( - _device, technique.GetShaderSignature(Shader.Shader.Type.VertexShader), - new[] - { - new InputElement( - "SV_POSITION", 0, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0), - new InputElement("COLOR", 0, Format.R32G32B32A32_Float, InputElement.AppendAligned, 0), - new InputElement("TEXCOORD", 0, Format.R32G32_Float, InputElement.AppendAligned, 0) - }); + _vertexInputLayout = group.CreateInputLayout(graphicsDevice, Shader.Shader.Type.VertexShader); } - _vertexBuffer = new Buffer( - _device, VERTEX_STRIDE * MAX_VERTEX_COUNT, ResourceUsage.Dynamic, BindFlags.VertexBuffer, - CpuAccessFlags.Write, ResourceOptionFlags.None, 0); - _vertexBufferBinding = new VertexBufferBinding(_vertexBuffer, VERTEX_STRIDE, 0); - _perFrameBuffer = new Buffer( - _device, sizeof(float) * 4 * 4 * 1, ResourceUsage.Default, BindFlags.ConstantBuffer, - CpuAccessFlags.None, ResourceOptionFlags.None, 0); + _vertexBuffer = VertexBuffer.Create(graphicsDevice, MAX_VERTEX_COUNT); + _perFrameBuffer = ConstantBuffer.Create(graphicsDevice); _sortIndices = new int[MAX_BATCH_SIZE]; _sortedSprites = new SpriteInfo[MAX_BATCH_SIZE]; @@ -176,21 +175,13 @@ public SpriteBatch(IGraphicsDevice iDevice, SpriteSortAlgorithm sortAlgorithm = _spriteQueue = new SpriteInfo[MAX_BATCH_SIZE]; _spriteTextures = new TextureInfo[MAX_BATCH_SIZE]; - iDevice.ResizeFinished += IDevice_onResizeFinished; - - Resize(iDevice.Viewport); - } + graphicsDevice.ResizeFinished += GraphicsDeviceOnResizeFinished; - /// - /// Finalizes an instance of the class. - /// - ~SpriteBatch() - { - Dispose(false); + Resize(graphicsDevice.Viewport); } /// - /// Begins. + /// Begins a new batch. /// /// Thrown when the requested operation is invalid. /// (Optional) The sort mode. @@ -230,7 +221,7 @@ public void Begin(SpriteSortMode sortMode = SpriteSortMode.Deferred } /// - /// Ends this object. + /// Ends the current batch. /// /// Thrown when the requested operation is invalid. public void End() @@ -239,6 +230,7 @@ public void End() { throw new InvalidOperationException("Begin must be called before End"); } + if (_spriteQueueCount > 0) { PrepareForRendering(); @@ -277,105 +269,23 @@ public void Resize(float width, float height) { float xRatio = width > 0 ? 1f / width : 0f; float yRatio = height > 0 ? -1f / height : 0f; + _projectionMatrix = new Matrix { M11 = xRatio * 2f, M22 = yRatio * 2f, M33 = 1f, M44 = 1f, - M41 = -1f, - M42 = 1f + M41 = -(_center ? 0f : 1f), + M42 = _center ? 0f : 1f }; } - /// - /// Updates the vertex from sprite information. - /// - /// [in,out] Information describing the sprite. - /// [in,out] If non-null, the vpct pointer. - /// The delta x coordinate. - /// The delta y coordinate. - private static unsafe void UpdateVertexFromSpriteInfo(ref SpriteInfo spriteInfo, - VertexPositionColorTexture* vpctPtr, - float deltaX, - float deltaY) + private void GraphicsDeviceOnResizeFinished(ViewportF viewport) { - Vector2 origin = spriteInfo.Origin; - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (spriteInfo.Source.Width != 0f) - { - origin.X /= spriteInfo.Source.Width; - } - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (spriteInfo.Source.Height != 0f) - { - origin.Y /= spriteInfo.Source.Height; - } - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (spriteInfo.Rotation == 0f) - { - for (int j = 0; j < VERTICES_PER_SPRITE; j++) - { - VertexPositionColorTexture* vertex = vpctPtr + j; - - Vector2 corner = s_cornerOffsets[j]; - float posX = (corner.X - origin.X) * spriteInfo.Destination.Width; - float posY = (corner.Y - origin.Y) * spriteInfo.Destination.Height; - - vertex->X = spriteInfo.Destination.X + posX; - vertex->Y = spriteInfo.Destination.Y + posY; - vertex->Z = spriteInfo.Depth; - vertex->W = 1.0f; - - vertex->R = spriteInfo.Color.R * spriteInfo.Opacity; - vertex->G = spriteInfo.Color.G * spriteInfo.Opacity; - vertex->B = spriteInfo.Color.B * spriteInfo.Opacity; - vertex->A = spriteInfo.Color.A * spriteInfo.Opacity; - - corner = s_cornerOffsets[j ^ (int)spriteInfo.SpriteEffects]; - vertex->U = (spriteInfo.Source.X + (corner.X * spriteInfo.Source.Width)) * deltaX; - vertex->V = (spriteInfo.Source.Y + (corner.Y * spriteInfo.Source.Height)) * deltaY; - } - } - else - { - float cos = (float)Math.Cos(spriteInfo.Rotation); - float sin = (float)Math.Sin(spriteInfo.Rotation); - for (int j = 0; j < VERTICES_PER_SPRITE; j++) - { - VertexPositionColorTexture* vertex = vpctPtr + j; - - Vector2 corner = s_cornerOffsets[j]; - float posX = (corner.X - origin.X) * spriteInfo.Destination.Width; - float posY = (corner.Y - origin.Y) * spriteInfo.Destination.Height; - - vertex->X = (spriteInfo.Destination.X + (posX * cos)) - (posY * sin); - vertex->Y = spriteInfo.Destination.Y + (posX * sin) + (posY * cos); - vertex->Z = spriteInfo.Depth; - vertex->W = 1.0f; - - vertex->R = spriteInfo.Color.R * spriteInfo.Opacity; - vertex->G = spriteInfo.Color.G * spriteInfo.Opacity; - vertex->B = spriteInfo.Color.B * spriteInfo.Opacity; - vertex->A = spriteInfo.Color.A * spriteInfo.Opacity; - - corner = s_cornerOffsets[j ^ (int)spriteInfo.SpriteEffects]; - vertex->U = (spriteInfo.Source.X + (corner.X * spriteInfo.Source.Width)) * deltaX; - vertex->V = (spriteInfo.Source.Y + (corner.Y * spriteInfo.Source.Height)) * deltaY; - } - } + Resize(viewport); } - /// - /// Draw batch per texture. - /// - /// [in,out] The texture. - /// The sprites. - /// The offset. - /// Number of. private unsafe void DrawBatchPerTexture(ref TextureInfo texture, SpriteInfo[] sprites, int offset, int count) { _context.PixelShader.SetShaderResource(0, texture.View); @@ -389,60 +299,52 @@ private unsafe void DrawBatchPerTexture(ref TextureInfo texture, SpriteInfo[] sp { batchSize = MAX_BATCH_SIZE; } - lock (_device) - { - DataBox box = _context.MapSubresource( - _vertexBuffer, 0, MapMode.WriteDiscard, MapFlags.None); - VertexPositionColorTexture* vpctPtr = (VertexPositionColorTexture*)box.DataPointer; - if (batchSize > BATCH_SEQUENTIAL_THRESHOLD) - { - int middle = batchSize >> 1; - Parallel.Invoke( - () => + DataBox box = _context.MapSubresource( + _vertexBuffer, 0, MapMode.WriteDiscard, MapFlags.None); + VertexPositionColorTexture* vpctPtr = (VertexPositionColorTexture*)box.DataPointer; + + if (batchSize > BATCH_SEQUENTIAL_THRESHOLD) + { + int middle = batchSize >> 1; + Parallel.Invoke( + () => + { + for (int i = 0; i < middle; i++) { - for (int i = 0; i < middle; i++) - { - UpdateVertexFromSpriteInfo( - - // ReSharper disable once AccessToModifiedClosure - ref sprites[i + offset], vpctPtr + (i << 2), deltaX, deltaY); - } - }, - () => + UpdateVertexFromSpriteInfo( + + // ReSharper disable once AccessToModifiedClosure + ref sprites[i + offset], vpctPtr + (i << 2), deltaX, deltaY); + } + }, + () => + { + for (int i = middle; i < batchSize; i++) { - for (int i = middle; i < batchSize; i++) - { - UpdateVertexFromSpriteInfo( - - // ReSharper disable once AccessToModifiedClosure - ref sprites[i + offset], vpctPtr + (i << 2), deltaX, deltaY); - } - }); - } - else + UpdateVertexFromSpriteInfo( + + // ReSharper disable once AccessToModifiedClosure + ref sprites[i + offset], vpctPtr + (i << 2), deltaX, deltaY); + } + }); + } + else + { + for (int i = 0; i < batchSize; i++) { - for (int i = 0; i < batchSize; i++) - { - UpdateVertexFromSpriteInfo( - ref sprites[i + offset], vpctPtr + (i << 2), deltaX, deltaY); - } + UpdateVertexFromSpriteInfo( + ref sprites[i + offset], vpctPtr + (i << 2), deltaX, deltaY); } - _context.UnmapSubresource(_vertexBuffer, 0); - _context.DrawIndexed(INDICES_PER_SPRITE * batchSize, 0, 0); } + _context.UnmapSubresource(_vertexBuffer, 0); + _context.DrawIndexed(INDICES_PER_SPRITE * batchSize, 0, 0); + offset += batchSize; count -= batchSize; } } - /// - /// Flushes the batch. - /// - /// - /// Thrown when an Invalid Enum Argument error - /// condition occurs. - /// private void FlushBatch() { SpriteInfo[] spriteQueueForBatch; @@ -510,121 +412,6 @@ private void FlushBatch() _spriteQueueCount = 0; } - /// - /// Device on resize finished. - /// - /// The viewport. - private void IDevice_onResizeFinished(ViewportF viewport) - { - Resize(viewport); - } - - /// - /// Initializes the states. - /// - /// The device. - private void InitializeStates(Device5 device) - { - _defaultSamplerState = new SamplerState( - device, - new SamplerStateDescription - { - AddressU = TextureAddressMode.Wrap, - AddressV = TextureAddressMode.Wrap, - AddressW = TextureAddressMode.Wrap, - BorderColor = Color.White, - ComparisonFunction = Comparison.Always, - Filter = Filter.ComparisonMinMagMipLinear, - MaximumAnisotropy = 16, - MaximumLod = float.MaxValue, - MinimumLod = 0, - MipLodBias = 0.0f - }); - - _defaultBlendState = new BlendState( - device, - new BlendStateDescription - { - AlphaToCoverageEnable = false, - IndependentBlendEnable = false, - RenderTarget = - { - [0] = new RenderTargetBlendDescription - { - IsBlendEnabled = true, - SourceBlend = BlendOption.One, - DestinationBlend = BlendOption.InverseSourceAlpha, - BlendOperation = BlendOperation.Add, - SourceAlphaBlend = BlendOption.Zero, - DestinationAlphaBlend = BlendOption.Zero, - AlphaBlendOperation = BlendOperation.Add, - RenderTargetWriteMask = ColorWriteMaskFlags.All - } - } - }) { DebugName = "AlphaBlend" }; - - _defaultDepthStencilState = new DepthStencilState( - device, - new DepthStencilStateDescription - { - IsDepthEnabled = false, - DepthWriteMask = DepthWriteMask.All, - DepthComparison = Comparison.LessEqual, - IsStencilEnabled = false, - StencilReadMask = 0xFF, - StencilWriteMask = 0xFF, - FrontFace = new DepthStencilOperationDescription - { - FailOperation = StencilOperation.Keep, - DepthFailOperation = StencilOperation.Increment, - PassOperation = StencilOperation.Keep, - Comparison = Comparison.Always - }, - BackFace = new DepthStencilOperationDescription - { - FailOperation = StencilOperation.Keep, - DepthFailOperation = StencilOperation.Decrement, - PassOperation = StencilOperation.Keep, - Comparison = Comparison.Always - } - }); - - _defaultRasterizerState = new RasterizerState( - device, - new RasterizerStateDescription - { - FillMode = FillMode.Solid, - CullMode = CullMode.Back, - IsFrontCounterClockwise = false, - DepthBias = 0, - DepthBiasClamp = 0, - SlopeScaledDepthBias = 0, - IsDepthClipEnabled = false, - IsScissorEnabled = false, - IsMultisampleEnabled = true, - IsAntialiasedLineEnabled = true - }); - - _defaultRasterizerScissorEnabledState = new RasterizerState( - device, - new RasterizerStateDescription - { - FillMode = FillMode.Solid, - CullMode = CullMode.Back, - IsFrontCounterClockwise = false, - DepthBias = 0, - DepthBiasClamp = 0, - SlopeScaledDepthBias = 0, - IsDepthClipEnabled = false, - IsScissorEnabled = true, - IsMultisampleEnabled = true, - IsAntialiasedLineEnabled = true - }); - } - - /// - /// Prepare for rendering. - /// private void PrepareForRendering() { _context.VertexShader.Set(_vertexShader); @@ -648,920 +435,88 @@ private void PrepareForRendering() _context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList; _context.InputAssembler.InputLayout = _vertexInputLayout; - _context.VertexShader.SetConstantBuffer(0, _perFrameBuffer); - - _context.InputAssembler.SetIndexBuffer(_indexBuffer, Format.R16_UInt, 0); - _context.InputAssembler.SetVertexBuffers(0, _vertexBufferBinding); - - Matrix worldViewProjection = _transformMatrix * _viewMatrix * _projectionMatrix; - worldViewProjection.Transpose(); - + Matrix worldViewProjection = Matrix.Transpose(_transformMatrix * _viewMatrix * _projectionMatrix); _context.UpdateSubresource(ref worldViewProjection, _perFrameBuffer); - } + _context.VertexShader.SetConstantBuffer(0, _perFrameBuffer); - internal struct SpriteInfo - { - public RectangleF Source; - public RectangleF Destination; - public Vector2 Origin; - public float Rotation; - public float Depth; - public SpriteEffects SpriteEffects; - public Color Color; - public float Opacity; + _context.InputAssembler.SetIndexBuffer(_indexBuffer, _indexBuffer.Format, 0); + _context.InputAssembler.SetVertexBuffers(0, _vertexBuffer); } - internal readonly struct TextureInfo + private static unsafe void UpdateVertexFromSpriteInfo(ref SpriteInfo spriteInfo, + VertexPositionColorTexture* vpctPtr, + float deltaX, + float deltaY) { - public readonly ShaderResourceView View; - public readonly int Width; - public readonly int Height; - public readonly long Ptr64; + Vector2 origin = spriteInfo.Origin; - public TextureInfo(ShaderResourceView view, int width, int height) + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (spriteInfo.Source.Width != 0f) { - View = view; - Width = width; - Height = height; - Ptr64 = view.NativePointer.ToInt64(); + origin.X /= spriteInfo.Source.Width; } - } - - [StructLayout(LayoutKind.Explicit, Size = VERTEX_STRIDE)] - private struct VertexPositionColorTexture - { - [FieldOffset(0)] - public float X; - - [FieldOffset(4)] - public float Y; - - [FieldOffset(8)] - public float Z; - - [FieldOffset(12)] - public float W; - - [FieldOffset(16)] - public float R; - - [FieldOffset(20)] - public float G; - - [FieldOffset(24)] - public float B; - - [FieldOffset(28)] - public float A; - - [FieldOffset(32)] - public float U; - - [FieldOffset(36)] - public float V; - } - - #region Drawing - - #region Defaults - - /// - /// Draw rectangle. - /// - /// Destination rectangle. - /// The color. - /// Width of the line. - /// The rotation. - /// The opacity. - /// Depth of the layer. - public void DrawRectangle(in RectangleF destinationRectangle, - in Color color, - float lineWidth, - float rotation, - float opacity, - float layerDepth) - { - DrawRectangle(destinationRectangle, color, lineWidth, rotation, s_vector2Zero, opacity, layerDepth); - } - - /// - /// Draw rectangle. - /// - /// Destination rectangle. - /// The color. - /// Width of the line. - /// The rotation. - /// The origin. - /// The opacity. - /// Depth of the layer. - public void DrawRectangle(in RectangleF destinationRectangle, - in Color color, - float lineWidth, - float rotation, - in Vector2 origin, - float opacity, - float layerDepth) - { - Vector2[] vertex; // ReSharper disable once CompareOfFloatsByEqualityOperator - if (rotation == 0.0f) - { - vertex = new[] - { - destinationRectangle.TopLeft, destinationRectangle.TopRight, destinationRectangle.BottomRight, - destinationRectangle.BottomLeft - }; - } - else + if (spriteInfo.Source.Height != 0f) { - vertex = new Vector2[4]; - - Vector2 o = origin; - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (destinationRectangle.Width != 0f) - { - o.X /= destinationRectangle.Width; - } - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if (destinationRectangle.Height != 0f) - { - o.Y /= destinationRectangle.Height; - } - - float cos = (float)Math.Cos(rotation); - float sin = (float)Math.Sin(rotation); - for (int j = 0; j < VERTICES_PER_SPRITE; j++) - { - Vector2 corner = s_cornerOffsets[j]; - float posX = (corner.X - o.X) * destinationRectangle.Width; - float posY = (corner.Y - o.Y) * destinationRectangle.Height; - - vertex[j] = new Vector2( - (destinationRectangle.X + (posX * cos)) - (posY * sin), - destinationRectangle.Y + (posX * sin) + (posY * cos)); - } + origin.Y /= spriteInfo.Source.Height; } - DrawPolygon(vertex, color, lineWidth, opacity, layerDepth); - } - - /// - /// Draw fill rectangle. - /// - /// Destination rectangle. - /// The color. - /// Depth of the layer. - public void DrawFillRectangle(in RectangleF destinationRectangle, in Color color, float layerDepth) - { - DrawSprite( - DefaultTextures.WhiteTexture, destinationRectangle, false, s_nullRectangle, - color, 0.0f, s_vector2Zero, 1.0f, SpriteEffects.None, layerDepth); - } - - /// - /// Draw fill rectangle. - /// - /// Destination rectangle. - /// The color. - /// The opacity. - /// Depth of the layer. - public void DrawFillRectangle(in RectangleF destinationRectangle, - in Color color, - float opacity, - float layerDepth) - { - DrawSprite( - DefaultTextures.WhiteTexture, destinationRectangle, false, s_nullRectangle, - color, 0.0f, s_vector2Zero, opacity, SpriteEffects.None, layerDepth); - } - - /// - /// Draw fill rectangle. - /// - /// Destination rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// Depth of the layer. - public void DrawFillRectangle(in RectangleF destinationRectangle, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - float layerDepth) - { - DrawSprite( - DefaultTextures.WhiteTexture, destinationRectangle, false, s_nullRectangle, - color, rotation, origin, opacity, SpriteEffects.None, layerDepth); - } - - /// - /// Draw line. - /// - /// The first point. - /// The second point. - /// The color. - /// Width of the line. - /// The opacity. - /// Depth of the layer. - public void DrawLine(in Vector2 point1, - in Vector2 point2, - in Color color, - float lineWidth, - float opacity, - float layerDepth) - { - DrawLine(point1, point2, color, lineWidth, opacity, 1.0f, layerDepth); - } - - /// - /// Draw line. - /// - /// The first point. - /// The second point. - /// The color. - /// Width of the line. - /// The opacity. - /// The length factor. - /// Depth of the layer. - public void DrawLine(in Vector2 point1, - in Vector2 point2, - in Color color, - float lineWidth, - float opacity, - float lengthFactor, - float layerDepth) - { - DrawSprite( - DefaultTextures.WhiteTexture, new RectangleF( - point1.X, point1.Y, Vector2.Distance(point1, point2) * lengthFactor, lineWidth), false, - s_nullRectangle, color, (float)Math.Atan2(point2.Y - point1.Y, point2.X - point1.X), - s_vector2Zero, opacity, SpriteEffects.None, layerDepth); - } - - /// - /// Draw polygon. - /// - /// The vertex. - /// The color. - /// Width of the line. - /// The opacity. - /// Depth of the layer. - public void DrawPolygon(Vector2[] vertex, in Color color, float lineWidth, float opacity, float layerDepth) - { - if (vertex.Length > 1) + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (spriteInfo.Rotation == 0f) { - int l = vertex.Length - 1; - for (int i = 0; i < l; i++) + for (int j = 0; j < VERTICES_PER_SPRITE; j++) { - DrawLine(vertex[i], vertex[i + 1], color, lineWidth, opacity, layerDepth); - } - DrawLine(vertex[l], vertex[0], color, lineWidth, opacity, layerDepth); - } - } - - /// - /// Draw circle. - /// - /// The center. - /// The radius. - /// The color. - /// Width of the line. - /// The opacity. - /// The segments. - /// Depth of the layer. - public void DrawCircle(in Vector2 center, - float radius, - in Color color, - float lineWidth, - float opacity, - int segments, - float layerDepth) - { - DrawCircle(center, radius, 0, MathUtil.TwoPi, color, lineWidth, opacity, segments, layerDepth); - } - - /// - /// Draw circle. - /// - /// The center. - /// The radius. - /// The start. - /// The end. - /// The color. - /// Width of the line. - /// The opacity. - /// The segments. - /// Depth of the layer. - public void DrawCircle(in Vector2 center, - float radius, - float start, - float end, - in Color color, - float lineWidth, - float opacity, - int segments, - float layerDepth) - { - Vector2[] vertex = new Vector2[segments]; - - float increment = (end - start) / segments; - float theta = start; - - for (int i = 0; i < segments; i++) - { - vertex[i].X = center.X + (radius * (float)Math.Cos(theta)); - vertex[i].Y = center.Y + (radius * (float)Math.Sin(theta)); - theta += increment; - } - - DrawPolygon(vertex, color, lineWidth, opacity, layerDepth); - } - - #endregion - - #region Texture - - /// - /// Draws. - /// - /// The texture. - /// The position. - /// The color. - public void Draw(Texture texture, in Vector2 position, in Color color) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, 1f, 1f), true, s_nullRectangle, - color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None, 0f); - } - - /// - /// Draws. - /// - /// The texture. - /// Destination rectangle. - /// The color. - public void Draw(Texture texture, in RectangleF destinationRectangle, in Color color) - { - DrawSprite( - texture, destinationRectangle, false, s_nullRectangle, - color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None, 0f); - } - - /// - /// Draws. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - public void Draw(Texture texture, in Vector2 position, in Rectangle? sourceRectangle, in Color color) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, 1f, 1f), true, sourceRectangle, - color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None, 0f); - } - - /// - /// Draws. - /// - /// The texture. - /// Destination rectangle. - /// Source rectangle. - /// The color. - public void Draw(Texture texture, - in RectangleF destinationRectangle, - in Rectangle? sourceRectangle, - in Color color) - { - DrawSprite( - texture, destinationRectangle, false, sourceRectangle, - color, 0f, s_vector2Zero, 1.0f, SpriteEffects.None, 0f); - } - - /// - /// Draws. - /// - /// The texture. - /// Destination rectangle. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void Draw(Texture texture, - in RectangleF destinationRectangle, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - DrawSprite( - texture, destinationRectangle, false, sourceRectangle, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draws. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The scale. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void Draw(Texture texture, - in Vector2 position, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float scale, - float opacity, - SpriteEffects effects, - float layerDepth) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, scale, scale), true, sourceRectangle, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draws. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The scale. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void Draw(Texture texture, - in Vector2 position, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - in Vector2 scale, - float opacity, - SpriteEffects effects, - float layerDepth) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, scale.X, scale.Y), true, sourceRectangle, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draw sprite. - /// - /// - /// Thrown when one or more required arguments are - /// null. - /// - /// Thrown when the requested operation is invalid. - /// The texture. - /// Destination for the. - /// True to scale destination. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// The depth. - private unsafe void DrawSprite(Texture texture, - in RectangleF destination, - bool scaleDestination, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float depth) - { - if (!_isBeginCalled) - { - throw new InvalidOperationException("Begin must be called before draw"); - } + VertexPositionColorTexture* vertex = vpctPtr + j; - if (texture.TexturePointer == IntPtr.Zero) - { - throw new ArgumentNullException(nameof(texture)); - } + Vector2 corner = s_cornerOffsets[j]; + float posX = (corner.X - origin.X) * spriteInfo.Destination.Width; + float posY = (corner.Y - origin.Y) * spriteInfo.Destination.Height; - if (_spriteQueueCount >= _spriteQueue.Length) - { - bool lockTaken = false; - try - { - _spinLock.Enter(ref lockTaken); + vertex->X = spriteInfo.Destination.X + posX; + vertex->Y = spriteInfo.Destination.Y + posY; + vertex->Z = spriteInfo.Depth; + vertex->W = 1.0f; - int size = _spriteQueue.Length * 2; - _sortIndices = new int[size]; - _sortedSprites = new SpriteInfo[size]; - Array.Resize(ref _spriteQueue, size); - Array.Resize(ref _spriteTextures, size); - } - finally - { - if (lockTaken) - { - _spinLock.Exit(false); - } - } - } + vertex->R = spriteInfo.Color.R * spriteInfo.Opacity; + vertex->G = spriteInfo.Color.G * spriteInfo.Opacity; + vertex->B = spriteInfo.Color.B * spriteInfo.Opacity; + vertex->A = spriteInfo.Color.A * spriteInfo.Opacity; - if (!_textureInfos.TryGetValue(texture.TexturePointer, out TextureInfo textureInfo)) - { - bool lockTaken = false; - try - { - _spinLock.Enter(ref lockTaken); - if (!_textureInfos.TryGetValue(texture.TexturePointer, out textureInfo)) - { - textureInfo = new TextureInfo(texture.TextureView, texture.Width, texture.Height); - _textureInfos.Add(texture.TexturePointer, textureInfo); - } - } - finally - { - if (lockTaken) - { - _spinLock.Exit(false); - } + corner = s_cornerOffsets[j ^ (int)spriteInfo.SpriteEffects]; + vertex->U = (spriteInfo.Source.X + (corner.X * spriteInfo.Source.Width)) * deltaX; + vertex->V = (spriteInfo.Source.Y + (corner.Y * spriteInfo.Source.Height)) * deltaY; } } - - int spriteQueueCount = Interlocked.Increment(ref _spriteQueueCount) - 1; - fixed (SpriteInfo* spriteInfo = &_spriteQueue[spriteQueueCount]) + else { - float width; - float height; - if (sourceRectangle.HasValue) - { - Rectangle rectangle = sourceRectangle.Value; - spriteInfo->Source.X = rectangle.X; - spriteInfo->Source.Y = rectangle.Y; - width = rectangle.Width; - height = rectangle.Height; - } - else + float cos = (float)Math.Cos(spriteInfo.Rotation); + float sin = (float)Math.Sin(spriteInfo.Rotation); + for (int j = 0; j < VERTICES_PER_SPRITE; j++) { - spriteInfo->Source.X = 0; - spriteInfo->Source.Y = 0; - width = texture.Width; - height = texture.Height; - } - - spriteInfo->Source.Width = width; - spriteInfo->Source.Height = height; + VertexPositionColorTexture* vertex = vpctPtr + j; - spriteInfo->Destination.X = destination.X; - spriteInfo->Destination.Y = destination.Y; + Vector2 corner = s_cornerOffsets[j]; + float posX = (corner.X - origin.X) * spriteInfo.Destination.Width; + float posY = (corner.Y - origin.Y) * spriteInfo.Destination.Height; - if (scaleDestination) - { - spriteInfo->Destination.Width = destination.Width * width; - spriteInfo->Destination.Height = destination.Height * height; - } - else - { - spriteInfo->Destination.Width = destination.Width; - spriteInfo->Destination.Height = destination.Height; - } + vertex->X = (spriteInfo.Destination.X + (posX * cos)) - (posY * sin); + vertex->Y = spriteInfo.Destination.Y + (posX * sin) + (posY * cos); + vertex->Z = spriteInfo.Depth; + vertex->W = 1.0f; - if (spriteInfo->Destination.Width < 0) - { - spriteInfo->Destination.X += spriteInfo->Destination.Width; - spriteInfo->Destination.Width = -spriteInfo->Destination.Width; - } + vertex->R = spriteInfo.Color.R * spriteInfo.Opacity; + vertex->G = spriteInfo.Color.G * spriteInfo.Opacity; + vertex->B = spriteInfo.Color.B * spriteInfo.Opacity; + vertex->A = spriteInfo.Color.A * spriteInfo.Opacity; - if (spriteInfo->Destination.Height < 0) - { - spriteInfo->Destination.Y += spriteInfo->Destination.Height; - spriteInfo->Destination.Height = -spriteInfo->Destination.Height; + corner = s_cornerOffsets[j ^ (int)spriteInfo.SpriteEffects]; + vertex->U = (spriteInfo.Source.X + (corner.X * spriteInfo.Source.Width)) * deltaX; + vertex->V = (spriteInfo.Source.Y + (corner.Y * spriteInfo.Source.Height)) * deltaY; } - - spriteInfo->Origin = origin; - spriteInfo->Rotation = rotation; - spriteInfo->Depth = depth; - spriteInfo->SpriteEffects = effects; - spriteInfo->Color = color; - spriteInfo->Opacity = opacity; } - - _spriteTextures[spriteQueueCount] = textureInfo; - } - - #endregion - - #region SpiteFont - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// Depth of the layer. - public void DrawText(SpriteFont font, string text, in Vector2 position, in Color color, float layerDepth) - { - font.Draw( - DrawTextInternal, text, position, - color, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// Depth of the layer. - public void DrawText(SpriteFont font, - string text, - in Vector2 position, - in Color color, - float rotation, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, position, - color, rotation, Vector2.Zero, 1.0f, SpriteEffects.None, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void DrawText(SpriteFont font, - string text, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, position, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void DrawText(SpriteFont font, - string text, - int start, - int end, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, start, end, position, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The dimension. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void DrawText(SpriteFont font, - string text, - int start, - int end, - in Vector2 position, - in Size2F dimension, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, start, end, position, dimension, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// Depth of the layer. - public void DrawText(SpriteFont font, StringBuilder text, in Vector2 position, in Color color, float layerDepth) - { - font.Draw( - DrawTextInternal, text, position, - color, 0f, Vector2.Zero, 1.0f, SpriteEffects.None, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// Depth of the layer. - public void DrawText(SpriteFont font, - StringBuilder text, - in Vector2 position, - in Color color, - float rotation, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, position, - color, rotation, Vector2.Zero, 1.0f, SpriteEffects.None, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void DrawText(SpriteFont font, - StringBuilder text, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, position, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void DrawText(SpriteFont font, - StringBuilder text, - int start, - int end, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, start, end, position, - color, rotation, origin, opacity, effects, layerDepth); - } - - /// - /// Draw text. - /// - /// The font. - /// The text. - /// The start. - /// The end. - /// The position. - /// The dimension. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - public void DrawText(SpriteFont font, - StringBuilder text, - int start, - int end, - in Vector2 position, - in Size2F dimension, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - font.Draw( - DrawTextInternal, text, start, end, position, dimension, - color, rotation, origin, opacity, effects, layerDepth); } - /// - /// Draw text internal. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The scale. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void DrawTextInternal(Texture texture, - in Vector2 position, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float scale, - float opacity, - SpriteEffects effects, - float layerDepth) - { - DrawSprite( - texture, new RectangleF(position.X, position.Y, scale, scale), true, sourceRectangle, - color, rotation, origin, opacity, effects, layerDepth); - } - - #endregion - - #endregion - #region IDisposable Support /// @@ -1590,15 +545,9 @@ private void Dispose(bool disposing) Utilities.Dispose(ref _samplerState); Utilities.Dispose(ref _depthStencilState); - Utilities.Dispose(ref _defaultBlendState); - Utilities.Dispose(ref _defaultRasterizerState); - Utilities.Dispose(ref _defaultSamplerState); - Utilities.Dispose(ref _defaultRasterizerScissorEnabledState); - Utilities.Dispose(ref _defaultDepthStencilState); - _vertexBuffer.Dispose(); - _perFrameBuffer.Dispose(); _indexBuffer.Dispose(); + _perFrameBuffer.Dispose(); _shader.Dispose(); _vertexInputLayout.Dispose(); @@ -1608,6 +557,14 @@ private void Dispose(bool disposing) } } + /// + /// Finalizes an instance of the class. + /// + ~SpriteBatch() + { + Dispose(false); + } + /// public void Dispose() { diff --git a/src/Exomia.Framework/Graphics/SpriteFontCR.cs b/src/Exomia.Framework/Graphics/SpriteFont.ContentSerializationReader.cs similarity index 87% rename from src/Exomia.Framework/Graphics/SpriteFontCR.cs rename to src/Exomia.Framework/Graphics/SpriteFont.ContentSerializationReader.cs index 97ad13db..130d2a2c 100644 --- a/src/Exomia.Framework/Graphics/SpriteFontCR.cs +++ b/src/Exomia.Framework/Graphics/SpriteFont.ContentSerializationReader.cs @@ -15,10 +15,7 @@ namespace Exomia.Framework.Graphics { - /// - /// A sprite font carriage return. This class cannot be inherited. - /// - sealed class SpriteFontCR : ContentSerializationReader + sealed class SpriteFontContentSerializationReader : ContentSerializationReader { /// public override SpriteFont ReadContext(ContentSerializationContext context) @@ -40,9 +37,6 @@ public override SpriteFont ReadContext(ContentSerializationContext context) } } - /// - /// A sprite font glyph carriage return. This class cannot be inherited. - /// sealed class SpriteFontGlyphCR : ContentSerializationReader { /// @@ -59,9 +53,6 @@ public override SpriteFont.Glyph ReadContext(ContentSerializationContext context } } - /// - /// A sprite font kerning carriage return. This class cannot be inherited. - /// sealed class SpriteFontKerningCR : ContentSerializationReader { /// diff --git a/src/Exomia.Framework/Graphics/SpriteFontCW.cs b/src/Exomia.Framework/Graphics/SpriteFont.ContentSerializationWriter.cs similarity index 85% rename from src/Exomia.Framework/Graphics/SpriteFontCW.cs rename to src/Exomia.Framework/Graphics/SpriteFont.ContentSerializationWriter.cs index 21ee3554..0b921186 100644 --- a/src/Exomia.Framework/Graphics/SpriteFontCW.cs +++ b/src/Exomia.Framework/Graphics/SpriteFont.ContentSerializationWriter.cs @@ -13,10 +13,7 @@ namespace Exomia.Framework.Graphics { - /// - /// A sprite font cw. This class cannot be inherited. - /// - sealed class SpriteFontCW : ContentSerializationWriter + sealed class SpriteFontContentSerializationWriter : ContentSerializationWriter { /// public override void WriteContext(ContentSerializationContext context, SpriteFont obj) @@ -40,9 +37,6 @@ public override void WriteContext(ContentSerializationContext context, SpriteFon } } - /// - /// A sprite font glyph cw. This class cannot be inherited. - /// sealed class SpriteFontGlyphCW : ContentSerializationWriter { /// @@ -56,9 +50,6 @@ public override void WriteContext(ContentSerializationContext context, SpriteFon } } - /// - /// A sprite font kerning cw. This class cannot be inherited. - /// sealed class SpriteFontKerningCW : ContentSerializationWriter { /// diff --git a/src/Exomia.Framework/Graphics/SpriteFont.Glyph.cs b/src/Exomia.Framework/Graphics/SpriteFont.Glyph.cs new file mode 100644 index 00000000..cf2c264a --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteFont.Glyph.cs @@ -0,0 +1,50 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using Exomia.Framework.ContentSerialization; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteFont + { + /// + /// A glyph. + /// + [ContentSerializable(typeof(SpriteFontGlyphCR), typeof(SpriteFontGlyphCW))] + public struct Glyph + { + /// + /// The character. + /// + public int Character; + + /// + /// The subrect. + /// + public Rectangle Subrect; + + /// + /// The offset x coordinate. + /// + public int OffsetX; + + /// + /// The offset y coordinate. + /// + public int OffsetY; + + /// + /// The advance. + /// + public int XAdvance; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteFont.Kerning.cs b/src/Exomia.Framework/Graphics/SpriteFont.Kerning.cs new file mode 100644 index 00000000..74dff3b0 --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteFont.Kerning.cs @@ -0,0 +1,39 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using Exomia.Framework.ContentSerialization; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteFont + { + /// + /// A kerning. + /// + [ContentSerializable(typeof(SpriteFontKerningCR), typeof(SpriteFontKerningCW))] + public struct Kerning + { + /// + /// The first. + /// + public int First; + + /// + /// The second. + /// + public int Second; + + /// + /// The offset. + /// + public int Offset; + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteFont.Methods.cs b/src/Exomia.Framework/Graphics/SpriteFont.Methods.cs new file mode 100644 index 00000000..32084504 --- /dev/null +++ b/src/Exomia.Framework/Graphics/SpriteFont.Methods.cs @@ -0,0 +1,531 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using SharpDX; + +namespace Exomia.Framework.Graphics +{ + public sealed partial class SpriteFont + { + /// + /// Returns the size of this text. + /// + /// The text. + /// + /// A with x and y set to width and height of + /// +#if NETSTANDARD2_1 + public Vector2 MeasureText(ReadOnlySpan text) +#else + public Vector2 MeasureText(string text) +#endif + { + return MeasureText(text, 0, text.Length); + } + + /// + /// Returns the size of this text from to . + /// + /// The text. + /// The start. + /// The end. + /// + /// A with x and y set to width and height of + /// +#if NETSTANDARD2_1 + public Vector2 MeasureText(ReadOnlySpan text, int start, int end) +#else + public Vector2 MeasureText(string text, int start, int end) +#endif + { + Vector2 size = Vector2.Zero; + + if (start >= end) { return size; } + if (end > text.Length) { end = text.Length; } + if (start < 0) { start = 0; } + + float x = 0; + float y = 0; + + int key = 0; + + for (int i = start; i < end; i++) + { + char c = text[i]; + switch (c) + { + case '\r': + { + key |= c; + continue; + } + case '\n': + { + key = 0; + x = 0; + y += LineSpacing; + } + break; + default: + { + if (!_glyphs.TryGetValue(c, out Glyph glyph)) + { + if (IgnoreUnknownCharacters) + { + continue; + } + + glyph = _defaultGlyph; + } + key |= c; + + float dx = glyph.OffsetX; + if (Kernings.TryGetValue(key, out Kerning kerning)) + { + dx += kerning.Offset; + } + + float nextX = x + glyph.XAdvance + SpacingX; + + float h = y + LineSpacing; + if (nextX + dx > size.X) + { + size.X = nextX; + } + if (h > size.Y) + { + size.Y = h; + } + + x = nextX; + } + break; + } + + key <<= 16; + } + + return size; + } + + /// + /// Determines which item (if any) has been hit. + /// + /// The text. + /// The start. + /// The end. + /// The position. + /// The position. + /// + /// An int. + /// +#if NETSTANDARD2_1 + public int HitTest(ReadOnlySpan text, int start, int end, float xPos, float yPos) +#else + public int HitTest(string text, int start, int end, float xPos, float yPos) +#endif + { + if (start >= end) { return end; } + if (end > text.Length) { end = text.Length; } + if (start < 0) { start = 0; } + + if (xPos < 0) { return -1; } + if (yPos < 0) { return -1; } + + float x = 0; + float y = 0; + + int key = 0; + + for (int i = start; i < end; i++) + { + char c = text[i]; + switch (c) + { + case '\r': + { + key |= c; + continue; + } + case '\n': + { + key = 0; + x = 0; + y += LineSpacing; + } + break; + default: + { + if (!_glyphs.TryGetValue(c, out Glyph glyph)) + { + if (IgnoreUnknownCharacters) + { + continue; + } + glyph = _defaultGlyph; + } + key |= c; + + float dx = glyph.OffsetX; + if (Kernings.TryGetValue(key, out Kerning kerning)) + { + dx += kerning.Offset; + } + + float nextX = x + glyph.XAdvance + SpacingX; + float h = y + LineSpacing; + + if (xPos >= x && xPos <= nextX + dx && yPos <= h && yPos >= y) + { + if (xPos < (x + nextX + dx) * 0.5f) { return i; } + return i + 1; + } + x = nextX; + } + break; + } + key <<= 16; + } + + return end; + } + +#if NETSTANDARD2_1 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Draw(DrawFont drawCallback, + ReadOnlySpan text, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth) +#else + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Draw(DrawFont drawCallback, + string text, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth) +#endif + { + Draw( + drawCallback, text, 0, text.Length, + in position, in color, rotation, in origin, + opacity, effects, layerDepth); + } + +#if NETSTANDARD2_1 + internal void Draw(DrawFont drawCallback, + ReadOnlySpan text, + int start, + int end, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth) +#else + internal void Draw(DrawFont drawCallback, + string text, + int start, + int end, + in Vector2 position, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth) +#endif + { + if (end <= start || end > text.Length) { end = text.Length; } + + float x = 0; + float y = 0; + + int key = 0; + + if (rotation == 0) + { + for (int i = start; i < end; i++) + { + char c = text[i]; + switch (c) + { + case '\r': + { + key |= c; + continue; + } + case '\n': + { + key = 0; + x = 0; + y += LineSpacing; + } + break; + default: + { + if (!_glyphs.TryGetValue(c, out Glyph glyph)) + { + if (IgnoreUnknownCharacters) + { + continue; + } + + glyph = _defaultGlyph; + } + key |= c; + + float dx = glyph.OffsetX; + if (Kernings.TryGetValue(key, out Kerning kerning)) + { + dx += kerning.Offset; + } + + drawCallback( + _texture, + new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), + glyph.Subrect, color, rotation, origin, + 1.0f, opacity, effects, layerDepth); + + x += glyph.XAdvance + SpacingX; + } + break; + } + + key <<= 16; + } + } + else + { + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + + for (int i = start; i < end; i++) + { + char c = text[i]; + switch (c) + { + case '\r': + { + key |= c; + continue; + } + case '\n': + { + key = 0; + x = 0; + y += LineSpacing; + } + break; + default: + { + if (!_glyphs.TryGetValue(c, out Glyph glyph)) + { + if (IgnoreUnknownCharacters) + { + continue; + } + + glyph = _defaultGlyph; + } + key |= c; + + float dx = glyph.OffsetX; + if (Kernings.TryGetValue(key, out Kerning kerning)) + { + dx += kerning.Offset; + } + + float ox = (position.X + x + dx) - origin.X; + float oy = (position.Y + y + glyph.OffsetY) - origin.Y; + drawCallback( + _texture, + new Vector2( + (float)(((cos * ox) - (sin * oy)) + origin.X), + (float)((sin * ox) + (cos * oy) + origin.Y)), + glyph.Subrect, + color, rotation, Vector2.Zero, 1.0f, opacity, effects, layerDepth); + + x += glyph.XAdvance + SpacingX; + } + break; + } + + key <<= 16; + } + } + } + +#if NETSTANDARD2_1 + internal void Draw(DrawFont drawCallback, + ReadOnlySpan text, + int start, + int end, + in Vector2 position, + in Size2F dimension, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth) +#else + internal void Draw(DrawFont drawCallback, + string text, + int start, + int end, + in Vector2 position, + in Size2F dimension, + in Color color, + float rotation, + in Vector2 origin, + float opacity, + TextureEffects effects, + float layerDepth) +#endif + { + if (end <= start || end > text.Length) { end = text.Length; } + + float x = 0; + float y = 0; + + int key = 0; + if (rotation == 0) + { + for (int i = start; i < end; i++) + { + char c = text[i]; + switch (c) + { + case '\r': + { + key |= c; + continue; + } + case '\n': + { + key = 0; + x = 0; + y += LineSpacing; + } + break; + default: + { + if (!_glyphs.TryGetValue(c, out Glyph glyph)) + { + if (IgnoreUnknownCharacters) + { + continue; + } + + glyph = _defaultGlyph; + } + key |= c; + + float dx = glyph.OffsetX; + if (Kernings.TryGetValue(key, out Kerning kerning)) + { + dx += kerning.Offset; + } + + if (x + dx + glyph.Subrect.Width > dimension.Width) { return; } + if (y + glyph.OffsetY + glyph.Subrect.Height > dimension.Height) { return; } + + drawCallback( + _texture, + new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), + glyph.Subrect, color, rotation, origin, + 1.0f, opacity, effects, layerDepth); + + x += glyph.XAdvance + SpacingX; + } + break; + } + + key <<= 16; + } + } + else + { + double cos = Math.Cos(rotation); + double sin = Math.Sin(rotation); + + for (int i = start; i < end; i++) + { + char c = text[i]; + switch (c) + { + case '\r': + { + key |= c; + continue; + } + case '\n': + { + key = 0; + x = 0; + y += LineSpacing; + } + break; + default: + { + if (!_glyphs.TryGetValue(c, out Glyph glyph)) + { + if (IgnoreUnknownCharacters) + { + continue; + } + + glyph = _defaultGlyph; + } + key |= c; + + float dx = glyph.OffsetX; + if (Kernings.TryGetValue(key, out Kerning kerning)) + { + dx += kerning.Offset; + } + + if (x + dx + glyph.Subrect.Width > dimension.Width) { return; } + if (y + glyph.OffsetY + glyph.Subrect.Height > dimension.Height) { return; } + + float ox = (position.X + x + dx) - origin.X; + float oy = (position.Y + y + glyph.OffsetY) - origin.Y; + drawCallback( + _texture, + new Vector2( + (float)(((cos * ox) - (sin * oy)) + origin.X), + (float)((sin * ox) + (cos * oy) + origin.Y)), + glyph.Subrect, color, rotation, Vector2.Zero, + 1.0f, opacity, effects, layerDepth); + + x += glyph.XAdvance + SpacingX; + } + break; + } + + key <<= 16; + } + } + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Graphics/SpriteFont.cs b/src/Exomia.Framework/Graphics/SpriteFont.cs index 89cb0b98..0ee162c3 100644 --- a/src/Exomia.Framework/Graphics/SpriteFont.cs +++ b/src/Exomia.Framework/Graphics/SpriteFont.cs @@ -10,7 +10,6 @@ using System; using System.Collections.Generic; -using System.Text; using Exomia.Framework.Content; using Exomia.Framework.ContentSerialization; using SharpDX; @@ -21,9 +20,20 @@ namespace Exomia.Framework.Graphics /// A sprite font. This class cannot be inherited. /// [ContentReadable(typeof(SpriteFontContentReader))] - [ContentSerializable(typeof(SpriteFontCR), typeof(SpriteFontCW))] - public sealed class SpriteFont : IDisposable + [ContentSerializable(typeof(SpriteFontContentSerializationReader), typeof(SpriteFontContentSerializationWriter))] + public sealed partial class SpriteFont : IDisposable { + internal delegate void DrawFont(Texture texture, + in Vector2 position, + in Rectangle? sourceRectangle, + in Color color, + float rotation, + in Vector2 origin, + float scale, + float opacity, + TextureEffects effects, + float layerDepth); + private Glyph _defaultGlyph; private Dictionary _glyphs; private Texture _texture; @@ -172,960 +182,10 @@ public SpriteFont() _texture = Texture.Empty; } - /// - /// Finalizes an instance of the class. - /// - ~SpriteFont() - { - Dispose(false); - } - - /// - /// A glyph. - /// - [ContentSerializable(typeof(SpriteFontGlyphCR), typeof(SpriteFontGlyphCW))] - public struct Glyph - { - /// - /// The character. - /// - public int Character; - - /// - /// The subrect. - /// - public Rectangle Subrect; - - /// - /// The offset x coordinate. - /// - public int OffsetX; - - /// - /// The offset y coordinate. - /// - public int OffsetY; - - /// - /// The advance. - /// - public int XAdvance; - } - - /// - /// A kerning. - /// - [ContentSerializable(typeof(SpriteFontKerningCR), typeof(SpriteFontKerningCW))] - public struct Kerning - { - /// - /// The first. - /// - public int First; - - /// - /// The second. - /// - public int Second; - - /// - /// The offset. - /// - public int Offset; - } - - /// - /// Draw font. - /// - /// The texture. - /// The position. - /// Source rectangle. - /// The color. - /// The rotation. - /// The origin. - /// The scale. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal delegate void DrawFont(Texture texture, - in Vector2 position, - in Rectangle? sourceRectangle, - in Color color, - float rotation, - in Vector2 origin, - float scale, - float opacity, - SpriteEffects effects, - float layerDepth); - - #region String - - /// - /// Measure text. - /// - /// The text. - /// - /// A Vector2. - /// - public Vector2 MeasureText(string text) - { - return MeasureText(text, 0, text.Length); - } - - /// - /// Measure text. - /// - /// The text. - /// The start. - /// The end. - /// - /// A Vector2. - /// - public Vector2 MeasureText(string text, int start, int end) - { - Vector2 size = Vector2.Zero; - - if (start >= end) { return size; } - if (end > text.Length) { end = text.Length; } - if (start < 0) { start = 0; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - float nextX = x + glyph.XAdvance + SpacingX; - - float h = y + LineSpacing; - if (nextX + dx > size.X) - { - size.X = nextX; - } - if (h > size.Y) - { - size.Y = h; - } - - x = nextX; - } - break; - } - - key <<= 16; - } - - return size; - } - - /// - /// Determines which item (if any) has been hit. - /// - /// The text. - /// The start. - /// The end. - /// The position. - /// The position. - /// - /// An int. - /// - public int HitTest(string text, int start, int end, float xPos, float yPos) - { - if (start >= end) { return end; } - if (end > text.Length) { end = text.Length; } - if (start < 0) { start = 0; } - - if (xPos < 0) { return -1; } - if (yPos < 0) { return -1; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - float nextX = x + glyph.XAdvance + SpacingX; - float h = y + LineSpacing; - - if (xPos >= x && xPos <= nextX + dx && yPos <= h && yPos >= y) - { - if (xPos < (x + nextX + dx) * 0.5f) { return i; } - return i + 1; - } - x = nextX; - } - break; - } - key <<= 16; - } - - return end; - } - - /// - /// Draws. - /// - /// The draw callback. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void Draw(DrawFont drawCallback, - string text, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - float x = 0; - float y = 0; - - int key = 0; - - int l = text.Length; - for (int i = 0; i < l; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - drawCallback( - _texture, - new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), - glyph.Subrect, - color, rotation, origin, 1.0f, opacity, effects, layerDepth); - - x += glyph.XAdvance + SpacingX; - } - break; - } - - key <<= 16; - } - } - - /// - /// Draws. - /// - /// The draw callback. - /// The text. - /// The start. - /// The end. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void Draw(DrawFont drawCallback, - string text, - int start, - int end, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - if (end <= start || end > text.Length) { end = text.Length; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - drawCallback( - _texture, - new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), - glyph.Subrect, - color, rotation, origin, 1.0f, opacity, effects, layerDepth); - - x += glyph.XAdvance + SpacingX; - } - break; - } - - key <<= 16; - } - } - - /// - /// Draws. - /// - /// The draw callback. - /// The text. - /// The start. - /// The end. - /// The position. - /// The dimension. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void Draw(DrawFont drawCallback, - string text, - int start, - int end, - in Vector2 position, - in Size2F dimension, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - if (end <= start || end > text.Length) { end = text.Length; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - if (x + dx + glyph.Subrect.Width > dimension.Width) { return; } - if (y + glyph.OffsetY + glyph.Subrect.Height > dimension.Height) { return; } - - drawCallback( - _texture, - new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), - glyph.Subrect, - color, rotation, origin, 1.0f, opacity, effects, layerDepth); - - x += glyph.XAdvance + SpacingX; - } - break; - } - - key <<= 16; - } - } - - #endregion - - #region StringBuilder - - /// - /// Measure text. - /// - /// The text. - /// - /// A Vector2. - /// - public Vector2 MeasureText(StringBuilder text) - { - return MeasureText(text, 0, text.Length); - } - - /// - /// Measure text. - /// - /// The text. - /// The start. - /// The end. - /// - /// A Vector2. - /// - public Vector2 MeasureText(StringBuilder text, int start, int end) - { - Vector2 size = Vector2.Zero; - - if (start >= end) { return size; } - if (end > text.Length) { end = text.Length; } - if (start < 0) { start = 0; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - float nextX = x + glyph.XAdvance + SpacingX; - - float h = y + LineSpacing; - if (nextX + dx > size.X) - { - size.X = nextX; - } - if (h > size.Y) - { - size.Y = h; - } - - x = nextX; - } - break; - } - - key <<= 16; - } - - return size; - } - - /// - /// Determines which item (if any) has been hit. - /// - /// The text. - /// The start. - /// The end. - /// The position. - /// The position. - /// - /// An int. - /// - public int HitTest(StringBuilder text, int start, int end, float xPos, float yPos) - { - if (start >= end) { return end; } - if (end > text.Length) { end = text.Length; } - if (start < 0) { start = 0; } - - if (xPos < 0) { return -1; } - if (yPos < 0) { return -1; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - float nextX = x + glyph.XAdvance + SpacingX; - float h = y + LineSpacing; - - if (xPos >= x && xPos <= nextX + dx && yPos <= h && yPos >= y) - { - if (xPos < (x + nextX + dx) * 0.5f) { return i; } - return i + 1; - } - x = nextX; - } - break; - } - key <<= 16; - } - - return end; - } - - /// - /// Draws. - /// - /// The draw callback. - /// The text. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void Draw(DrawFont drawCallback, - StringBuilder text, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - float x = 0; - float y = 0; - - int key = 0; - - int l = text.Length; - for (int i = 0; i < l; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - break; - } - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - drawCallback( - _texture, new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), - glyph.Subrect, - color, rotation, origin, 1.0f, opacity, effects, layerDepth); - - x += glyph.XAdvance + SpacingX; - break; - } - } - - key <<= 16; - } - } - - /// - /// Draws. - /// - /// The draw callback. - /// The text. - /// The start. - /// The end. - /// The position. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void Draw(DrawFont drawCallback, - StringBuilder text, - int start, - int end, - in Vector2 position, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - if (end <= start || end > text.Length) { end = text.Length; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - drawCallback( - _texture, - new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), - glyph.Subrect, - color, rotation, origin, 1.0f, opacity, effects, layerDepth); - - x += glyph.XAdvance + SpacingX; - } - break; - } - - key <<= 16; - } - } - - /// - /// Draws. - /// - /// The draw callback. - /// The text. - /// The start. - /// The end. - /// The position. - /// The dimension. - /// The color. - /// The rotation. - /// The origin. - /// The opacity. - /// The effects. - /// Depth of the layer. - internal void Draw(DrawFont drawCallback, - StringBuilder text, - int start, - int end, - in Vector2 position, - in Size2F dimension, - in Color color, - float rotation, - in Vector2 origin, - float opacity, - SpriteEffects effects, - float layerDepth) - { - if (end <= start || end > text.Length) { end = text.Length; } - - float x = 0; - float y = 0; - - int key = 0; - - for (int i = start; i < end; i++) - { - char c = text[i]; - switch (c) - { - case '\r': - { - key |= c; - continue; - } - case '\n': - { - key = 0; - x = 0; - y += LineSpacing; - } - break; - default: - { - if (!_glyphs.TryGetValue(c, out Glyph glyph)) - { - if (IgnoreUnknownCharacters) - { - continue; - } - - glyph = _defaultGlyph; - } - key |= c; - - float dx = glyph.OffsetX; - if (Kernings.TryGetValue(key, out Kerning kerning)) - { - dx += kerning.Offset; - } - - if (x + dx + glyph.Subrect.Width > dimension.Width) { return; } - if (y + glyph.OffsetY + glyph.Subrect.Height > dimension.Height) { return; } - - drawCallback( - _texture, - new Vector2(position.X + x + dx, position.Y + y + glyph.OffsetY), - glyph.Subrect, - color, rotation, origin, 1.0f, opacity, effects, layerDepth); - - x += glyph.XAdvance + SpacingX; - } - break; - } - - key <<= 16; - } - } - - #endregion - #region IDisposable Support - - /// - /// True if disposed. - /// + private bool _disposed; - - /// - /// Releases the unmanaged resources used by the Exomia.Framework.Graphics.SpriteFont and - /// optionally releases the managed resources. - /// - /// - /// True to release both managed and unmanaged resources; false to - /// release only unmanaged resources. - /// + private void Dispose(bool disposing) { if (!_disposed) @@ -1144,6 +204,14 @@ private void Dispose(bool disposing) } } + /// + /// Finalizes an instance of the class. + /// + ~SpriteFont() + { + Dispose(false); + } + /// public void Dispose() { diff --git a/src/Exomia.Framework/Graphics/SpriteFontContentReader.cs b/src/Exomia.Framework/Graphics/SpriteFontContentReader.cs index e1170373..eb141197 100644 --- a/src/Exomia.Framework/Graphics/SpriteFontContentReader.cs +++ b/src/Exomia.Framework/Graphics/SpriteFontContentReader.cs @@ -15,16 +15,13 @@ namespace Exomia.Framework.Graphics { - /// - /// A sprite font content reader. This class cannot be inherited. - /// sealed class SpriteFontContentReader : IContentReader { /// public object? ReadContent(IContentManager contentManager, ref ContentReaderParameters parameters) { SpriteFont font = ContentSerializer.Read(parameters.Stream); - if (font.ImageData == null) + if (font.ImageData.Length <= 0) { return null; } @@ -34,9 +31,11 @@ sealed class SpriteFontContentReader : IContentReader try { - using MemoryStream ms = new MemoryStream(font.ImageData) { Position = 0 }; - font.Texture = Texture.Load(graphicsDevice.Device, ms) ?? - throw new NullReferenceException($"{nameof(font.Texture)}"); + using (MemoryStream ms = new MemoryStream(font.ImageData) { Position = 0 }) + { + font.Texture = Texture.Load(graphicsDevice.Device, ms) ?? + throw new NullReferenceException($"{nameof(font.Texture)}"); + } } catch { return null; } diff --git a/src/Exomia.Framework/Graphics/SpriteEffects.cs b/src/Exomia.Framework/Graphics/TextureEffects.cs similarity index 91% rename from src/Exomia.Framework/Graphics/SpriteEffects.cs rename to src/Exomia.Framework/Graphics/TextureEffects.cs index f0c735df..5f6759e5 100644 --- a/src/Exomia.Framework/Graphics/SpriteEffects.cs +++ b/src/Exomia.Framework/Graphics/TextureEffects.cs @@ -13,10 +13,10 @@ namespace Exomia.Framework.Graphics { /// - /// Bitfield of flags for specifying SpriteEffects. + /// Bitfield of flags for specifying TextureEffects. /// [Flags] - public enum SpriteEffects + public enum TextureEffects { /// /// A binary constant representing the none flag. diff --git a/src/Exomia.Framework/Graphics/Textures.cs b/src/Exomia.Framework/Graphics/Textures.cs new file mode 100644 index 00000000..0434491c --- /dev/null +++ b/src/Exomia.Framework/Graphics/Textures.cs @@ -0,0 +1,85 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.IO; + +namespace Exomia.Framework.Graphics +{ + /// + /// The built-in textures. This class cannot be inherited. + /// + public class Textures : IDisposable + { + private const string WHITE_TEXTURE_BASE64 = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII="; + + private readonly Texture _white; + + /// + /// Built-in white texture object with size of 1x1 px. + /// + /// + /// The white texture. + /// + public Texture White + { + get { return _white!; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The graphics device. + /// Thrown when a value was unexpectedly null. + internal Textures(IGraphicsDevice graphicsDevice) + { + using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(WHITE_TEXTURE_BASE64))) + { + _white = Texture.Load(graphicsDevice.Device, ms) ?? + throw new NullReferenceException($"{nameof(White)}"); + } + } + + #region IDisposable Support + + private bool _disposed; + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _white.Dispose(); + } + + _disposed = true; + } + } + + /// + ~Textures() + { + Dispose(false); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged/managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/IDrawable.cs b/src/Exomia.Framework/IDrawable.cs index 4230cddc..5045703a 100644 --- a/src/Exomia.Framework/IDrawable.cs +++ b/src/Exomia.Framework/IDrawable.cs @@ -38,7 +38,7 @@ public interface IDrawable /// /// Gets a value indicating whether the method should be called by - /// . + /// the . /// /// /// true if this drawable component is visible; otherwise, false. diff --git a/src/Exomia.Framework/Input/InputState.cs b/src/Exomia.Framework/Input/InputState.cs new file mode 100644 index 00000000..11063622 --- /dev/null +++ b/src/Exomia.Framework/Input/InputState.cs @@ -0,0 +1,28 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +namespace Exomia.Framework.Input +{ + /// + /// Values that represent EventAction. + /// + public enum EventAction + { + /// + /// An enum constant representing the continue option. + /// + Continue = 0, + + /// + /// An enum constant representing the stop propagation option. + /// + StopPropagation = 1, + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Input/KeyEventHandler.cs b/src/Exomia.Framework/Input/KeyEventHandler.cs index ac1fe9b7..357c7d63 100644 --- a/src/Exomia.Framework/Input/KeyEventHandler.cs +++ b/src/Exomia.Framework/Input/KeyEventHandler.cs @@ -18,25 +18,25 @@ namespace Exomia.Framework.Input /// The key value. /// The modifiers. /// - /// true if the key event was handled; false otherwise. + /// An . /// - public delegate bool KeyEventHandler(int keyValue, KeyModifier modifiers); + public delegate EventAction KeyEventHandler(int keyValue, KeyModifier modifiers); /// /// Delegate for handling key press events. /// /// The key. /// - /// true if the key press event was handled; false otherwise. + /// An . /// - public delegate bool KeyPressEventHandler(char key); + public delegate EventAction KeyPressEventHandler(char key); /// /// Delegate for handling raw key events. /// /// The message. /// - /// true if the message event was handled; false otherwise. + /// An . /// - public delegate bool RawKeyEventHandler(in Message message); + public delegate EventAction RawKeyEventHandler(in Message message); } \ No newline at end of file diff --git a/src/Exomia.Framework/Input/MouseEventHandler.cs b/src/Exomia.Framework/Input/MouseEventHandler.cs index a233656b..c7d25cb6 100644 --- a/src/Exomia.Framework/Input/MouseEventHandler.cs +++ b/src/Exomia.Framework/Input/MouseEventHandler.cs @@ -15,7 +15,7 @@ namespace Exomia.Framework.Input /// /// In mouse event information. /// - /// true if the mouse event was handled; false otherwise. + /// An . /// - public delegate bool MouseEventHandler(in MouseEventArgs mouseEventArgs); + public delegate EventAction MouseEventHandler(in MouseEventArgs mouseEventArgs); } \ No newline at end of file diff --git a/src/Exomia.Framework/Mathematics/Arc2.cs b/src/Exomia.Framework/Mathematics/Arc2.cs new file mode 100644 index 00000000..5543a59e --- /dev/null +++ b/src/Exomia.Framework/Mathematics/Arc2.cs @@ -0,0 +1,205 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SharpDX; + +namespace Exomia.Framework.Mathematics +{ + /// + /// A 2d arc. + /// + /// + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 20)] + public readonly struct Arc2 : IFormattable + { + /// + /// The x value. + /// + public readonly float X; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// The y value. + /// + public readonly float Y; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// The radius. + /// + public readonly float Radius; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// The start angle in radians. + /// + public readonly float Start; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// The end angle in radians. + /// + public readonly float End; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// Initializes a new instance of the struct. + /// + /// The x value. + /// The y value. + /// The radius. + /// + /// (Optional) + /// The start angle in radians. + /// + /// + /// (Optional) + /// The end angle in radians. + /// + public Arc2(float x, float y, float radius, float start = 0, float end = MathUtil.TwoPi) + { + X = x; + Y = y; + Radius = radius; + Start = start; + End = end; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The center. + /// The radius. + /// + /// (Optional) + /// The start angle in radians. + /// + /// + /// (Optional) + /// The end angle in radians. + /// + public Arc2(VectorI2 center, float radius, float start = 0, float end = MathUtil.TwoPi) + : this(center.X, center.Y, radius, start, end) { } + + /// + /// Initializes a new instance of the struct. + /// + /// The center. + /// The radius. + /// + /// (Optional) + /// The start angle in radians. + /// + /// + /// (Optional) + /// The end angle in radians. + /// + public Arc2(Vector2 center, float radius, float start = 0, float end = MathUtil.TwoPi) + : this(center.X, center.Y, radius, start, end) { } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; false otherwise. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(in Arc2 other) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return + X == other.X && + Y == other.Y && + Radius == other.Radius && + Start == other.Start && + End == other.End; + + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object value) + { + return value is Arc2 other && Equals(in other); + } + + /// + public override int GetHashCode() + { + return (((((((X.GetHashCode() * 307) ^ Y.GetHashCode()) * 521) ^ Radius.GetHashCode()) * 853) ^ + Start.GetHashCode()) * 443) ^ End.GetHashCode(); + } + + /// + public override string ToString() + { + return string.Format( + CultureInfo.CurrentCulture, + "X:{0} Y:{1} | Radius:{2}, Start:{3}, End:{4}", X, Y, Radius, Start, End); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string? format) + { + if (format == null) + { + return ToString(); + } + + return string.Format( + CultureInfo.CurrentCulture, + "X:{0} Y:{1} | Radius:{2}, Start:{3}, End:{4}", + X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), + Radius.ToString(format, CultureInfo.CurrentCulture), + Start.ToString(format, CultureInfo.CurrentCulture), + End.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format( + formatProvider, + "X:{0} Y:{1} | Radius:{2}, Start:{3}, End:{4}", X, Y, Radius, Start, End); + } + + /// + public string ToString(string? format, IFormatProvider formatProvider) + { + if (format == null) + { + return ToString(formatProvider); + } + + return string.Format( + formatProvider, + "X:{0} Y:{1} | Radius:{2}, Start:{3}, End:{4}", + X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), + Radius.ToString(format, formatProvider), + Start.ToString(format, formatProvider), + End.ToString(format, formatProvider)); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Mathematics/Circle2.cs b/src/Exomia.Framework/Mathematics/Circle2.cs new file mode 100644 index 00000000..35352916 --- /dev/null +++ b/src/Exomia.Framework/Mathematics/Circle2.cs @@ -0,0 +1,162 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SharpDX; + +namespace Exomia.Framework.Mathematics +{ + /// + /// A 2d circle. + /// + /// + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 12)] + public readonly struct Circle2 : IFormattable + { + /// + /// The x value. + /// + public readonly float X; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// The y value. + /// + public readonly float Y; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// The radius. + /// + public readonly float Radius; //Note: do not reorder this field, unless you know what you are doing. + + /// + /// Initializes a new instance of the struct. + /// + /// The x value. + /// The y value. + /// The radius. + public Circle2(float x, float y, float radius) + { + X = x; + Y = y; + Radius = radius; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The center. + /// The radius. + public Circle2(VectorI2 center, float radius) + : this(center.X, center.Y, radius) { } + + /// + /// Initializes a new instance of the struct. + /// + /// The center. + /// The radius. + public Circle2(Vector2 center, float radius) + : this(center.X, center.Y, radius) { } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; false otherwise. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(in Circle2 other) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return + X == other.X && + Y == other.Y && + Radius == other.Radius; + + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object value) + { + return value is Circle2 other && Equals(in other); + } + + /// + public override int GetHashCode() + { + return (((X.GetHashCode() * 307) ^ Y.GetHashCode()) * 521) ^ Radius.GetHashCode(); + } + + /// + public override string ToString() + { + return string.Format( + CultureInfo.CurrentCulture, + "X:{0} Y:{1} | Radius:{2}", X, Y, Radius); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string? format) + { + if (format == null) + { + return ToString(); + } + + return string.Format( + CultureInfo.CurrentCulture, + "X:{0} Y:{1} | Radius:{2}", + X.ToString(format, CultureInfo.CurrentCulture), + Y.ToString(format, CultureInfo.CurrentCulture), + Radius.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format( + formatProvider, + "X:{0} Y:{1} | Radius:{2}", X, Y, Radius); + } + + /// + public string ToString(string? format, IFormatProvider formatProvider) + { + if (format == null) + { + return ToString(formatProvider); + } + + return string.Format( + formatProvider, + "X:{0} Y:{1} | Radius:{2}", + X.ToString(format, formatProvider), + Y.ToString(format, formatProvider), + Radius.ToString(format, formatProvider)); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Mathematics/Extensions/Vector/Vector2Extensions.cs b/src/Exomia.Framework/Mathematics/Extensions/Vector/Vector2Extensions.cs index 163d32bf..913fdc05 100644 --- a/src/Exomia.Framework/Mathematics/Extensions/Vector/Vector2Extensions.cs +++ b/src/Exomia.Framework/Mathematics/Extensions/Vector/Vector2Extensions.cs @@ -15,31 +15,44 @@ namespace Exomia.Framework.Mathematics.Extensions.Vector { /// - /// Vector2Extensions static class. + /// A vector 2 extensions class. /// public static class Vector2Extensions { /// - /// calculate the angle between two vectors. + /// Calculate the angle from the anchor point to another point vector. /// - /// this vec1. - /// vec2. + /// This anchor . + /// The point . /// - /// angle between the two vectors in radians. + /// The angle from anchor vector to the point vector in radians. + /// + public static double AngleTo(this Vector2 anchor, in Vector2 point) + { + return Math.Atan2(point.Y - anchor.Y, point.X - anchor.X); + } + + /// + /// Calculate the angle between two vectors. + /// + /// This . + /// The . + /// + /// The angle between the two vectors in radians. /// public static double AngleBetween(this Vector2 vec1, in Vector2 vec2) { - float scalar = (vec1.X * vec2.X) + (vec1.Y * vec2.Y); - float length = vec1.Length() * vec2.Length(); - return Math.Cos(scalar / length); + return Math.Atan2( + (vec1.X * vec2.Y) - (vec2.X * vec1.Y), + (vec1.X * vec2.X) + (vec1.Y * vec2.Y)); } /// - /// calculates the horizontal angle of a vector2. + /// Calculates the horizontal angle of a . /// - /// this vec. + /// This . /// - /// angle horizontal. + /// The angle horizontal. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double AngleHorizontal(this Vector2 vec) @@ -48,11 +61,11 @@ public static double AngleHorizontal(this Vector2 vec) } /// - /// calculates the vertical angle of a vector2. + /// Calculates the vertical angle of a . /// - /// this vec. + /// This . /// - /// angle vertical. + /// The angle vertical. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double AngleVertical(this Vector2 vec) @@ -61,12 +74,12 @@ public static double AngleVertical(this Vector2 vec) } /// - /// rotate a vector by an angle (in radian) + /// Rotate a by an angle (in radian) /// - /// this vec. + /// This . /// angle. /// - /// new rotated vector2. + /// The new rotated . /// public static Vector2 Rotate(this Vector2 vec, double angle) { @@ -76,12 +89,12 @@ public static Vector2 Rotate(this Vector2 vec, double angle) } /// - /// transforms the vector with a transform matrix. + /// Transforms the with a transform . /// /// this vec. /// transform. /// - /// new vector2. + /// the new . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Transform(this Vector2 vec, in Matrix transform) diff --git a/src/Exomia.Framework/Mathematics/Line2.cs b/src/Exomia.Framework/Mathematics/Line2.cs new file mode 100644 index 00000000..18658738 --- /dev/null +++ b/src/Exomia.Framework/Mathematics/Line2.cs @@ -0,0 +1,297 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SharpDX; + +namespace Exomia.Framework.Mathematics +{ + /// + /// A 2d line. + /// + /// + [StructLayout(LayoutKind.Explicit, Pack = 4, Size = 16)] + public readonly struct Line2 : IFormattable + { + /// + /// The first x value. + /// + [FieldOffset(0)] + public readonly float X1; + + /// + /// The first y value. + /// + [FieldOffset(4)] + public readonly float Y1; + + /// + /// The first xy. + /// + [FieldOffset(0)] + public readonly Vector2 XY1; + + /// + /// The second y value. + /// + [FieldOffset(8)] + public readonly float X2; + + /// + /// The second x value. + /// + [FieldOffset(12)] + public readonly float Y2; + + /// + /// The second xy. + /// + [FieldOffset(8)] + public readonly Vector2 XY2; + + /// + /// Initializes a new instance of the struct. + /// + /// The first x value. + /// The first y value. + /// The second x value. + /// The second y value. + public Line2(float x1, float y1, float x2, float y2) + : this() + { + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + } + + /// + /// Initializes a new instance of the struct. + /// + /// [in,out] The to process. + /// [in,out] The to process. + public Line2(in VectorI2 a, in VectorI2 b) + : this(a.X, a.Y, b.X, b.Y) { } + + /// + /// Initializes a new instance of the struct. + /// + /// [in,out] The to process. + /// [in,out] The to process. + public Line2(in Vector2 a, in Vector2 b) + : this() + { + XY1 = a; + XY2 = b; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; false otherwise. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(in Line2 other) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return + X1 == other.X1 && + Y1 == other.Y1 && + X2 == other.X2 && + Y2 == other.Y2; + + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object value) + { + return value is Line2 other && Equals(in other); + } + + /// + public override int GetHashCode() + { + return (((((X1.GetHashCode() * 307) ^ Y1.GetHashCode()) * 521) ^ X2.GetHashCode()) * 853) ^ + Y2.GetHashCode(); + } + + /// + public override string ToString() + { + return string.Format( + CultureInfo.CurrentCulture, + "X1:{0} Y1:{1} | X2:{2} Y2:{3}", X1, Y1, X2, Y2); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string? format) + { + if (format == null) + { + return ToString(); + } + + return string.Format( + CultureInfo.CurrentCulture, + "X1:{0} Y1:{1} | X2:{2} Y2:{3}", + X1.ToString(format, CultureInfo.CurrentCulture), + Y1.ToString(format, CultureInfo.CurrentCulture), + X2.ToString(format, CultureInfo.CurrentCulture), + Y2.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format( + formatProvider, + "X1:{0} Y1:{1} | X2:{2} Y2:{3}", X1, Y1, X2, Y2); + } + + /// + public string ToString(string? format, IFormatProvider formatProvider) + { + if (format == null) + { + return ToString(formatProvider); + } + + return string.Format( + formatProvider, + "X1:{0} Y1:{1} | X2:{2} Y2:{3}", + X1.ToString(format, formatProvider), + Y1.ToString(format, formatProvider), + X2.ToString(format, formatProvider), + Y2.ToString(format, formatProvider)); + } + + /// + /// Intersect with other . + /// + /// The to compare with this instance. + /// [out] The intersection point. + /// + /// True if it succeeds, false if it fails. + /// + public bool IntersectWith(in Line2 other, out Vector2 intersectionPoint) + { + float a1 = Y2 - Y1; + float b1 = X1 - X2; + float c1 = (a1 * X1) + (b1 * Y1); + + float a2 = other.Y2 - other.Y1; + float b2 = other.X1 - other.X2; + float c2 = (a2 * other.X1) + (b2 * other.Y1); + + float det = (a1 * b2) - (a2 * b1); + + if (det == 0.0f) + { + intersectionPoint = default; + return false; + } + + intersectionPoint.X = ((b2 * c1) - (b1 * c2)) / det; + intersectionPoint.Y = ((a1 * c2) - (a2 * c1)) / det; + return true; + } + + /// + /// Gets a perpendicular from this line. + /// + /// The offset. + /// + /// The perpendicular. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Line2 GetPerpendicular(float offset) + { + float dx = X2 - X1; + float dy = Y2 - Y1; + + double dl = Math.Sqrt((dx * dx) + (dy * dy)); + float nx = (float)((dy / dl) * offset); + float ny = (float)((dx / dl) * offset); + + return new Line2(X1 - nx, Y1 + ny, X2 - nx, Y2 + ny); + } + + /// + /// Rotates the line around the given . + /// + /// The line. + /// The rotation (in radians). + /// The origin. + /// + /// A new . + /// + public static Line2 RotateAround(in Line2 line, float rotation, in Vector2 origin) + { + double sin = Math.Sin(rotation); + double cos = Math.Cos(rotation); + + float x1 = line.X1 - origin.X; + float y1 = line.Y1 - origin.Y; + + float x2 = line.X2 - origin.X; + float y2 = line.Y2 - origin.Y; + + return new Line2( + (float)((x1 * cos) - (y1 * sin)) + origin.X, (float)((x1 * sin) + (y1 * cos)) + origin.Y, + (float)((x2 * cos) - (y2 * sin)) + origin.X, (float)((x2 * sin) + (y2 * cos)) + origin.Y); + } + + /// + /// Creates a line out of and , as well the perpendicular from this line. + /// + /// [in,out] The first ref . + /// [in,out] The second ref . + /// The offset. + /// [out] The perpendicular. + /// + /// The line created from and . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Line2 CreateWithPerpendicular(ref Vector2 p1, + ref Vector2 p2, + float offset, + out Line2 perpendicular) + { + float dx = p2.X - p1.X; + float dy = p2.Y - p1.Y; + + double dl = Math.Sqrt((dx * dx) + (dy * dy)); + float nx = (float)((dy / dl) * offset); + float ny = (float)((dx / dl) * offset); + + perpendicular = new Line2(p1.X - nx, p1.Y + ny, p2.X - nx, p2.Y + ny); + return new Line2(p1.X, p1.Y, p2.X, p2.Y); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Mathematics/Triangle2.cs b/src/Exomia.Framework/Mathematics/Triangle2.cs new file mode 100644 index 00000000..19b207d3 --- /dev/null +++ b/src/Exomia.Framework/Mathematics/Triangle2.cs @@ -0,0 +1,256 @@ +#region License + +// Copyright (c) 2018-2020, exomia +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +#endregion + +using System; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using SharpDX; + +namespace Exomia.Framework.Mathematics +{ + /// + /// A 2d triangle. + /// + /// + [StructLayout(LayoutKind.Explicit, Pack = 4, Size = 24)] + public readonly struct Triangle2 : IFormattable + { + /// + /// The first x value. + /// + [FieldOffset(0)] + public readonly float X1; + + /// + /// The first y value. + /// + [FieldOffset(4)] + public readonly float Y1; + + /// + /// The first xy. + /// + [FieldOffset(0)] + public readonly Vector2 XY1; + + /// + /// The second y value. + /// + [FieldOffset(8)] + public readonly float X2; + + /// + /// The second x value. + /// + [FieldOffset(12)] + public readonly float Y2; + + /// + /// The second xy. + /// + [FieldOffset(8)] + public readonly Vector2 XY2; + + /// + /// The third x value. + /// + [FieldOffset(16)] + public readonly float X3; + + /// + /// The third y value. + /// + [FieldOffset(20)] + public readonly float Y3; + + /// + /// The third xy. + /// + [FieldOffset(16)] + public readonly Vector2 XY3; + + /// + /// Initializes a new instance of the struct. + /// + /// The first x value. + /// The first y value. + /// The second x value. + /// The second y value. + /// The third x value. + /// The third y value. + public Triangle2(float x1, float y1, float x2, float y2, float x3, float y3) + : this() + { + X1 = x1; + Y1 = y1; + X2 = x2; + Y2 = y2; + X3 = x3; + Y3 = y3; + } + + /// + /// Initializes a new instance of the struct. + /// + /// The to process. + /// The to process. + /// The to process. + public Triangle2(in VectorI2 a, in VectorI2 b, in VectorI2 c) + : this(a.X, a.Y, b.X, b.Y, c.X, c.Y) { } + + /// + /// Initializes a new instance of the struct. + /// + /// The to process. + /// The to process. + /// The to process. + public Triangle2(in Vector2 a, in Vector2 b, in Vector2 c) + : this() + { + XY1 = a; + XY2 = b; + XY3 = c; + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; false otherwise. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(in Triangle2 other) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + return + X1 == other.X1 && + Y1 == other.Y1 && + X2 == other.X2 && + Y2 == other.Y2 && + X3 == other.X3 && + Y3 == other.Y3; + + // ReSharper restore CompareOfFloatsByEqualityOperator + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object value) + { + return value is Triangle2 other && Equals(in other); + } + + /// + public override int GetHashCode() + { + return (((((((((X1.GetHashCode() * 307) ^ Y1.GetHashCode()) * 521) ^ X2.GetHashCode()) * 853) ^ + Y2.GetHashCode()) * 443) ^ X3.GetHashCode()) * 937) ^ Y3.GetHashCode(); + } + + /// + public override string ToString() + { + return string.Format( + CultureInfo.CurrentCulture, + "X1:{0} Y1:{1} | X2:{2} Y2:{3} | X3:{4} Y3:{5}", + X1, Y1, X2, Y2, X3, Y3); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string? format) + { + if (format == null) + { + return ToString(); + } + + return string.Format( + CultureInfo.CurrentCulture, + "X1:{0} Y1:{1} | X2:{2} Y2:{3} | X3:{4} Y3:{5}", + X1.ToString(format, CultureInfo.CurrentCulture), + Y1.ToString(format, CultureInfo.CurrentCulture), + X2.ToString(format, CultureInfo.CurrentCulture), + Y2.ToString(format, CultureInfo.CurrentCulture), + X3.ToString(format, CultureInfo.CurrentCulture), + Y3.ToString(format, CultureInfo.CurrentCulture)); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return string.Format( + formatProvider, + "X1:{0} Y1:{1} | X2:{2} Y2:{3} | X3:{4} Y3:{5}", + X1, Y1, X2, Y2, X3, Y3); + } + + /// + public string ToString(string? format, IFormatProvider formatProvider) + { + if (format == null) + { + return ToString(formatProvider); + } + + return string.Format( + formatProvider, + "X1:{0} Y1:{1} | X2:{2} Y2:{3} | X3:{4} Y3:{5}", + X1.ToString(format, formatProvider), + Y1.ToString(format, formatProvider), + X2.ToString(format, formatProvider), + Y2.ToString(format, formatProvider), + X3.ToString(format, formatProvider), + Y3.ToString(format, formatProvider)); + } + + /// + /// Rotates the triangle around the given . + /// + /// The triangle. + /// The rotation (in radians). + /// The origin. + /// + /// A new . + /// + public static Triangle2 RotateAround(in Triangle2 triangle, float rotation, in Vector2 origin) + { + double sin = Math.Sin(rotation); + double cos = Math.Cos(rotation); + + float x1 = triangle.X1 - origin.X; + float y1 = triangle.Y1 - origin.Y; + + float x2 = triangle.X2 - origin.X; + float y2 = triangle.Y2 - origin.Y; + + float x3 = triangle.X3 - origin.X; + float y3 = triangle.Y3 - origin.Y; + + return new Triangle2( + (float)((x1 * cos) - (y1 * sin)) + origin.X, (float)((x1 * sin) + (y1 * cos)) + origin.Y, + (float)((x2 * cos) - (y2 * sin)) + origin.X, (float)((x2 * sin) + (y2 * cos)) + origin.Y, + (float)((x3 * cos) - (y3 * sin)) + origin.X, (float)((x3 * sin) + (y3 * cos)) + origin.Y); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Resources/Resources.cs b/src/Exomia.Framework/Resources/Resources.cs index c352b662..a8774b97 100644 --- a/src/Exomia.Framework/Resources/Resources.cs +++ b/src/Exomia.Framework/Resources/Resources.cs @@ -105,5 +105,10 @@ public static class Shaders /// The position color texture2 shader resource path. /// public const string POSITION_NORMAL_TEXTURE = "Resources.shaders.position_normal_texture.ehlsl"; + + /// + /// The canvas shader resource path. + /// + public const string CANVAS = "Resources.shaders.canvas.ehlsl"; } } \ No newline at end of file diff --git a/src/Exomia.Framework/Resources/shaders/canvas.ehlsl b/src/Exomia.Framework/Resources/shaders/canvas.ehlsl new file mode 100644 index 00000000..17bcaeea --- /dev/null +++ b/src/Exomia.Framework/Resources/shaders/canvas.ehlsl @@ -0,0 +1,195 @@ +/** Shaderdefinition + * + * group DEFAULT + * vs VSMain vs_5_0 OptimizationLevel3 + * ps PSMain ps_5_0 OptimizationLevel3 + */ + +Texture2D g_Texture1 : register(t0); +Texture2D g_Texture2 : register(t1); +Texture2D g_Texture3 : register(t2); +Texture2D g_Texture4 : register(t3); +Texture2D g_Texture5 : register(t4); +Texture2D g_Texture6 : register(t5); +Texture2D g_Texture7 : register(t6); +Texture2D g_Texture8 : register(t7); + + +Texture2D g_FontTexture1 : register(t8); +Texture2D g_FontTexture2 : register(t9); +Texture2D g_FontTexture3 : register(t10); +Texture2D g_FontTexture4 : register(t11); + +SamplerState g_Sampler : register(s0); + +cbuffer PerFrame : register(b0) +{ + float4x4 g_WorldViewProjectionMatrix; +}; + +struct VS_INPUT +{ + float4 p : SV_POSITION0; + float4 c : COLOR0; + float2 uv : TEXCOORD0; + float2 mo : TEXCOORD1; +}; + +struct PS_INPUT +{ + float4 p : SV_POSITION0; + float4 c : COLOR0; + float2 uv : TEXCOORD0; + float2 mo : TEXCOORD1; + float2 zw : TEXCOORD2; +}; + +PS_INPUT VSMain(VS_INPUT input) +{ + PS_INPUT output = (PS_INPUT)0; + output.zw = input.p.zw; + output.p = mul(float4(input.p.xy, 0.0f, 1.0f), g_WorldViewProjectionMatrix); + output.c = input.c / 255.0f; + output.uv = input.uv; + output.mo = input.mo; + return output; +} + +#define COLOR_MODE 0.0f +#define TEXTURE_MODE 1.0f +#define FONT_TEXTURE_MODE 2.0f +#define FILL_CIRCLE_MODE 3.0f +#define FILL_CIRCLE_ARC_MODE 4.0f +#define BORDER_CIRCLE_MODE 5.0f +#define BORDER_CIRCLE_ARC_MODE 6.0f + +#define PI 3.141593f + +float4 PSMain(PS_INPUT input) : SV_TARGET +{ + switch (input.mo.x) + { + default: + case COLOR_MODE: + return input.c; + case TEXTURE_MODE: + { + switch (input.mo.y) + { + case 0.0f: + return g_Texture1.Sample(g_Sampler, input.uv) * input.c; + case 1.0f: + return g_Texture2.Sample(g_Sampler, input.uv) * input.c; + case 2.0f: + return g_Texture3.Sample(g_Sampler, input.uv) * input.c; + case 3.0f: + return g_Texture4.Sample(g_Sampler, input.uv) * input.c; + case 4.0f: + return g_Texture5.Sample(g_Sampler, input.uv) * input.c; + case 5.0f: + return g_Texture6.Sample(g_Sampler, input.uv) * input.c; + case 6.0f: + return g_Texture7.Sample(g_Sampler, input.uv) * input.c; + } + return g_Texture8.Sample(g_Sampler, input.uv) * input.c; + } + case FONT_TEXTURE_MODE: + { + switch (input.mo.y) + { + case 0.0f: + return g_FontTexture1.Sample(g_Sampler, input.uv) * input.c; + case 1.0f: + return g_FontTexture2.Sample(g_Sampler, input.uv) * input.c; + case 2.0f: + return g_FontTexture3.Sample(g_Sampler, input.uv) * input.c; + } + return g_FontTexture4.Sample(g_Sampler, input.uv) * input.c; + } + case FILL_CIRCLE_MODE: + { + float2 p = input.p; + float2 center = input.zw; + float radius = input.mo.y; + + float2 d = center - p; + float ls = (d.x * d.x) + (d.y * d.y); + + if (ls > radius * radius) + discard; + + return input.c; + } + case FILL_CIRCLE_ARC_MODE: + { + float2 p = input.p; + float2 center = input.zw; + float radius = input.mo.y; + + float2 d = center - p; + float ls = (d.x * d.x) + (d.y * d.y); + + if (ls < radius * radius) + { + float start = input.uv.x; + float end = input.uv.y; + + float anglePositive = atan2(d.y, d.x) + PI; + float angleNegative = atan2(d.y, d.x) - PI; + + if (anglePositive >= start && anglePositive <= end || + angleNegative >= start && angleNegative <= end) + { + return input.c; + } + } + discard; + return float4(0, 0, 0, 0); + } + case BORDER_CIRCLE_MODE: + { + float2 p = input.p; + float2 center = input.zw; + + float2 d = center - p; + float ls = (d.x * d.x) + (d.y * d.y); + + float r = float(((uint)input.mo.y >> 16) / 10.0f); + float l = float(((uint)input.mo.y & 0xffff) / 10.0f); + + if (ls > r * r || ls < l * l) + discard; + + return input.c; + } + case BORDER_CIRCLE_ARC_MODE: + { + float2 p = input.p; + float2 center = input.zw; + + float2 d = center - p; + float ls = (d.x * d.x) + (d.y * d.y); + + float r = float(((uint) input.mo.y >> 16) / 10.0f); + float l = float(((uint) input.mo.y & 0xffff) / 10.0f); + + if (ls < r * r && ls > l * l) + { + float start = input.uv.x; + float end = input.uv.y; + + float anglePositive = atan2(d.y, d.x) + PI; + float angleNegative = atan2(d.y, d.x) - PI; + + if (anglePositive >= start && anglePositive <= end || + angleNegative >= start && angleNegative <= end) + { + return input.c; + } + } + + discard; + return float4(0, 0, 0, 0); + } + } +} \ No newline at end of file diff --git a/src/Exomia.Framework/Resources/shaders/position_color.ehlsl b/src/Exomia.Framework/Resources/shaders/position_color.ehlsl index 54154171..63b481b8 100644 --- a/src/Exomia.Framework/Resources/shaders/position_color.ehlsl +++ b/src/Exomia.Framework/Resources/shaders/position_color.ehlsl @@ -1,6 +1,6 @@ /** Shaderdefinition * - * technique DEFAULT + * group DEFAULT * vs VSMain vs_5_0 OptimizationLevel3 * ps PSMain ps_5_0 OptimizationLevel3 */ diff --git a/src/Exomia.Framework/Resources/shaders/position_color_texture.ehlsl b/src/Exomia.Framework/Resources/shaders/position_color_texture.ehlsl index 4d73e51f..b957adaa 100644 --- a/src/Exomia.Framework/Resources/shaders/position_color_texture.ehlsl +++ b/src/Exomia.Framework/Resources/shaders/position_color_texture.ehlsl @@ -1,6 +1,6 @@ /** Shaderdefinition * - * technique DEFAULT + * group DEFAULT * vs VSMain vs_5_0 OptimizationLevel3 * ps PSMain ps_5_0 OptimizationLevel3 */ diff --git a/src/Exomia.Framework/Resources/shaders/position_normal_texture.ehlsl b/src/Exomia.Framework/Resources/shaders/position_normal_texture.ehlsl index dfebbd8c..260428d9 100644 --- a/src/Exomia.Framework/Resources/shaders/position_normal_texture.ehlsl +++ b/src/Exomia.Framework/Resources/shaders/position_normal_texture.ehlsl @@ -1,18 +1,18 @@ /** Shaderdefinition * - * technique VERTEX_LIGHTING_PHONG + * group VERTEX_LIGHTING_PHONG * vs VS_VERTEX_LIGHTING_PHONG vs_5_0 OptimizationLevel3 * ps PS_VERTEX_LIGHTING ps_5_0 OptimizationLevel3 * - * technique VERTEX_LIGHTING_BLINNPHONG + * group VERTEX_LIGHTING_BLINNPHONG * vs VS_VERTEX_LIGHTING_BLINNPHONG vs_5_0 OptimizationLevel3 * ps PS_VERTEX_LIGHTING ps_5_0 OptimizationLevel3 * - * technique PIXEL_LIGHTING_PHONG + * group PIXEL_LIGHTING_PHONG * vs VS_PIXEL_LIGHTING_PHONG vs_5_0 OptimizationLevel3 * ps PS_PIXEL_LIGHTING_PHONG ps_5_0 OptimizationLevel3 * - * technique PIXEL_LIGHTING_BLINNPHONG + * group PIXEL_LIGHTING_BLINNPHONG * vs VS_PIXEL_LIGHTING_BLINNPHONG vs_5_0 OptimizationLevel3 * ps PS_PIXEL_LIGHTING_BLINNPHONG ps_5_0 OptimizationLevel3 */ @@ -40,15 +40,15 @@ struct Material //-------------------------------------------------------------------------------------- cbuffer PerFrame : register(b0) { - float4x4 g_WorldMatrix; float4x4 g_ViewMatrix; float4x4 g_ProjectionMatrix; - float4x4 g_WorldViewProjectionMatrix; + float4x4 g_ViewProjectionMatrix; float3 g_EyeVector; }; cbuffer PerObject : register(b1) { + float4x4 g_TransformMatrix; Material g_Material; }; @@ -59,7 +59,7 @@ struct VS_INPUT { float4 p : SV_POSITION0; float3 n : NORMAL0; - float2 t : TEXCOORD0; + float2 t : TEXCOORD0; }; //-------------------------------------------------------------------------------------- @@ -74,10 +74,10 @@ struct PS_INPUT_PV struct PS_INPUT_PP_PHONG { - float4 p : SV_POSITION; + float4 p : SV_POSITION; float4 wp : POSITION; - float2 t : TEXCOORD; - float3 n : TEXCOORD1; + float2 t : TEXCOORD; + float3 n : TEXCOORD1; }; struct PS_INPUT_PP_BLINNPHONG @@ -131,13 +131,13 @@ PS_INPUT_PV VS_VERTEX_LIGHTING_PHONG(VS_INPUT input) PS_INPUT_PV output; //transform position to clip space - output.p = mul(input.p, g_WorldViewProjectionMatrix); + output.p = mul(input.p, mul(g_TransformMatrix, g_ViewProjectionMatrix)); //set texture coords output.t = input.t; //calculate lighting vectors - float3 n = normalize(mul(input.n, (float3x3)g_WorldMatrix)); + float3 n = normalize(mul(input.n, (float3x3)g_TransformMatrix)); float3 v = normalize(g_EyeVector - (float3)input.p); //DONOT USE -light.dir since the reflection returns a ray from the surface float3 r = reflect(light.direction, n); @@ -156,14 +156,14 @@ PS_INPUT_PV VS_VERTEX_LIGHTING_BLINNPHONG(VS_INPUT input) PS_INPUT_PV output; //transform position to clip space - output.p = mul(input.p, g_WorldViewProjectionMatrix); + output.p = mul(input.p, mul(g_TransformMatrix, g_ViewProjectionMatrix)); //set texture coords output.t = input.t; - + //calculate lighting - float3 n = normalize(mul(input.n, (float3x3)g_WorldMatrix)); - float3 v = normalize(g_EyeVector - (float3)input.p); + float3 n = normalize(mul(input.n, (float3x3)g_TransformMatrix)); + float3 v = normalize(g_EyeVector - (float3)input.p); float3 h = normalize(-light.direction + v); //calculate per vertex lighting intensity and interpolate it like a color @@ -178,7 +178,7 @@ PS_INPUT_PV VS_VERTEX_LIGHTING_BLINNPHONG(VS_INPUT input) float4 PS_VERTEX_LIGHTING(PS_INPUT_PV input) : SV_Target { //with texturing - return input.i * g_Texture.Sample(g_Sampler, input.t); + return input.i * g_Texture.Sample(g_Sampler, input.t); } //-------------------------------------------------------------------------------------- @@ -189,14 +189,14 @@ PS_INPUT_PP_PHONG VS_PIXEL_LIGHTING_PHONG(VS_INPUT input) PS_INPUT_PP_PHONG output; //transform position to clip space - keep worldspace position - output.wp = mul(input.p, g_WorldMatrix); - output.p = mul(input.p, g_WorldViewProjectionMatrix); + output.wp = mul(input.p, g_TransformMatrix); + output.p = mul(input.p, mul(g_TransformMatrix, g_ViewProjectionMatrix)); //set texture coords output.t = input.t; //set required lighting vectors for interpolation - output.n = normalize(mul(input.n, (float3x3)g_WorldMatrix)); + output.n = normalize(mul(input.n, (float3x3) g_TransformMatrix)); return output; } @@ -225,14 +225,14 @@ PS_INPUT_PP_BLINNPHONG VS_PIXEL_LIGHTING_BLINNPHONG(VS_INPUT input) PS_INPUT_PP_BLINNPHONG output; //set position into clip space - output.p = mul(input.p, g_WorldViewProjectionMatrix); + output.p = mul(input.p, mul(g_TransformMatrix, g_ViewProjectionMatrix)); //set texture coords output.t = input.t; //set required lighting vectors for interpolation float3 v = normalize(g_EyeVector - (float3)input.p); - output.n = normalize(mul(input.n, (float3x3)g_WorldMatrix)); + output.n = normalize(mul(input.n, (float3x3)g_TransformMatrix)); output.h = normalize(-light.direction + v); return output; diff --git a/src/Exomia.Framework/UpdateableComparer.cs b/src/Exomia.Framework/UpdateableComparer.cs index 3a05025a..aa935194 100644 --- a/src/Exomia.Framework/UpdateableComparer.cs +++ b/src/Exomia.Framework/UpdateableComparer.cs @@ -30,16 +30,6 @@ public int Compare(IUpdateable left, IUpdateable right) return 0; } - if (left == null) - { - return 1; - } - - if (right == null) - { - return -1; - } - return left.UpdateOrder < right.UpdateOrder ? 1 : -1; } } diff --git a/src/Exomia.Framework/VectorI2.cs b/src/Exomia.Framework/VectorI2.cs index 5baa599e..9a15baad 100644 --- a/src/Exomia.Framework/VectorI2.cs +++ b/src/Exomia.Framework/VectorI2.cs @@ -79,8 +79,7 @@ public VectorI2(int x, int y) /// /// The to compare with this instance. /// - /// true if the specified is equal to this instance; otherwise, - /// false. + /// true if the specified is equal to this instance; false otherwise. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(in VectorI2 other) @@ -94,11 +93,7 @@ public readonly bool Equals(in VectorI2 other) [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly bool Equals(object value) { - if (value is VectorI2 other) - { - return Equals(in other); - } - return false; + return value is VectorI2 other && Equals(in other); } /// diff --git a/src/Exomia.Framework/VectorI3.cs b/src/Exomia.Framework/VectorI3.cs index 98e1d392..173ab08b 100644 --- a/src/Exomia.Framework/VectorI3.cs +++ b/src/Exomia.Framework/VectorI3.cs @@ -105,8 +105,7 @@ public VectorI3(in VectorI2 value, int z) /// /// The to compare with this instance. /// - /// true if the specified is equal to this instance; - /// false otherwise. + /// true if the specified is equal to this instance; false otherwise. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool Equals(in VectorI3 other) @@ -121,11 +120,7 @@ public readonly bool Equals(in VectorI3 other) [MethodImpl(MethodImplOptions.AggressiveInlining)] public override readonly bool Equals(object value) { - if (value is VectorI3 other) - { - return Equals(in other); - } - return false; + return value is VectorI3 other && Equals(in other); } /// diff --git a/src/Exomia.Framework/Win32/Mem.cs b/src/Exomia.Framework/Win32/Mem.cs index 504fc4bd..31a240f5 100644 --- a/src/Exomia.Framework/Win32/Mem.cs +++ b/src/Exomia.Framework/Win32/Mem.cs @@ -8,6 +8,8 @@ #endregion +using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security; @@ -47,5 +49,42 @@ public static extern void Cpy(void* dest, public static extern void Move(void* dest, void* src, int count); + + /// + /// memset call + /// Sets the first num bytes of the block of memory pointed by ptr to the specified value + /// (interpreted as an unsigned char). + /// + /// [in,out] destination addr. + /// value to be set. + /// count of bytes. + /// + /// Null if it fails, else a void*. + /// + [SuppressUnmanagedCodeSecurity] + [DllImport( + "msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] + public static extern void* Set(void* dest, + int value, + int count); + + /// + /// Resizes the given array with the given to the given + /// . + /// + /// Generic type parameter. + /// [in,out] The source array ptr. + /// [in,out] The length of the source array. + /// The length of the new array. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Resize(ref T* src, ref int srcLength, int newLength) where T : unmanaged + { + T* ptr = (T*)Marshal.AllocHGlobal(sizeof(T) * newLength); + Cpy(ptr, src, srcLength * sizeof(T)); + Marshal.FreeHGlobal(new IntPtr(src)); + + src = ptr; + srcLength = newLength; + } } } \ No newline at end of file diff --git a/tools/Exomia.Framework.ContentManager/AboutForm.cs b/tools/Exomia.Framework.ContentManager/AboutForm.cs index 092f05f8..38f9fdb0 100644 --- a/tools/Exomia.Framework.ContentManager/AboutForm.cs +++ b/tools/Exomia.Framework.ContentManager/AboutForm.cs @@ -24,7 +24,7 @@ public AboutForm() private void OnLoad(object sender, EventArgs e) { - var assembly = Assembly.GetExecutingAssembly(); + Assembly? assembly = Assembly.GetExecutingAssembly(); Text = $"About {assembly.GetCustomAttribute().Title}"; productNameLbl.Text = assembly.GetCustomAttribute().Product; versionLbl.Text = $"Version {assembly.GetName().Version}"; diff --git a/tools/Exomia.Framework.ContentManager/CreateProjectForm.cs b/tools/Exomia.Framework.ContentManager/CreateProjectForm.cs index 32ec1325..f634f7ad 100644 --- a/tools/Exomia.Framework.ContentManager/CreateProjectForm.cs +++ b/tools/Exomia.Framework.ContentManager/CreateProjectForm.cs @@ -37,13 +37,13 @@ public CreateProjectForm() /// public ProjectFile CreateProjectFile() { - var directoryInfo = new DirectoryInfo(Path.Combine(locationTb.Text, nameTb.Text)); + DirectoryInfo? directoryInfo = new DirectoryInfo(Path.Combine(locationTb.Text, nameTb.Text)); if (!directoryInfo.Exists) { directoryInfo.Create(); } - var projectFile = new ProjectFile(nameTb.Text, directoryInfo.FullName) + ProjectFile? projectFile = new ProjectFile(nameTb.Text, directoryInfo.FullName) { Content = new ContentPropertyGridItem { @@ -73,7 +73,7 @@ private void button1_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(locationTb.Text) || string.IsNullOrEmpty(nameTb.Text)) { return; } - var fileInfo = new FileInfo(Path.Combine(locationTb.Text, nameTb.Text, $"{nameTb.Text}.ecp")); + FileInfo? fileInfo = new FileInfo(Path.Combine(locationTb.Text, nameTb.Text, $"{nameTb.Text}.ecp")); if (fileInfo.Exists) { // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault diff --git a/tools/Exomia.Framework.ContentManager/Json.cs b/tools/Exomia.Framework.ContentManager/Json.cs index 880e6580..9b12e199 100644 --- a/tools/Exomia.Framework.ContentManager/Json.cs +++ b/tools/Exomia.Framework.ContentManager/Json.cs @@ -42,8 +42,8 @@ public static void Serialize(string filePath, object value) public static T? Deserialize(Stream s) where T : class { - using (var sr = new StreamReader(s)) - using (var jr = new JsonTextReader(sr)) + using (StreamReader? sr = new StreamReader(s)) + using (JsonTextReader? jr = new JsonTextReader(sr)) { return s_jsonSerializer.Deserialize(jr); } @@ -51,7 +51,7 @@ public static void Serialize(string filePath, object value) public static T? Deserialize(string filePath) where T : class { - using (var sr = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) + using (FileStream? sr = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None)) { return Deserialize(sr); } diff --git a/tools/Exomia.Framework.ContentManager/MainForm.Build.cs b/tools/Exomia.Framework.ContentManager/MainForm.Build.cs index 8c54a466..588747a9 100644 --- a/tools/Exomia.Framework.ContentManager/MainForm.Build.cs +++ b/tools/Exomia.Framework.ContentManager/MainForm.Build.cs @@ -48,7 +48,7 @@ private void CancelBuild() int skipped = 0; int failed = 0; - var rootNode = treeView1.Nodes[ROOT_KEY_PREFIX]; + TreeNode? rootNode = treeView1.Nodes[ROOT_KEY_PREFIX]; async Task ForTreeNode(TreeNode node, ContentPropertyGridItem contentPropertyGridItem) { diff --git a/tools/Exomia.Framework.ContentManager/MainForm.Core.cs b/tools/Exomia.Framework.ContentManager/MainForm.Core.cs index 405bf453..885c5b82 100644 --- a/tools/Exomia.Framework.ContentManager/MainForm.Core.cs +++ b/tools/Exomia.Framework.ContentManager/MainForm.Core.cs @@ -104,7 +104,8 @@ private void EditItem(TreeNode node) Path.Combine(_projectFile!.Location, item.VirtualPath, item.Name)); if (description == null) { return; } - using (var jsonEditorForm = new JsonEditorForm(description) { Text = $"Edit font '{item.Name}'" }) + using (JsonEditorForm? jsonEditorForm = + new JsonEditorForm(description) { Text = $"Edit font '{item.Name}'" }) { if (jsonEditorForm.ShowDialog() != DialogResult.OK) { diff --git a/tools/Exomia.Framework.ContentManager/MainForm.Events.cs b/tools/Exomia.Framework.ContentManager/MainForm.Events.cs index 28f46fcb..ff339abd 100644 --- a/tools/Exomia.Framework.ContentManager/MainForm.Events.cs +++ b/tools/Exomia.Framework.ContentManager/MainForm.Events.cs @@ -116,8 +116,8 @@ private void openToolStripMenuItem_Click(object sender, EventArgs e) .OfType() .OrderBy(p => p.VirtualPath!.Length)) { - var n = GetNodeFromPath(node, f.VirtualPath!) - ?? throw new InvalidDataException("The project file is corrupt!"); + TreeNode? n = GetNodeFromPath(node, f.VirtualPath!) + ?? throw new InvalidDataException("The project file is corrupt!"); int nodeCount = n.GetNodeCount(false); n = n.Nodes.Add( $"{FOLDER_KEY_PREFIX}{nodeCount}", f.Name, 1, 1); @@ -129,8 +129,8 @@ private void openToolStripMenuItem_Click(object sender, EventArgs e) .Resources .OfType()) { - var n = GetNodeFromPath(node, i.VirtualPath!) - ?? throw new InvalidDataException("The project file is corrupt!"); + TreeNode? n = GetNodeFromPath(node, i.VirtualPath!) + ?? throw new InvalidDataException("The project file is corrupt!"); int nodeCount = n.GetNodeCount(false); n = n.Nodes.Add( $"{FONT_KEY_PREFIX}{nodeCount}", i.Name, 4, 4); @@ -364,7 +364,7 @@ private void addFolderToolStripMenuItem_Click(object sender, EventArgs e) treeView1.InvokeIfRequired( x => { - var selectedNode = x.SelectedNode ?? x.TopNode; + TreeNode? selectedNode = x.SelectedNode ?? x.TopNode; if (selectedNode == null) { return; } int selectedNodeCount = selectedNode.GetNodeCount(false); @@ -375,7 +375,7 @@ private void addFolderToolStripMenuItem_Click(object sender, EventArgs e) di.Create(); } - var node = selectedNode.Nodes.Add($"{FOLDER_KEY_PREFIX}{selectedNodeCount}", di.Name, 1, 1); + TreeNode? node = selectedNode.Nodes.Add($"{FOLDER_KEY_PREFIX}{selectedNodeCount}", di.Name, 1, 1); node.Tag = _projectFile.AddResource( new FolderPropertyGridItem { @@ -409,11 +409,11 @@ private void addFontToolStripMenuItem_Click(object sender, EventArgs e) treeView1.InvokeIfRequired( x => { - var selectedNode = x.SelectedNode ?? x.TopNode; + TreeNode? selectedNode = x.SelectedNode ?? x.TopNode; if (selectedNode == null) { return; } int selectedNodeCount = selectedNode.GetNodeCount(false); - using (var jsonEditorForm = new JsonEditorForm( + using (JsonEditorForm? jsonEditorForm = new JsonEditorForm( new FontDescription { Name = "Arial", @@ -442,7 +442,7 @@ private void addFontToolStripMenuItem_Click(object sender, EventArgs e) jsonEditorForm.Save(fntFilePath); - var node = selectedNode.Nodes.Add( + TreeNode? node = selectedNode.Nodes.Add( $"{FONT_KEY_PREFIX}{selectedNodeCount}", Path.GetFileName(fntFilePath), 4, 4); node.Tag = _projectFile.AddResource( diff --git a/tools/Exomia.Framework.ContentManager/MainForm.cs b/tools/Exomia.Framework.ContentManager/MainForm.cs index 90896190..97e86dd4 100644 --- a/tools/Exomia.Framework.ContentManager/MainForm.cs +++ b/tools/Exomia.Framework.ContentManager/MainForm.cs @@ -78,29 +78,6 @@ public void SetProgressbarValue(bool state) }); } - private static bool IsNumber(object? value) - { - return value is sbyte - || value is byte - || value is short - || value is ushort - || value is int - || value is uint - || value is long - || value is ulong - || value is float - || value is double - || value is decimal; - } - - private static void ForAll(Action action, params T[] items) - { - foreach (T item in items) - { - action(item); - } - } - private void Clear() { richTextBox1.InvokeIfRequired( @@ -112,7 +89,7 @@ private void WriteLine(string text, params object?[] args) richTextBox1.InvokeIfRequired( x => { - var matches = Regex.Matches(text, "\\{([0-9]+)(?:\\:([A-Za-z]+))?\\}"); + MatchCollection? matches = Regex.Matches(text, "\\{([0-9]+)(?:\\:([A-Za-z]+))?\\}"); if (matches.Count <= 0) { @@ -175,6 +152,29 @@ private void MainForm_FormClosing(object sender, FormClosingEventArgs e) _projectFile = null; } + private static bool IsNumber(object? value) + { + return value is sbyte + || value is byte + || value is short + || value is ushort + || value is int + || value is uint + || value is long + || value is ulong + || value is float + || value is double + || value is decimal; + } + + private static void ForAll(Action action, params T[] items) + { + foreach (T item in items) + { + action(item); + } + } + private class NodeSorter : IComparer { // Compare the length of the strings, or the strings