feat: basic MonoGame integration implementations added

This commit is contained in:
Syntriax 2025-06-01 15:02:25 +03:00
parent fe8bde855d
commit 2caa042317
14 changed files with 607 additions and 0 deletions

View File

@ -0,0 +1,10 @@
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public interface IDrawableSprite : IBehaviour
{
void Draw(SpriteBatch spriteBatch);
}

View File

@ -0,0 +1,8 @@
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public interface IDrawableTriangle : IBehaviour
{
void Draw(ITriangleBatch triangleBatch);
}

View File

@ -0,0 +1,12 @@
using Microsoft.Xna.Framework;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public interface ITriangleBatch
{
void Begin(Matrix? view = null, Matrix? projection = null);
void Draw(Triangle triangle, ColorRGBA colorRGBA);
void End();
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class DrawableShapeBehaviour : Behaviour2D, IDrawableTriangle, IPreDraw
{
private readonly Shape2D shape = new([]);
private readonly List<Triangle> worldTriangles = [];
private readonly Shape2D worldShape = new([]);
protected ColorRGB color = new(255, 255, 255);
public void PreDraw() => UpdateWorldShape();
public void Draw(ITriangleBatch triangleBatch)
{
worldShape.ToTrianglesConvex(worldTriangles);
foreach (Triangle triangle in worldTriangles)
triangleBatch.Draw(new(triangle.C, triangle.B, triangle.A), color);
}
protected void UpdateWorldShape() => shape.Transform(Transform, worldShape);
public DrawableShapeBehaviour() => shape = Shape2D.Triangle;
public DrawableShapeBehaviour(Shape2D shape) => this.shape = shape;
public DrawableShapeBehaviour(Shape2D shape, ColorRGB color) { this.shape = shape; this.color = color; }
}

View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Input;
using Syntriax.Engine.Core;
using Syntriax.Engine.Systems.Input;
namespace Syntriax.Engine.Integration.MonoGame;
public class KeyboardInputsBehaviour : Behaviour, IButtonInputs<Keys>, IUpdate
{
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonPressed { get; } = new();
public Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments> OnAnyButtonReleased { get; } = new();
private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnPressed = new(256);
private readonly Dictionary<Keys, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>> OnReleased = new(256);
private int cachePressedCurrentlyCount = 0;
private readonly Keys[] cachePressedCurrently = new Keys[256];
private int cachePressedPreviouslyCount = 0;
private readonly Keys[] cachePressedPreviously = new Keys[256];
public void RegisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (!OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
{
delegateCallback = new();
OnPressed.Add(key, delegateCallback);
}
delegateCallback.AddListener(callback);
}
public void UnregisterOnPress(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (OnPressed.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
delegateCallback.RemoveListener(callback);
}
public void RegisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (!OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
{
delegateCallback = new();
OnReleased.Add(key, delegateCallback);
}
delegateCallback.AddListener(callback);
}
public void UnregisterOnRelease(Keys key, Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>.EventHandler callback)
{
if (OnReleased.TryGetValue(key, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? delegateCallback))
delegateCallback.RemoveListener(callback);
}
public void Update()
{
KeyboardState keyboardState = Keyboard.GetState();
keyboardState.GetPressedKeys(cachePressedCurrently);
cachePressedCurrentlyCount = keyboardState.GetPressedKeyCount();
for (int i = 0; i < cachePressedCurrentlyCount; i++)
{
Keys currentlyPressedKey = cachePressedCurrently[i];
if (WasPressed(currentlyPressedKey))
continue;
if (OnPressed.TryGetValue(currentlyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
callback?.Invoke(this, new(currentlyPressedKey));
OnAnyButtonPressed?.Invoke(this, new(currentlyPressedKey));
}
for (int i = 0; i < cachePressedPreviouslyCount; i++)
{
Keys previouslyPressedKey = cachePressedPreviously[i];
if (IsPressed(previouslyPressedKey))
continue;
if (OnReleased.TryGetValue(previouslyPressedKey, out Event<IButtonInputs<Keys>, IButtonInputs<Keys>.ButtonCallbackArguments>? callback))
callback?.Invoke(this, new(previouslyPressedKey));
OnAnyButtonReleased?.Invoke(this, new(previouslyPressedKey));
}
Array.Copy(cachePressedCurrently, cachePressedPreviously, cachePressedCurrentlyCount);
cachePressedPreviouslyCount = cachePressedCurrentlyCount;
}
public bool IsPressed(Keys key)
{
for (int i = 0; i < cachePressedCurrentlyCount; i++)
if (cachePressedCurrently[i] == key)
return true;
return false;
}
private bool WasPressed(Keys key)
{
for (int i = 0; i < cachePressedPreviouslyCount; i++)
if (cachePressedPreviously[i] == key)
return true;
return false;
}
}

View File

@ -0,0 +1,109 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class MonoGameCamera2DBehaviour : BehaviourBase, ICamera2D, IFirstFrameUpdate, IPreDraw
{
public event MatrixTransformChangedArguments? OnMatrixTransformChanged = null;
public event ViewportChangedArguments? OnViewportChanged = null;
public event ZoomChangedArguments? OnZoomChanged = null;
private Matrix _matrixTransform = Matrix.Identity;
private Viewport _viewport = default;
private float _zoom = 1f;
public GraphicsDeviceManager Graphics { get; private set; } = null!;
public ITransform2D Transform { get; private set; } = null!;
public Matrix MatrixTransform
{
get => _matrixTransform;
set
{
if (_matrixTransform == value)
return;
_matrixTransform = value;
OnMatrixTransformChanged?.Invoke(this);
}
}
public Vector2D Position
{
get => Transform.Position;
set => Transform.Position = value;
}
public Viewport Viewport
{
get => _viewport;
set
{
if (_viewport.Equals(value))
return;
_viewport = value;
OnViewportChanged?.Invoke(this);
}
}
public float Zoom
{
get => _zoom;
set
{
float newValue = Syntriax.Engine.Core.Math.Max(0.1f, value);
if (_zoom == newValue)
return;
_zoom = newValue;
OnZoomChanged?.Invoke(this);
}
}
public float Rotation
{
get => Transform.Rotation;
set => Transform.Rotation = value;
}
// TODO This causes delay since OnPreDraw calls assuming this is called in in Update
public Vector2D ScreenToWorldPosition(Vector2D screenPosition)
{
Vector2D worldPosition = Vector2.Transform(screenPosition.ToVector2(), Matrix.Invert(MatrixTransform)).ToVector2D();
return worldPosition.Scale(EngineConverterExtensions.screenScale);
}
public Vector2D WorldToScreenPosition(Vector2D worldPosition)
{
Vector2D screenPosition = Vector2.Transform(worldPosition.ToVector2(), MatrixTransform).ToVector2D();
return screenPosition.Scale(EngineConverterExtensions.screenScale);
}
public void FirstActiveFrame()
{
Graphics = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>().Window.Graphics;
Viewport = Graphics.GraphicsDevice.Viewport;
}
public void PreDraw()
{
MatrixTransform =
Matrix.CreateTranslation(new Vector3(-Position.X, Position.Y, 0f)) *
Matrix.CreateRotationZ(Rotation * Syntriax.Engine.Core.Math.DegreeToRadian) *
Matrix.CreateScale(Transform.Scale.X.Max(Transform.Scale.Y)) *
Matrix.CreateScale(Zoom) *
Matrix.CreateTranslation(new Vector3(_viewport.Width * .5f, _viewport.Height * .5f, 0f));
}
protected sealed override void InitializeInternal() => Transform = BehaviourController.GetRequiredBehaviour<ITransform2D>();
protected sealed override void FinalizeInternal() => Transform = null!;
public delegate void MatrixTransformChangedArguments(MonoGameCamera2DBehaviour sender);
public delegate void ViewportChangedArguments(MonoGameCamera2DBehaviour sender);
public delegate void ZoomChangedArguments(MonoGameCamera2DBehaviour sender);
}

View File

@ -0,0 +1,31 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class SpriteBatcher : BehaviourBase, IFirstFrameUpdate, IDraw
{
private static Comparer<IBehaviour> SortByPriority() => Comparer<IBehaviour>.Create((x, y) => y.Priority.CompareTo(x.Priority));
private SpriteBatch spriteBatch = null!;
private MonoGameCamera2DBehaviour camera2D = null!;
private readonly ActiveBehaviourCollectorSorted<IDrawableSprite> drawableSprites = new() { SortBy = SortByPriority() };
public void FirstActiveFrame()
{
MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameCamera2DBehaviour>();
spriteBatch = new(windowContainer.Window.GraphicsDevice);
}
public void Draw()
{
spriteBatch.Begin(transformMatrix: camera2D.MatrixTransform);
for (int i = 0; i < drawableSprites.Count; i++)
drawableSprites[i].Draw(spriteBatch);
spriteBatch.End();
}
}

View File

@ -0,0 +1,38 @@
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class TriangleBatcher : BehaviourBase, ITriangleBatch, IFirstFrameUpdate, IDraw
{
private static Comparer<IBehaviour> SortByAscendingPriority() => Comparer<IBehaviour>.Create((x, y) => x.Priority.CompareTo(y.Priority));
private TriangleBatch triangleBatch = null!;
private MonoGameCamera2DBehaviour camera2D = null!;
private readonly ActiveBehaviourCollectorSorted<IDrawableTriangle> drawableShapes = new() { SortBy = SortByAscendingPriority() };
public void FirstActiveFrame()
{
MonoGameWindowContainer windowContainer = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameWindowContainer>();
camera2D = BehaviourController.UniverseObject.Universe.FindRequiredBehaviour<MonoGameCamera2DBehaviour>();
triangleBatch = new(windowContainer.Window.GraphicsDevice);
drawableShapes.Unassign();
drawableShapes.Assign(BehaviourController.UniverseObject.Universe);
}
public void Draw()
{
triangleBatch.Begin(camera2D.MatrixTransform);
for (int i = 0; i < drawableShapes.Count; i++)
drawableShapes[i].Draw(triangleBatch);
triangleBatch.End();
}
public void Begin(Matrix? view = null, Matrix? projection = null) => triangleBatch.Begin(view, projection);
public void Draw(Triangle triangle, ColorRGBA colorRGBA) => triangleBatch.Draw(triangle, colorRGBA);
public void End() => triangleBatch.End();
}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Syntriax.Engine.Integration.MonoGame</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.2.1105">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Engine\Engine.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,36 @@
using System.Runtime.CompilerServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public static class EngineConverterExtensions
{
public readonly static Vector2D screenScale = Vector2D.Down + Vector2D.Right;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UniverseTime ToEngineTime(this GameTime gameTime) => new(gameTime.TotalGameTime, gameTime.ElapsedGameTime);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2D ToVector2D(this Vector2 vector) => new(vector.X, vector.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Color ToColor(this ColorRGBA rgba) => new(rgba.R, rgba.G, rgba.B, rgba.A);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2D ToVector2D(this Point point) => new(point.X, point.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 ToVector2(this Vector2D vector) => new(vector.X, vector.Y);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 ToDisplayVector2(this Vector2D vector) => vector.Scale(screenScale).ToVector2();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2D ApplyDisplayScale(this Vector2D vector) => vector.Scale(screenScale);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle ToDisplayRectangle(this Rectangle rectangle, DisplayMode displayMode) => new()
{
X = rectangle.X,
Y = displayMode.Height - rectangle.Y - rectangle.Height,
Width = rectangle.Width,
Height = rectangle.Height
};
}

View File

@ -0,0 +1,55 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public abstract class MonoGameWindow : Game
{
public GraphicsDeviceManager Graphics { get; protected set; } = null!;
public IUniverse Universe { get; protected set; } = null!;
public ColorRGB BackgroundColor { get; set; } = new ColorRGB(35, 20, 35);
public MonoGameWindow()
{
Preserver.Preserve();
Graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
Universe = new Universe();
}
protected abstract void PopulateUniverse(IUniverse universe);
protected override void Initialize()
{
Universe.Initialize();
Universe.InstantiateUniverseObject().SetUniverseObject("Window Container")
.BehaviourController.AddBehaviour<MonoGameWindowContainer>(this);
PopulateUniverse(Universe);
base.Initialize();
}
protected override void LoadContent()
{
// TODO: use this.Content to load your game content here
}
protected override void Update(GameTime gameTime)
{
Universe.Update(gameTime.ToEngineTime());
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(((ColorRGBA)BackgroundColor).ToColor());
Universe.Draw();
base.Draw(gameTime);
}
}

View File

@ -0,0 +1,74 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Syntriax.Engine.Core;
namespace Syntriax.Engine.Integration.MonoGame;
public class TriangleBatch : ITriangleBatch
{
private readonly GraphicsDevice graphicsDevice;
private VertexBuffer vertexBuffer = default!;
private readonly VertexPositionColor[] vertices = new VertexPositionColor[1024];
private int verticesIndex = 0;
private Matrix _view;
private Matrix _projection;
private readonly BasicEffect basicEffect;
public TriangleBatch(GraphicsDevice graphicsDevice)
{
this.graphicsDevice = graphicsDevice;
this.graphicsDevice.RasterizerState = new RasterizerState() { CullMode = CullMode.None };
basicEffect = new(graphicsDevice);
basicEffect.VertexColorEnabled = true;
}
public void Draw(Triangle triangle, ColorRGBA colorRGBA)
{
if (verticesIndex + 3 >= vertices.Length)
Flush();
Vector2 A = triangle.A.ToDisplayVector2();
Vector2 B = triangle.B.ToDisplayVector2();
Vector2 C = triangle.C.ToDisplayVector2();
Color color = colorRGBA.ToColor();
vertices[verticesIndex++] = new(new(A.X, A.Y, 0f), color);
vertices[verticesIndex++] = new(new(B.X, B.Y, 0f), color);
vertices[verticesIndex++] = new(new(C.X, C.Y, 0f), color);
}
public void Begin(Matrix? view = null, Matrix? projection = null)
{
if (view != null)
_view = view.Value;
else
_view = Matrix.Identity;
if (projection != null)
_projection = projection.Value;
else
{
Viewport viewport = graphicsDevice.Viewport;
_projection = Matrix.CreateOrthographicOffCenter(viewport.X, viewport.Width, viewport.Height, viewport.Y, 0, 1);
}
}
public void End() => Flush();
private void Flush()
{
basicEffect.Projection = _projection;
basicEffect.View = _view;
vertexBuffer = new VertexBuffer(graphicsDevice, typeof(VertexPositionColor), 1024, BufferUsage.WriteOnly);
vertexBuffer.SetData(vertices);
graphicsDevice.SetVertexBuffer(vertexBuffer);
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
pass.Apply();
graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, verticesIndex / 3);
verticesIndex = 0;
}
}

View File

@ -0,0 +1,10 @@
using Syntriax.Engine.Core;
using Syntriax.Engine.Core.Serialization;
namespace Syntriax.Engine.Integration.MonoGame;
[IgnoreSerialization]
public class MonoGameWindowContainer(MonoGameWindow GameWindow) : BehaviourBase
{
public MonoGameWindow Window { get; } = GameWindow;
}

View File

@ -17,36 +17,104 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Serializers.Yaml", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YamlDotNet", "Engine.Serializers\YamlDotNet\YamlDotNet\YamlDotNet.csproj", "{3D852C92-BC14-4893-AEF2-50612DAFCD8F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Integration", "Integration", "{823D4020-332D-2C13-F261-6F510F11A57E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonoGame", "Engine.Integration\Engine.Integration.MonoGame\Engine.Integration.MonoGame.csproj", "{C3438D33-0879-44E4-9DF0-D29F5621C44C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|x64.ActiveCfg = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|x64.Build.0 = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|x86.ActiveCfg = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Debug|x86.Build.0 = Debug|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|Any CPU.Build.0 = Release|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|x64.ActiveCfg = Release|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|x64.Build.0 = Release|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|x86.ActiveCfg = Release|Any CPU
{71719EAD-1B6B-4229-B39E-E53A2E8BFAB1}.Release|x86.Build.0 = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|x64.ActiveCfg = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|x64.Build.0 = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|x86.ActiveCfg = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|x86.Build.0 = Debug|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.Build.0 = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|x64.ActiveCfg = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|x64.Build.0 = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|x86.ActiveCfg = Release|Any CPU
{3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|x86.Build.0 = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|x64.ActiveCfg = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|x64.Build.0 = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|x86.ActiveCfg = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Debug|x86.Build.0 = Debug|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|Any CPU.Build.0 = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|x64.ActiveCfg = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|x64.Build.0 = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|x86.ActiveCfg = Release|Any CPU
{8452323D-99EF-43B1-8E7B-123E02279674}.Release|x86.Build.0 = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|x64.ActiveCfg = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|x64.Build.0 = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|x86.ActiveCfg = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Debug|x86.Build.0 = Debug|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|Any CPU.Build.0 = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|x64.ActiveCfg = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|x64.Build.0 = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|x86.ActiveCfg = Release|Any CPU
{58AE79C1-9203-44AE-8022-AA180F0A71DC}.Release|x86.Build.0 = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|x64.ActiveCfg = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|x64.Build.0 = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|x86.ActiveCfg = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Debug|x86.Build.0 = Debug|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|Any CPU.Build.0 = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|x64.ActiveCfg = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|x64.Build.0 = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|x86.ActiveCfg = Release|Any CPU
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176}.Release|x86.Build.0 = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|x64.ActiveCfg = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|x64.Build.0 = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|x86.ActiveCfg = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Debug|x86.Build.0 = Debug|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|Any CPU.Build.0 = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|x64.ActiveCfg = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|x64.Build.0 = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|x86.ActiveCfg = Release|Any CPU
{3D852C92-BC14-4893-AEF2-50612DAFCD8F}.Release|x86.Build.0 = Release|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Debug|x64.ActiveCfg = Debug|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Debug|x64.Build.0 = Debug|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Debug|x86.ActiveCfg = Debug|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Debug|x86.Build.0 = Debug|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|Any CPU.Build.0 = Release|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x64.ActiveCfg = Release|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x64.Build.0 = Release|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.ActiveCfg = Release|Any CPU
{C3438D33-0879-44E4-9DF0-D29F5621C44C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -54,5 +122,6 @@ Global
GlobalSection(NestedProjects) = preSolution
{E9D1CDC3-5BFF-4C87-AFEB-6CE372709176} = {F88E129A-9A47-4D27-96EE-6EC02F79594B}
{3D852C92-BC14-4893-AEF2-50612DAFCD8F} = {F88E129A-9A47-4D27-96EE-6EC02F79594B}
{C3438D33-0879-44E4-9DF0-D29F5621C44C} = {823D4020-332D-2C13-F261-6F510F11A57E}
EndGlobalSection
EndGlobal