From 8d692a7558949a537089f229a9f3b534c9d79ee9 Mon Sep 17 00:00:00 2001 From: Martenfur Date: Tue, 11 Oct 2022 01:03:40 +0200 Subject: [PATCH 01/22] Added numerics vector extensions. --- .../NumericsVectorExtensions.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Monofoxe/Monofoxe.Engine/NumericsVectorExtensions.cs diff --git a/Monofoxe/Monofoxe.Engine/NumericsVectorExtensions.cs b/Monofoxe/Monofoxe.Engine/NumericsVectorExtensions.cs new file mode 100644 index 0000000..80d7e37 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/NumericsVectorExtensions.cs @@ -0,0 +1,49 @@ +using Microsoft.Xna.Framework; + +namespace Monofoxe.Engine +{ + public static class NumericsVectorExtensions + { + public static System.Numerics.Vector2 ToNumericsVector(this Vector2 v) => + new System.Numerics.Vector2(v.X, v.Y); + + public static Vector2 ToVector2(this System.Numerics.Vector2 v) => + new Vector2(v.X, v.Y); + + public static System.Numerics.Vector2 ToNumericsVector2(this Vector3 v) => + new System.Numerics.Vector2(v.X, v.Y); + + public static System.Numerics.Vector3 ToNumericsVector(this Vector3 v) => + new System.Numerics.Vector3(v.X, v.Y, v.Z); + + public static System.Numerics.Vector4 ToNumericsVector(this Vector4 v) => + new System.Numerics.Vector4(v.X, v.Y, v.Z, v.W); + + public static Vector3 ToVector3(this System.Numerics.Vector3 v) => + new Vector3(v.X, v.Y, v.Z); + + public static Vector3 ToVector3(this System.Numerics.Vector2 v, float z = 0f) => + new Vector3(v.X, v.Y, z); + + public static Vector4 ToVector4(this System.Numerics.Vector4 v) => + new Vector4(v.X, v.Y, v.Z, v.W); + + public static System.Numerics.Vector4 ToNumericsVector4(this Color c) => + new System.Numerics.Vector4(c.R / 255f, c.G / 255f, c.B / 255f, c.A / 255f); + + public static System.Numerics.Vector3 ToNumericsVector3(this Color c) => + new System.Numerics.Vector3(c.R / 255f, c.G / 255f, c.B / 255f); + + public static Color ToColor(this System.Numerics.Vector4 v) => + new Color(v.X, v.Y, v.Z, v.W); + + public static Color ToColor(this System.Numerics.Vector3 v) => + new Color(v.X, v.Y, v.Z, 1); + + public static RectangleF ToRectangleF(this System.Numerics.Vector4 v) => + new RectangleF(v.X, v.Y, v.Z, v.W); + + public static System.Numerics.Vector4 ToNumericsVector(this RectangleF r) => + new System.Numerics.Vector4(r.X, r.Y, r.Width, r.Height); + } +} From 7467f06c7b46c9dfc2d548ef5225fa6d484038ba Mon Sep 17 00:00:00 2001 From: Martenfur Date: Sun, 6 Nov 2022 22:14:03 +0100 Subject: [PATCH 02/22] Added Clear() to ResourceBox. --- CHANGELOG.md | 3 +++ Monofoxe/Monofoxe.Engine/Resources/ResourceBox.cs | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a05197d..31ab4d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Added + +- Added `Clear()` method to `ResourceBox`. ## [v2.2.0] - *17.09.2022* diff --git a/Monofoxe/Monofoxe.Engine/Resources/ResourceBox.cs b/Monofoxe/Monofoxe.Engine/Resources/ResourceBox.cs index 23141bc..fa5168c 100644 --- a/Monofoxe/Monofoxe.Engine/Resources/ResourceBox.cs +++ b/Monofoxe/Monofoxe.Engine/Resources/ResourceBox.cs @@ -67,8 +67,13 @@ public void AddResource(string key, T resource) => public void RemoveResource(string key) => _resources.Remove(key); - - + + /// + /// Removes all resources from the box. + /// + public void Clear() => + _resources.Clear(); + IEnumerator> IEnumerable>.GetEnumerator() { return ((IEnumerable>)_resources).GetEnumerator(); From e2e0f6f4756d900d4706a5e12e8c45c6452906d5 Mon Sep 17 00:00:00 2001 From: Martenfur Date: Sun, 27 Nov 2022 23:45:11 +0100 Subject: [PATCH 03/22] Fixed `AddComponent<>()` not taking generic type into account. --- CHANGELOG.md | 5 +++++ Monofoxe/Monofoxe.Engine/EC/Entity.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ab4d8..e685880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ - Added `Clear()` method to `ResourceBox`. +### Fixed + +- Fixed `AddComponent<>()` not taking generic type into account. + + ## [v2.2.0] - *17.09.2022* ### Added diff --git a/Monofoxe/Monofoxe.Engine/EC/Entity.cs b/Monofoxe/Monofoxe.Engine/EC/Entity.cs index 3094e0c..b106903 100644 --- a/Monofoxe/Monofoxe.Engine/EC/Entity.cs +++ b/Monofoxe/Monofoxe.Engine/EC/Entity.cs @@ -174,7 +174,7 @@ public T AddComponent(T component) where T : Component { throw new Exception("Component " + component + "already has an owner!"); } - _componentDictionary.Add(component.GetType(), component); + _componentDictionary.Add(typeof(T), component); _componentList.Add(component); component.Owner = this; component.Initialize(); From f0f0b247f8c6ebb920540caa9d999efe294d3a78 Mon Sep 17 00:00:00 2001 From: Martenfur Date: Fri, 16 Dec 2022 02:38:36 +0100 Subject: [PATCH 04/22] Made ResourceInfoMgr accept wildcards instead of raw paths. --- CHANGELOG.md | 5 +- Monofoxe/Monofoxe.Engine/ResourceInfoMgr.cs | 54 +++++++++---------- .../Resources/DirectoryResourceBox.cs | 9 +++- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e685880..6d724d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,13 @@ - Added `Clear()` method to `ResourceBox`. +### Changed +- **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` shoulw now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` + ### Fixed - Fixed `AddComponent<>()` not taking generic type into account. - +- `DirectoryResourceBox` now ignores non-xnb files properly. ## [v2.2.0] - *17.09.2022* diff --git a/Monofoxe/Monofoxe.Engine/ResourceInfoMgr.cs b/Monofoxe/Monofoxe.Engine/ResourceInfoMgr.cs index cf99e35..67493e7 100644 --- a/Monofoxe/Monofoxe.Engine/ResourceInfoMgr.cs +++ b/Monofoxe/Monofoxe.Engine/ResourceInfoMgr.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; namespace Monofoxe.Engine { @@ -27,48 +29,42 @@ internal static void Init() { _assetPaths = GameMgr.Game.Content.Load("Content"); } - catch(Exception) { } + catch (Exception) { } } - - - + + /// - /// Returns list of resource paths matching input path. - /// Empty string will return all asset paths. + /// Returns list of resource paths matching the given wildcard pattern. * and ? are supported. /// - public static string[] GetResourcePaths(string path = "") + public static string[] GetResourcePaths(string pattern) { if (_assetPaths == null) - { + { return null; } - path = path.Replace('\\', '/'); - if (path != "") - { - var list = new List(); - - if (!path.EndsWith("/")) - { - path += '/'; - } - foreach(var info in _assetPaths) - { - if (info.StartsWith(path)) - { - list.Add(info); - } - } - - return list.ToArray(); - } - else + if (pattern == "") { var assetPathsCopy = new string[_assetPaths.Length]; _assetPaths.CopyTo(assetPathsCopy, 0); return assetPathsCopy; } + + var pattenRegex = WildCardToRegular(pattern.Replace("\\", "/")); + + var list = new List(); + foreach (var info in _assetPaths) + { + if (Regex.IsMatch(info, pattenRegex)) + { + list.Add(info); + } + } + + return list.ToArray(); } - + + private static string WildCardToRegular(string value) => + "^" + Regex.Escape(value).Replace("\\?", ".").Replace("\\*", ".*") + "$"; } } diff --git a/Monofoxe/Monofoxe.Engine/Resources/DirectoryResourceBox.cs b/Monofoxe/Monofoxe.Engine/Resources/DirectoryResourceBox.cs index 02fb08b..3fb2573 100644 --- a/Monofoxe/Monofoxe.Engine/Resources/DirectoryResourceBox.cs +++ b/Monofoxe/Monofoxe.Engine/Resources/DirectoryResourceBox.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics; using System.IO; +using System.Text.RegularExpressions; namespace Monofoxe.Resources { @@ -35,17 +36,21 @@ public override void Load() _content.RootDirectory = ResourceInfoMgr.ContentDir; // TODO: Add Recursive flag to GetResourcePaths. - var paths = ResourceInfoMgr.GetResourcePaths(_resourceDir); + var paths = ResourceInfoMgr.GetResourcePaths(_resourceDir + "/*"); foreach (var path in paths) { try { + if (Path.HasExtension(path)) + { + continue; + } AddResource(Path.GetFileNameWithoutExtension(path), _content.Load(path)); } catch (InvalidCastException) { - Debug.WriteLine("Failed to load " + path + ". It has different type."); + Debug.WriteLine("Failed to load " + path + ". It has a different type."); } } } From 3a3c4ba93d978d68537564eaabf07bf2f77edb96 Mon Sep 17 00:00:00 2001 From: Martenfur Date: Tue, 31 Jan 2023 23:27:23 +0100 Subject: [PATCH 05/22] Added AddComponent method with Type. --- Monofoxe/Monofoxe.Engine/EC/Entity.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Monofoxe/Monofoxe.Engine/EC/Entity.cs b/Monofoxe/Monofoxe.Engine/EC/Entity.cs index b106903..89a9efe 100644 --- a/Monofoxe/Monofoxe.Engine/EC/Entity.cs +++ b/Monofoxe/Monofoxe.Engine/EC/Entity.cs @@ -168,22 +168,29 @@ public virtual void Destroy() /// /// Adds component to the entity. /// - public T AddComponent(T component) where T : Component + public Component AddComponent(Type type, Component component) { if (component.Owner != null) { throw new Exception("Component " + component + "already has an owner!"); } - _componentDictionary.Add(typeof(T), component); + _componentDictionary.Add(type, component); _componentList.Add(component); component.Owner = this; component.Initialize(); component.Initialized = true; - return component; // Doing a passthrough for nicer syntax. + return component; // Doing a passthrough for nicer syntax. } + /// + /// Adds component to the entity. + /// + public T AddComponent(T component) where T : Component => + (T)AddComponent(typeof(T), component); + + /// /// Returns component of given class. /// From 0e7c1ad8fdef66acab26e98b9112ad0a754a44af Mon Sep 17 00:00:00 2001 From: Martenfur Date: Sat, 11 Feb 2023 00:38:32 +0100 Subject: [PATCH 06/22] Added Offset method to linear dampers. --- CHANGELOG.md | 1 + Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs | 10 ++++++++++ Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs | 10 ++++++++++ Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs | 10 ++++++++++ 4 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d724d6..c7ed487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Added `Clear()` method to `ResourceBox`. +- Added `Offset()` method to linear dampers. ### Changed - **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` shoulw now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` diff --git a/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs b/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs index 00d1df6..4bd06a6 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs @@ -161,6 +161,16 @@ public float Update(float value, float speed) } + /// + /// Offsets the internal values of the damper while keeping its motion. + /// + public void Offset(float offset) + { + _y += offset; + _oldValue += offset; + } + + private void ComputeConstants() { _k1 = _dampingCoefficient / (MathHelper.Pi * Frequency); diff --git a/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs b/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs index 31268f9..db4edc4 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs @@ -161,6 +161,16 @@ public Vector2 Update(Vector2 value, Vector2 speed) } + /// + /// Offsets the internal values of the damper while keeping its motion. + /// + public void Offset(Vector2 offset) + { + _y += offset; + _oldValue += offset; + } + + private void ComputeConstants() { _k1 = _dampingCoefficient / (MathHelper.Pi * Frequency); diff --git a/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs b/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs index 6e848c8..18c1db4 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs @@ -161,6 +161,16 @@ public Vector3 Update(Vector3 value, Vector3 speed) } + /// + /// Offsets the internal values of the damper while keeping its motion. + /// + public void Offset(Vector3 offset) + { + _y += offset; + _oldValue += offset; + } + + private void ComputeConstants() { _k1 = _dampingCoefficient / (MathHelper.Pi * Frequency); From fdd81745fce98b11ec35f51efa8fc5c32ed34af7 Mon Sep 17 00:00:00 2001 From: Martenfur Date: Tue, 18 Jul 2023 10:06:30 +0200 Subject: [PATCH 07/22] Added RenderMask and removed camera filters. --- CHANGELOG.md | 4 ++ Monofoxe/Monofoxe.Engine/Cameras/Camera.cs | 72 +------------------ .../Monofoxe.Engine/Cameras/FilterMode.cs | 23 ------ .../Monofoxe.Engine/Cameras/RenderMask.cs | 43 +++++++++++ Monofoxe/Monofoxe.Engine/EC/Entity.cs | 8 +++ Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs | 13 +++- Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs | 11 ++- .../Monofoxe.Engine/SceneSystem/SceneMgr.cs | 5 +- 8 files changed, 81 insertions(+), 98 deletions(-) delete mode 100644 Monofoxe/Monofoxe.Engine/Cameras/FilterMode.cs create mode 100644 Monofoxe/Monofoxe.Engine/Cameras/RenderMask.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index c7ed487..7556108 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,12 @@ ## [Unreleased] +### Breaking +- Removed camera layer filters. + ### Added +- Added `RenderMask` as a replacement to camera filters to `Scene`, `Layer` and `Entity`. - Added `Clear()` method to `ResourceBox`. - Added `Offset()` method to linear dampers. diff --git a/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs b/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs index 5d989c0..a845a30 100644 --- a/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs +++ b/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs @@ -122,15 +122,10 @@ public int Priority public Matrix View; public Matrix Projection; - - private Dictionary> _filter = - new Dictionary>(StringComparer.OrdinalIgnoreCase); - /// - /// Mode for filtering out certain layers. + /// For a layer or a scene to be rendered, it has to match at least one bit of RenderMask. /// - public FilterMode FilterMode = FilterMode.None; - + public RenderMask RenderMask = RenderMask.Default; /// /// Shaders applied to the surface. @@ -223,69 +218,6 @@ public void Dispose() /// Returns mouse position relative to the camera. /// public abstract Vector2 GetRelativeMousePosition(); - - - public void AddFilterEntry(string sceneName, string layerName) - { - if (_filter.TryGetValue(sceneName, out HashSet filterSet)) - { - filterSet.Add(layerName); - } - else - { - var newSet = new HashSet(StringComparer.OrdinalIgnoreCase); - newSet.Add(layerName); - _filter.Add(sceneName, newSet); - } - } - - public void RemoveFilterEntry(string sceneName, string layerName) - { - if (_filter.TryGetValue(sceneName, out HashSet filterSet)) - { - filterSet.Remove(layerName); - if (filterSet.Count == 0) - { - _filter.Remove(sceneName); - } - } - } - - /// - /// Returns true, if given layer is filtered out. - /// - public bool Filter(string sceneName, string layerName) - { - if (FilterMode == FilterMode.None) - { - return false; - } - - var result = false; - - // NOTE: Here was a strange TO-DO with just said "fix this". - // Haven't found any bugs here, so just removed it. Probably - // was an already fixed bug with case-sensitivity. In case of any - // camera filter-related bugs, this place if the first suspect. - // Thanks, past me. - - if (_filter.TryGetValue(sceneName, out HashSet filterSet)) - { - result = filterSet.Contains(layerName); - } - - if (FilterMode == FilterMode.Inclusive) - { - return !result; - } - else - { - // NOTE: Add additional check here, if any other filter types will be created. - return result; - } - - } - /// diff --git a/Monofoxe/Monofoxe.Engine/Cameras/FilterMode.cs b/Monofoxe/Monofoxe.Engine/Cameras/FilterMode.cs deleted file mode 100644 index 8395eb8..0000000 --- a/Monofoxe/Monofoxe.Engine/Cameras/FilterMode.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Monofoxe.Engine.Cameras -{ - /// - /// filtering modes for camera. - /// - public enum FilterMode - { - /// - /// Triggers rendering, if filter DOES contain layer. - /// - Inclusive, - - /// - /// Triggers rendering, if filter DOES NOT contain layer. - /// - Exclusive, - - /// - /// Renders all layers. - /// - None, - } -} diff --git a/Monofoxe/Monofoxe.Engine/Cameras/RenderMask.cs b/Monofoxe/Monofoxe.Engine/Cameras/RenderMask.cs new file mode 100644 index 0000000..bc1a871 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Cameras/RenderMask.cs @@ -0,0 +1,43 @@ +using System; + +namespace Monofoxe.Engine.Cameras +{ + [Flags] + public enum RenderMask + { + None = 0x00000000, + Default = 0x00000001, + Mask1 = 0x00000001, + Mask2 = 0x00000002, + Mask3 = 0x00000004, + Mask4 = 0x00000008, + Mask5 = 0x00000010, + Mask6 = 0x00000020, + Mask7 = 0x00000040, + Mask8 = 0x00000080, + Mask9 = 0x00000100, + Mask10 = 0x00000200, + Mask11 = 0x00000400, + Mask12 = 0x00000800, + Mask13 = 0x00001000, + Mask14 = 0x00002000, + Mask15 = 0x00004000, + Mask16 = 0x00008000, + Mask17 = 0x00010000, + Mask18 = 0x00020000, + Mask19 = 0x00040000, + Mask20 = 0x00080000, + Mask21 = 0x00100000, + Mask22 = 0x00200000, + Mask23 = 0x00400000, + Mask24 = 0x00800000, + Mask25 = 0x01000000, + Mask26 = 0x02000000, + Mask27 = 0x04000000, + Mask28 = 0x08000000, + Mask29 = 0x10000000, + Mask30 = 0x20000000, + Mask31 = 0x40000000, + All = int.MaxValue, + } +} diff --git a/Monofoxe/Monofoxe.Engine/EC/Entity.cs b/Monofoxe/Monofoxe.Engine/EC/Entity.cs index 89a9efe..26dcee7 100644 --- a/Monofoxe/Monofoxe.Engine/EC/Entity.cs +++ b/Monofoxe/Monofoxe.Engine/EC/Entity.cs @@ -4,6 +4,7 @@ using Monofoxe.Engine.Utils.Coroutines; using Monofoxe.Engine.SceneSystem; using Monofoxe.Engine.Utils.CustomCollections; +using Monofoxe.Engine.Cameras; namespace Monofoxe.Engine.EC { @@ -78,6 +79,13 @@ public Layer Layer private Dictionary _componentDictionary; private SafeList _componentList; + + /// + /// If camera's RenderMask does not have any bits in common with entity's RenderMask, + /// the entity will not be renderdd for that camera. + /// + public RenderMask RenderMask = RenderMask.Default; + public Entity(Layer layer) { diff --git a/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs b/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs index da8f0c1..081a552 100644 --- a/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs +++ b/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs @@ -89,6 +89,13 @@ public bool DepthSorting public bool IsGUI = false; + /// + /// If camera's RenderMask does not have any bits in common with layer's RenderMask, + /// the layer will not be renderdd for that camera. + /// + public RenderMask RenderMask = RenderMask.Default; + + /// /// List of all layer's entities. /// @@ -332,7 +339,11 @@ internal void Draw() OnPreDraw?.Invoke(this); foreach (var entity in _depthSortedEntities) { - if (entity.Visible && !entity.Destroyed) + if ( + entity.Visible + && !entity.Destroyed + && ((GraphicsMgr.CurrentCamera.RenderMask & entity.RenderMask) != 0) + ) { entity.Draw(); } diff --git a/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs b/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs index efb1cd8..c8c628c 100644 --- a/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs +++ b/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs @@ -50,6 +50,11 @@ public int Priority } private int _priority; + /// + /// If camera's RenderMask does not have any bits in common with scene's RenderMask, + /// the scene will not be renderdd for that camera. + /// + public RenderMask RenderMask = RenderMask.Default; /// /// Current active layer. @@ -361,9 +366,9 @@ internal void Draw() foreach (var layer in _layers) { if ( - layer.Visible && - !layer.IsGUI && - !GraphicsMgr.CurrentCamera.Filter(Name, layer.Name) + layer.Visible + && !layer.IsGUI + && ((GraphicsMgr.CurrentCamera.RenderMask & layer.RenderMask) != 0) ) { CurrentLayer = layer; diff --git a/Monofoxe/Monofoxe.Engine/SceneSystem/SceneMgr.cs b/Monofoxe/Monofoxe.Engine/SceneSystem/SceneMgr.cs index 50870d3..39acfd0 100644 --- a/Monofoxe/Monofoxe.Engine/SceneSystem/SceneMgr.cs +++ b/Monofoxe/Monofoxe.Engine/SceneSystem/SceneMgr.cs @@ -291,7 +291,10 @@ internal static void CallDrawEvents() OnPreDraw?.Invoke(); foreach (var scene in Scenes) { - if (scene.Visible) + if ( + scene.Visible + && ((GraphicsMgr.CurrentCamera.RenderMask & scene.RenderMask) != 0) + ) { CurrentScene = scene; From 3b3d237c91752fb656e69c1646577d21b9e4a28d Mon Sep 17 00:00:00 2001 From: Martenfur Date: Tue, 18 Jul 2023 10:23:27 +0200 Subject: [PATCH 08/22] Fixed typos. --- Monofoxe/Monofoxe.Engine/EC/Entity.cs | 2 +- Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs | 2 +- Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Monofoxe/Monofoxe.Engine/EC/Entity.cs b/Monofoxe/Monofoxe.Engine/EC/Entity.cs index 26dcee7..5cbe97e 100644 --- a/Monofoxe/Monofoxe.Engine/EC/Entity.cs +++ b/Monofoxe/Monofoxe.Engine/EC/Entity.cs @@ -82,7 +82,7 @@ public Layer Layer /// /// If camera's RenderMask does not have any bits in common with entity's RenderMask, - /// the entity will not be renderdd for that camera. + /// the entity will not be rendered for that camera. /// public RenderMask RenderMask = RenderMask.Default; diff --git a/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs b/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs index 081a552..c9b2df8 100644 --- a/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs +++ b/Monofoxe/Monofoxe.Engine/SceneSystem/Layer.cs @@ -91,7 +91,7 @@ public bool DepthSorting /// /// If camera's RenderMask does not have any bits in common with layer's RenderMask, - /// the layer will not be renderdd for that camera. + /// the layer will not be rendered for that camera. /// public RenderMask RenderMask = RenderMask.Default; diff --git a/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs b/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs index c8c628c..a60eaa7 100644 --- a/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs +++ b/Monofoxe/Monofoxe.Engine/SceneSystem/Scene.cs @@ -52,7 +52,7 @@ public int Priority /// /// If camera's RenderMask does not have any bits in common with scene's RenderMask, - /// the scene will not be renderdd for that camera. + /// the scene will not be rendered for that camera. /// public RenderMask RenderMask = RenderMask.Default; From 4ab409319d9a158854317b9595d1125afd172ef8 Mon Sep 17 00:00:00 2001 From: Martenfur Date: Tue, 18 Jul 2023 17:26:27 +0200 Subject: [PATCH 09/22] Added `OnFrameStart`, `OnFrameFinish` and `OnAfterDraw` events to `GraphicsMgr` --- CHANGELOG.md | 9 +++--- .../Monofoxe.Engine/Drawing/GraphicsMgr.cs | 30 ++++++++++++++++--- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7556108..b04fdbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,12 @@ ## [Unreleased] -### Breaking -- Removed camera layer filters. - ### Added - Added `RenderMask` as a replacement to camera filters to `Scene`, `Layer` and `Entity`. - Added `Clear()` method to `ResourceBox`. - Added `Offset()` method to linear dampers. +- Added `OnFrameStart`, `OnFrameFinish` and `OnAfterDraw` events to `GraphicsMgr`. ### Changed - **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` shoulw now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` @@ -17,7 +15,10 @@ ### Fixed - Fixed `AddComponent<>()` not taking generic type into account. -- `DirectoryResourceBox` now ignores non-xnb files properly. +- DirectoryResourceBox` now ignores non-xnb files properly. + +### Removed +- **BREAKING CHANGE:** Removed camera layer filters. ## [v2.2.0] - *17.09.2022* diff --git a/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs b/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs index e31b71a..c049e48 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs @@ -43,6 +43,21 @@ public static class GraphicsMgr /// public static Matrix CanvasMatrix = Matrix.CreateTranslation(Vector3.Zero); //We need zero matrix here, or else mouse position will derp out. + /// + /// Called at the start of the frame before any Draw events have been called. + /// + public static event Action OnFrameStart; + + /// + /// Called at the end of the frame after all Draw events have been called and before all DrawGui events are called. + /// + public static event Action OnAfterDraw; + + /// + /// Called at the end of the frame after all Draw/DrawGui events have been called. + /// + public static event Action OnFrameFinish; + /// /// Initialization function for draw manager. /// @@ -131,6 +146,8 @@ public static void Update(GameTime gameTime) #region Main draw events. + OnFrameStart?.Invoke(); + foreach(var camera in CameraMgr.Cameras) { if (camera.Enabled) @@ -154,6 +171,9 @@ public static void Update(GameTime gameTime) Surface.ResetTarget(); } } + + OnAfterDraw?.Invoke(); + #endregion Main draw events. @@ -208,25 +228,27 @@ public static void Update(GameTime gameTime) VertexBatch.FlushBatch(); // Drawing GUI stuff. + OnFrameFinish?.Invoke(); + VertexBatch.PopViewMatrix(); VertexBatch.PopProjectionMatrix(); // Safety checks. if (!Surface.SurfaceStackEmpty) { - throw new InvalidOperationException("Unbalanced surface stack! Did you forgot to reset a surface somewhere?"); + throw new InvalidOperationException("Unbalanced surface stack! Did you forget to reset a surface somewhere?"); } if (!VertexBatch.WorldStackEmpty) { - throw new InvalidOperationException("Unbalanced World matrix stack! Did you forgot to pop a matrix somewhere?"); + throw new InvalidOperationException("Unbalanced World matrix stack! Did you forget to pop a matrix somewhere?"); } if (!VertexBatch.ViewStackEmpty) { - throw new InvalidOperationException("Unbalanced View matrix stack! Did you forgot to pop a matrix somewhere?"); + throw new InvalidOperationException("Unbalanced View matrix stack! Did you forget to pop a matrix somewhere?"); } if (!VertexBatch.ProjectionStackEmpty) { - throw new InvalidOperationException("Unbalanced Projection matrix stack! Did you forgot to pop a matrix somewhere?"); + throw new InvalidOperationException("Unbalanced Projection matrix stack! Did you forget to pop a matrix somewhere?"); } // Safety checks. } From dc45a54ab808ff122389e96f0c09e30be8e3434d Mon Sep 17 00:00:00 2001 From: minkberry Date: Sat, 3 Feb 2024 01:29:01 +0100 Subject: [PATCH 10/22] Added basic GJK collisions. --- .../Collisions/Algorithms/GJK.cs | 294 ++++++++++++++++++ .../Algorithms/SimpleCollisionDetection.cs | 14 + .../Collisions/CollisionChecker.cs | 56 ++++ .../Monofoxe.Engine/Collisions/Settings.cs | 31 ++ .../Monofoxe.Engine/Collisions/ShapePool.cs | 32 ++ .../Collisions/Shapes/Circle.cs | 32 ++ .../Collisions/Shapes/IShape.cs | 11 + .../Collisions/Shapes/Polygon.cs | 121 +++++++ .../Collisions/Shapes/ShapeType.cs | 8 + .../Collisions/UnitConversionExtensions.cs | 55 ++++ Monofoxe/Monofoxe.Engine/Utils/AABB.cs | 109 +++++++ .../CustomCollections/AccumulationBuffer.cs | 37 +++ .../Utils/CustomCollections/IPoolable.cs | 10 + .../Utils/CustomCollections/Pool.cs | 42 +++ .../Utils/CustomCollections/UnorderedList.cs | 119 +++++++ .../Monofoxe.Samples/Demos/CollisionsDemo.cs | 115 +++++++ Samples/Monofoxe.Samples/GameController.cs | 2 +- Samples/Monofoxe.Samples/SceneSwitcher.cs | 1 + 18 files changed, 1088 insertions(+), 1 deletion(-) create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Algorithms/GJK.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Algorithms/SimpleCollisionDetection.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Settings.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/ShapePool.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs create mode 100644 Monofoxe/Monofoxe.Engine/Utils/AABB.cs create mode 100644 Monofoxe/Monofoxe.Engine/Utils/CustomCollections/AccumulationBuffer.cs create mode 100644 Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs create mode 100644 Monofoxe/Monofoxe.Engine/Utils/CustomCollections/Pool.cs create mode 100644 Monofoxe/Monofoxe.Engine/Utils/CustomCollections/UnorderedList.cs create mode 100644 Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/GJK.cs b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/GJK.cs new file mode 100644 index 0000000..9fa2307 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/GJK.cs @@ -0,0 +1,294 @@ +using Microsoft.Xna.Framework; +using System.Runtime.CompilerServices; + + +/// This code is a port of Kroitor's GJK implementation. +/// https://github.com/kroitor/gjk.c + +namespace Monofoxe.Engine.Collisions.Algorithms +{ + internal static class GJK + { + //----------------------------------------------------------------------------- + // Basic vector arithmetic operations + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector2 Negate(Vector2 v) { v.X = -v.X; v.Y = -v.Y; return v; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector2 Perpendicular(Vector2 v) { return new Vector2(v.Y, -v.X); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float DotProduct(Vector2 a, Vector2 b) { return a.X * b.X + a.Y * b.Y; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector2 SafeNormalize(Vector2 v) + { + if (v == Vector2.Zero) + { + return Vector2.Zero; + } + + v.Normalize(); + return v; + } + + + //----------------------------------------------------------------------------- + // Triple product expansion is used to calculate perpendicular normal vectors + // which kinda 'prefer' pointing towards the Origin in Minkowski space + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector2 TripleProduct(Vector2 a, Vector2 b, Vector2 c) + { + Vector2 r; + + float ac = a.X * c.X + a.Y * c.Y; // perform a.dot(c) + float bc = b.X * c.X + b.Y * c.Y; // perform b.dot(c) + + // perform b * a.dot(c) - a * b.dot(c) + r.X = b.X * ac - a.X * bc; + r.Y = b.Y * ac - a.Y * bc; + return r; + } + + + //----------------------------------------------------------------------------- + // This is to compute average center (roughly). It might be different from + // Center of Gravity, especially for bodies with nonuniform density, + // but this is ok as initial direction of simplex search in GJK. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector2 AveragePoint(Vector2[] vertices, int count) + { + Vector2 avg = new Vector2(); + for (int i = 0; i < count; i++) + { + avg.X += vertices[i].X; + avg.Y += vertices[i].Y; + } + avg.X /= count; + avg.Y /= count; + return avg; + } + + + //----------------------------------------------------------------------------- + // Get furthest vertex along a certain direction + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int IndexOfFurthestPoint(Vector2[] vertices, int count, Vector2 d) + { + + float maxProduct = DotProduct(d, vertices[0]); + int index = 0; + for (int i = 1; i < count; i++) + { + float product = DotProduct(d, vertices[i]); + if (product > maxProduct) + { + maxProduct = product; + index = i; + } + } + return index; + } + + + //----------------------------------------------------------------------------- + // Minkowski sum support function for GJK for two polygons. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector2 SupportPolyPoly(Vector2[] vertices1, int count1, Vector2[] vertices2, int count2, Vector2 d) + { + + // get furthest point of first body along an arbitrary direction + int i = IndexOfFurthestPoint(vertices1, count1, d); + + // get furthest point of second body along the opposite direction + int j = IndexOfFurthestPoint(vertices2, count2, Negate(d)); + + // subtract (Minkowski sum) the two points to see if bodies 'overlap' + return vertices1[i] - vertices2[j]; + } + + + //----------------------------------------------------------------------------- + // Minkowski sum support function for GJK for a circle and a polygon. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector2 SupportCirclePoly(Vector2 center, float r, Vector2[] vertices2, int count2, Vector2 d) + { + // get furthest point of second body along the opposite direction + int j = IndexOfFurthestPoint(vertices2, count2, Negate(d)); + + // subtract (Minkowski sum) the two points to see if bodies 'overlap' + return center + r * SafeNormalize(d) - vertices2[j]; + } + + + //----------------------------------------------------------------------------- + // The GJK yes/no test + public static bool CheckPolyPoly(Vector2[] vertices1, int count1, Vector2[] vertices2, int count2) + { + int index = 0; // index of current vertex of simplex + Vector2 a, b, c, d, ao, ab, ac, abperp, acperp; + Vector2[] simplex = new Vector2[3]; + + Vector2 position1 = AveragePoint(vertices1, count1); // not a CoG but + Vector2 position2 = AveragePoint(vertices2, count2); // it's ok for GJK ) + + // initial direction from the center of 1st body to the center of 2nd body + d = position1 - position2; + + // if initial direction is zero – set it to any arbitrary axis (we choose X) + if (d.X == 0 && d.Y == 0) + d.X = 1f; + + // set the first support as initial point of the new simplex + a = simplex[0] = SupportPolyPoly(vertices1, count1, vertices2, count2, d); + + if (DotProduct(a, d) <= 0) + return false; // no collision + + d = Negate(a); // The next search direction is always towards the origin, so the next search direction is negate(a) + + var iteration = 0; + + while (true) + { + if (iteration > 1000) + { + // A failsafe to ensure that we don't end up in an infinite loop. + break; + } + + a = simplex[++index] = SupportPolyPoly(vertices1, count1, vertices2, count2, d); + + if (DotProduct(a, d) <= 0) + return false; // no collision + + ao = Negate(a); // from point A to Origin is just negative A + + // simplex has 2 points (a line segment, not a triangle yet) + if (index < 2) + { + b = simplex[0]; + ab = b - a; // from point A to B + d = TripleProduct(ab, ao, ab); // normal to AB towards Origin + if (d.LengthSquared() == 0) + d = Perpendicular(ab); + continue; // skip to next iteration + } + + b = simplex[1]; + c = simplex[0]; + ab = b - a; // from point A to B + ac = c - a; // from point A to C + + acperp = TripleProduct(ab, ac, ac); + + if (DotProduct(acperp, ao) >= 0) + { + d = acperp; // new direction is normal to AC towards Origin + } + else + { + + abperp = TripleProduct(ac, ab, ab); + + if (DotProduct(abperp, ao) < 0) + return true; // collision + + simplex[0] = simplex[1]; // swap first element (point C) + + d = abperp; // new direction is normal to AB towards Origin + } + + simplex[1] = simplex[2]; // swap element in the middle (point B) + --index; + } + + return false; + } + + + //----------------------------------------------------------------------------- + // The GJK yes/no test + public static bool CheckCirclePoly(Vector2 center, float r, Vector2[] vertices2, int count2) + { + int index = 0; // index of current vertex of simplex + Vector2 a, b, c, d, ao, ab, ac, abperp, acperp; + Vector2[] simplex = new Vector2[3]; + + Vector2 position1 = center; // not a CoG but + Vector2 position2 = AveragePoint(vertices2, count2); // it's ok for GJK ) + + // initial direction from the center of 1st body to the center of 2nd body + d = position1 - position2; + + // if initial direction is zero – set it to any arbitrary axis (we choose X) + if (d.X == 0 && d.Y == 0) + d.X = 1f; + + // set the first support as initial point of the new simplex + a = simplex[0] = SupportCirclePoly(center, r, vertices2, count2, d); + + if (DotProduct(a, d) <= 0) + return false; // no collision + + d = Negate(a); // The next search direction is always towards the origin, so the next search direction is negate(a) + + var iteration = 0; + + while (true) + { + iteration += 1; + if (iteration > 1000) + { + // A failsafe to ensure that we don't end up in an infinite loop. + break; + } + + a = simplex[++index] = SupportCirclePoly(center, r, vertices2, count2, d); + + if (DotProduct(a, d) <= 0) + return false; // no collision + + ao = Negate(a); // from point A to Origin is just negative A + + // simplex has 2 points (a line segment, not a triangle yet) + if (index < 2) + { + b = simplex[0]; + ab = b - a; // from point A to B + d = TripleProduct(ab, ao, ab); // normal to AB towards Origin + if (d.LengthSquared() == 0) + d = Perpendicular(ab); + continue; // skip to next iteration + } + + b = simplex[1]; + c = simplex[0]; + ab = b - a; // from point A to B + ac = c - a; // from point A to C + + acperp = TripleProduct(ab, ac, ac); + + if (DotProduct(acperp, ao) >= 0) + { + d = acperp; // new direction is normal to AC towards Origin + } + else + { + + abperp = TripleProduct(ac, ab, ab); + + if (DotProduct(abperp, ao) < 0) + return true; // collision + + simplex[0] = simplex[1]; // swap first element (point C) + + d = abperp; // new direction is normal to AB towards Origin + } + + simplex[1] = simplex[2]; // swap element in the middle (point B) + --index; + } + + return false; + } + + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/SimpleCollisionDetection.cs b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/SimpleCollisionDetection.cs new file mode 100644 index 0000000..ada20fa --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/SimpleCollisionDetection.cs @@ -0,0 +1,14 @@ +using Microsoft.Xna.Framework; +using System.Runtime.CompilerServices; + +namespace Monofoxe.Engine.Collisions.Algorithms +{ + internal static class SimpleCollisionDetection + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckCircleCircle(Vector2 center1, float r1, Vector2 center2, float r2) + { + return (center1 - center2).LengthSquared() <= (r1 + r2) * (r1 + r2); + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs new file mode 100644 index 0000000..765a40b --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs @@ -0,0 +1,56 @@ +using Monofoxe.Engine.Collisions.Algorithms; +using Monofoxe.Engine.Collisions.Shapes; +using Monofoxe.Engine.Utils; + +namespace Monofoxe.Engine.Collisions +{ + public static class CollisionChecker + { + public static bool CheckCollision(IShape a, IShape b) + { + if (a.Type == ShapeType.Circle && b.Type == ShapeType.Circle) + { + // AABB check for this case is kinda redundant. + var circle1 = (Circle)a; + var circle2 = (Circle)b; + return SimpleCollisionDetection.CheckCircleCircle(circle1.Position, circle1.Radius, circle2.Position, circle2.Radius); + } + + var aabb1 = a.GetBoundingBox(); + var aabb2 = b.GetBoundingBox(); + if (!AABB.TestOverlap(ref aabb2, ref aabb1)) + { + return false; + } + + switch (a.Type) + { + case ShapeType.Circle: + switch (b.Type) + { + // No circle-circle case because it is handled in the beginning. + case ShapeType.Polygon: + var circle = (Circle)a; + var poly = (Polygon)b; + return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); + } + break; + case ShapeType.Polygon: + switch (b.Type) + { + case ShapeType.Circle: + var circle = (Circle)b; + var poly = (Polygon)a; + return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); + case ShapeType.Polygon: + var poly1 = (Polygon)a; + var poly2 = (Polygon)b; + return GJK.CheckPolyPoly(poly1.Vertices, poly1.Count, poly2.Vertices, poly2.Count); + } + break; + } + + return false; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Settings.cs b/Monofoxe/Monofoxe.Engine/Collisions/Settings.cs new file mode 100644 index 0000000..f6f45fb --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Settings.cs @@ -0,0 +1,31 @@ +namespace Monofoxe.Engine.Collisions +{ + public static class Settings + { + public const float MaxFloat = 3.402823466e+38f; + public const float Epsilon = 1.192092896e-07f; + + + /// + /// The maximum number of vertices on a convex polygon. + /// + public static int MaxPolygonVertices = 8; + + /// + /// It is recommended to downscale your colliders and not make them too large, that's why collider sizes are measured in meters and not pixels. + /// This value signifies how many pixels there are in one meter. + /// + public static float WorldScale + { + get => _worldScale; + set + { + _worldScale = value; + OneOverWorldScale = 1 / _worldScale; + } + } + private static float _worldScale = 64f; + + public static float OneOverWorldScale { get; private set; } = 1 / _worldScale; + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ShapePool.cs b/Monofoxe/Monofoxe.Engine/Collisions/ShapePool.cs new file mode 100644 index 0000000..6cb2344 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/ShapePool.cs @@ -0,0 +1,32 @@ +using Monofoxe.Engine.Collisions.Shapes; +using Monofoxe.Engine.Utils.CustomCollections; + +namespace Monofoxe.Engine.Collisions +{ + public static class ShapePool + { + private static Pool _circlePool = new Pool(); + private static Pool _polygonPool = new Pool(); + + + public static Circle GetCircle() => + _circlePool.Get(); + + + public static Polygon GetPolygon() => + _polygonPool.Get(); + + + public static void Return(IShape shape) + { + if (shape.Type == ShapeType.Circle) + { + _circlePool.Return((Circle)shape); + } + else + { + _polygonPool.Return((Polygon)shape); + } + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs new file mode 100644 index 0000000..7c77142 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs @@ -0,0 +1,32 @@ +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Utils; +using Monofoxe.Engine.Utils.CustomCollections; +using System.Runtime.InteropServices; + +namespace Monofoxe.Engine.Collisions.Shapes +{ + public class Circle : IShape, IPoolable + { + public ShapeType Type => ShapeType.Circle; + + + public float Radius; + public Vector2 Position; + + public AABB GetBoundingBox() + { + var a = new AABB(Position - new Vector2(Radius, Radius), Position + new Vector2(Radius, Radius)); + return a; + } + + public bool InPool { get; set; } + + public void OnTakenFromPool() {} + + public void OnReturnedToPool() + { + Position = Vector2.Zero; + Radius = 0; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs new file mode 100644 index 0000000..9d76174 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs @@ -0,0 +1,11 @@ +using Monofoxe.Engine.Utils; + +namespace Monofoxe.Engine.Collisions.Shapes +{ + public interface IShape + { + ShapeType Type { get; } + + AABB GetBoundingBox(); + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs new file mode 100644 index 0000000..6b3f46b --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs @@ -0,0 +1,121 @@ +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Utils; +using Monofoxe.Engine.Utils.CustomCollections; +using System; + +namespace Monofoxe.Engine.Collisions.Shapes +{ + public class Polygon : IShape, IPoolable + { + public ShapeType Type => ShapeType.Polygon; + + public Vector2 Position; + public Vector2 Origin; + public float Rotation = 0; + public float _cachedRotation = 0; + private Vector2 _rotationCosSin = new Vector2(1, 0); + + /// + /// Polygon verts in relative space, before any transformation gets applied. + /// + public readonly Vector2[] RelativeVertices = new Vector2[Settings.MaxPolygonVertices]; + + /// + /// Polygon verts in absolute space. + /// NOTE: These vertices should ONLY be generated from RelativeVertices by calling UpdateTransform() + /// + public readonly Vector2[] Vertices = new Vector2[Settings.MaxPolygonVertices]; + + public int Count = 0; + + private const float _radianConversion = MathF.PI / 180f; + + + private AABB _cachedAABB; + private bool _cachedAABBStale = true; + + public AABB GetBoundingBox() + { + if (_cachedAABBStale) + { + _cachedAABB = new AABB(ref Vertices[0], ref Vertices[1]); + + for (var i = 0; i < Count; i += 1) + { + _cachedAABB.TopLeft.X = Math.Min(Vertices[i].X, _cachedAABB.TopLeft.X); + _cachedAABB.TopLeft.Y = Math.Min(Vertices[i].Y, _cachedAABB.TopLeft.Y); + _cachedAABB.BottomRight.X = Math.Max(Vertices[i].X, _cachedAABB.BottomRight.X); + _cachedAABB.BottomRight.Y = Math.Max(Vertices[i].Y, _cachedAABB.BottomRight.Y); + } + _cachedAABBStale = false; + } + return _cachedAABB; + } + + + public void Add(Vector2 v) + { + if (Count >= Settings.MaxPolygonVertices) + { + throw new InvalidOperationException("Cannot add another vertex! Maximum allowed number of vertices per polygon is " + Settings.MaxPolygonVertices + ". Consider splitting your polygon."); + } + RelativeVertices[Count] = v; + Count += 1; + } + + + public void UpdateTransform() + { + _cachedAABBStale = true; + if (Rotation == 0) + { + for (var i = 0; i < Count; i += 1) + { + Vertices[i] = RelativeVertices[i] + Position; + } + _cachedRotation = Rotation; + } + else + { + if (Rotation != _cachedRotation) + { + _rotationCosSin.X = MathF.Cos(Rotation * _radianConversion); + _rotationCosSin.Y = MathF.Sin(Rotation * _radianConversion); + _cachedRotation = Rotation; + } + for (var i = 0; i < Count; i += 1) + { + Vertices[i] = RotateAroundOrigin(RelativeVertices[i]) + Position; + } + } + } + + + private Vector2 RotateAroundOrigin(Vector2 p) + { + p -= Origin; + + p = new Vector2( + p.X * _rotationCosSin.X - p.Y * _rotationCosSin.Y, + p.X * _rotationCosSin.Y + p.Y * _rotationCosSin.X + ) + Origin; + + return p; + } + + public bool InPool { get; set; } + + public void OnTakenFromPool() + { + } + + public void OnReturnedToPool() + { + Count = 0; + _cachedAABBStale = true; + Position = Vector2.Zero; + Origin = Vector2.Zero; + Rotation = 0; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs new file mode 100644 index 0000000..a304474 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs @@ -0,0 +1,8 @@ +namespace Monofoxe.Engine.Collisions.Shapes +{ + public enum ShapeType + { + Circle, + Polygon + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs b/Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs new file mode 100644 index 0000000..0570843 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs @@ -0,0 +1,55 @@ +using Microsoft.Xna.Framework; + +namespace Monofoxe.Engine.Collisions +{ + public static class UnitConversionExtensions + { + /// + /// Converts pixels to meters which are used by the collision detection. + /// + public static Vector2 ToMeters(this Vector2 v) => + v * Settings.OneOverWorldScale; + + /// + /// Converts meters to pixels which are used for rendering and everything else. + /// + public static Vector2 ToPixels(this Vector2 v) => + v * Settings.WorldScale; + + /// + /// Converts pixels to meters which are used by the collision detection. + /// + public static float ToMeters(this int v) => + v * Settings.OneOverWorldScale; + + /// + /// Converts meters to pixels which are used for rendering and everything else. + /// + public static float ToPixels(this int v) => + v * Settings.WorldScale; + + /// + /// Converts pixels to meters which are used by the collision detection. + /// + public static float ToMeters(this float v) => + v * Settings.OneOverWorldScale; + + /// + /// Converts meters to pixels which are used for rendering and everything else. + /// + public static float ToPixels(this float v) => + v * Settings.WorldScale; + + /// + /// Converts pixels to meters which are used by the collision detection. + /// + public static double ToMeters(this double v) => + v * Settings.OneOverWorldScale; + + /// + /// Converts meters to pixels which are used for rendering and everything else. + /// + public static double ToPixels(this double v) => + v * Settings.WorldScale; + } +} diff --git a/Monofoxe/Monofoxe.Engine/Utils/AABB.cs b/Monofoxe/Monofoxe.Engine/Utils/AABB.cs new file mode 100644 index 0000000..a3655fe --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Utils/AABB.cs @@ -0,0 +1,109 @@ +using Microsoft.Xna.Framework; +using System.Diagnostics.CodeAnalysis; + +namespace Monofoxe.Engine.Utils +{ + /// + /// An axis aligned bounding box. + /// + public struct AABB + { + /// + /// The lower vertex + /// + public Vector2 TopLeft; + + /// + /// The upper vertex + /// + public Vector2 BottomRight; + + public AABB(Vector2 min, Vector2 max) + : this(ref min, ref max) + { + } + + public AABB(ref Vector2 min, ref Vector2 max) + { + TopLeft = min; + BottomRight = max; + } + + public AABB(Vector2 center, float width, float height) + { + TopLeft = center - new Vector2(width / 2, height / 2); + BottomRight = center + new Vector2(width / 2, height / 2); + } + + + public Vector2 Size => BottomRight - TopLeft; + + + /// + /// Get the center of the AABB. + /// + public Vector2 Center + { + get { return 0.5f * (TopLeft + BottomRight); } + } + + /// + /// Get the extents of the AABB (half-widths). + /// + public Vector2 Extents + { + get { return 0.5f * (BottomRight - TopLeft); } + } + + + /// + /// Verify that the bounds are sorted. + /// + /// + /// true if this instance is valid; otherwise, false. + /// + public bool IsValid() + { + Vector2 d = BottomRight - TopLeft; + return d.X >= 0.0f && d.Y >= 0.0f; + } + + /// + /// Combine an AABB into this one. + /// + /// The aabb. + public void Combine(ref AABB aabb) + { + Vector2.Min(ref TopLeft, ref aabb.TopLeft, out TopLeft); + Vector2.Max(ref BottomRight, ref aabb.BottomRight, out BottomRight); + } + + /// + /// Combine two AABBs into this one. + /// + /// The aabb1. + /// The aabb2. + public void Combine(ref AABB aabb1, ref AABB aabb2) + { + Vector2.Min(ref aabb1.TopLeft, ref aabb2.TopLeft, out TopLeft); + Vector2.Max(ref aabb1.BottomRight, ref aabb2.BottomRight, out BottomRight); + } + + /// + /// Test if the two AABBs overlap. + /// + /// The first AABB. + /// The second AABB. + /// True if they are overlapping. + public static bool TestOverlap(ref AABB a, ref AABB b) + { + if (b.TopLeft.X > a.BottomRight.X || b.TopLeft.Y > a.BottomRight.Y) + return false; + + if (a.TopLeft.X > b.BottomRight.X || a.TopLeft.Y > b.BottomRight.Y) + return false; + + return true; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/AccumulationBuffer.cs b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/AccumulationBuffer.cs new file mode 100644 index 0000000..a53b626 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/AccumulationBuffer.cs @@ -0,0 +1,37 @@ +using System; + +namespace Monofoxe.Engine.Utils.CustomCollections +{ + public class AccumulationBuffer where T : struct + { + private T[] buffer; + + + public int Length { get; private set; } + + + public AccumulationBuffer(int capacity = 32) + { + buffer = new T[capacity]; + } + + public ref T this[int i] => ref buffer[i]; + + + public void Add(T obj) + { + if (Length >= buffer.Length) + { + Array.Resize(ref buffer, buffer.Length * 2); + } + buffer[Length] = obj; + Length += 1; + } + + + public void Clear() + { + Length = 0; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs new file mode 100644 index 0000000..0a8beb4 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs @@ -0,0 +1,10 @@ +namespace Monofoxe.Engine.Utils.CustomCollections +{ + public interface IPoolable + { + bool InPool { get; set; } + + void OnTakenFromPool(); + void OnReturnedToPool(); + } +} diff --git a/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/Pool.cs b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/Pool.cs new file mode 100644 index 0000000..754e4a9 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/Pool.cs @@ -0,0 +1,42 @@ +using System; + +namespace Monofoxe.Engine.Utils.CustomCollections +{ + public class Pool where T : IPoolable, new() + { + private UnorderedList _items = new UnorderedList(32); + + public T Get() + { + if (_items.Count > 0) + { + T obj; + obj = _items[_items.Count - 1]; + _items.RemoveLast(); + + obj.OnTakenFromPool(); + obj.InPool = false; + return obj; + } + else + { + T obj = new T(); + obj.OnTakenFromPool(); + obj.InPool = false; + return obj; + } + } + + + public void Return(T obj) + { + if (obj.InPool) + { + throw new InvalidOperationException("Provided object is already in a pool!"); + } + obj.OnReturnedToPool(); + obj.InPool = true; + _items.Add(obj); + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/UnorderedList.cs b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/UnorderedList.cs new file mode 100644 index 0000000..9e92af0 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/UnorderedList.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Monofoxe.Engine.Utils.CustomCollections +{ + /// + /// Operates like a regular list but the order of items is not guaranteed to stay the same. + /// As a tradeoff, it is way faster to remove items form this list. + /// Use it when you don't care about the order of items but want it to be as fast as possible. + /// + public class UnorderedList : IEnumerable, IEnumerable + { + private T[] _items; + private int _count = 0; + + public UnorderedList(int capacity = 1) + { + if (capacity <= 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity)); + } + + _items = new T[capacity]; + } + + + public T this[int index] + { + get + { + if (index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return _items[index]; + } + set + { + if (index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + _items[index] = value; + } + } + + + public int Count => _count; + + + public void Add(T item) + { + _count += 1; + if (_count >= _items.Length) + { + Array.Resize(ref _items, _items.Length * 2); + } + _items[_count - 1] = item; + } + + + public void Clear() + { + _count = 0; + } + + + public bool Contains(T item) + { + throw new NotImplementedException(); + } + + + public bool Remove(T item) + { + throw new NotImplementedException(); + } + + + public void RemoveAt(int index) + { + if (index < 0 || index >= _count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + _count -= 1; + if (index == _count) + { + return; + } + + // This changes the order of items but is way faster. + _items[index] = _items[_count]; + } + + + public void RemoveLast() + { + if (_count > 0) + { + _count -= 1; + } + } + + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } +} diff --git a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs new file mode 100644 index 0000000..c7f3234 --- /dev/null +++ b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs @@ -0,0 +1,115 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using Monofoxe.Engine.Collisions; +using Monofoxe.Engine; +using Monofoxe.Engine.Cameras; +using Monofoxe.Engine.Drawing; +using Monofoxe.Engine.EC; +using Monofoxe.Engine.Resources; +using Monofoxe.Engine.SceneSystem; +using Monofoxe.Engine.Utils; +using Monofoxe.Engine.Collisions.Shapes; +using System; + +namespace Monofoxe.Samples.Demos +{ + public class CollisionsDemo : Entity + { + private Circle _circle; + private Polygon _squarePolygon; + private bool _circleAndSquarePolygonCollided; + + public CollisionsDemo(Layer layer) : base(layer) + { + Settings.WorldScale = 16; + + _circle = ShapePool.GetCircle(); + // All collider measuremenets must be converted to meters, which is basically 1 / WorldScale. + // This is done for performance reasons. Collision detection works best with smaller shapes. + _circle.Radius = 30.ToMeters(); + + _squarePolygon = ShapePool.GetPolygon(); + // Note that we don't add vertices directly to the RelativeVertices array. + // It is possible to do that, however, you also must increment Count by 1. + _squarePolygon.Add(new Vector2(-32, -32).ToMeters()); + _squarePolygon.Add(new Vector2(-32, 32).ToMeters()); + _squarePolygon.Add(new Vector2(32, 32).ToMeters()); + _squarePolygon.Add(new Vector2(32, -32).ToMeters() * 3); + } + + + + public override void Update() + { + base.Update(); + + _squarePolygon.Position = new Vector2(200, 100).ToMeters(); + _squarePolygon.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _squarePolygon.UpdateTransform(); // Generating the correctly translated vertices. + + _circle.Position = new Vector2( + 100 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), + 100 + ).ToMeters(); + + _circle.Position = GameController.MainCamera.GetRelativeMousePosition().ToMeters(); + + _circleAndSquarePolygonCollided = CollisionChecker.CheckCollision(_circle, _squarePolygon); + } + + + public override void Draw() + { + base.Draw(); + + GraphicsMgr.CurrentColor = Color.White; + if (_circleAndSquarePolygonCollided) + { + GraphicsMgr.CurrentColor = Color.Red; + } + + DrawCircle(_circle); + DrawPolygon(_squarePolygon); + + DrawAABB(_circle); + DrawAABB(_squarePolygon); + } + + + public override void Destroy() + { + base.Destroy(); + + // Returning used shapes back to the pool. + ShapePool.Return(_circle); + ShapePool.Return(_squarePolygon); + } + + private void DrawCircle(Circle circle) + { + CircleShape.Draw(circle.Position.ToPixels(), circle.Radius.ToPixels(), true); + } + + private void DrawPolygon(Polygon poly) + { + for (var i = 0; i < poly.Count - 1; i += 1) + { + LineShape.Draw(poly.Vertices[i].ToPixels(), poly.Vertices[i + 1].ToPixels()); + } + LineShape.Draw(poly.Vertices[0].ToPixels(), poly.Vertices[poly.Count - 1].ToPixels()); + } + + private void DrawAABB(IShape shape) + { + GraphicsMgr.CurrentColor = Color.Gray; + var aabb = shape.GetBoundingBox(); + RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), true); + GraphicsMgr.CurrentColor = Color.Red; + CircleShape.Draw(aabb.BottomRight.ToPixels(), 3, true); + + GraphicsMgr.CurrentColor = Color.Green; + CircleShape.Draw(aabb.TopLeft.ToPixels(), 3, true); + } + + } +} diff --git a/Samples/Monofoxe.Samples/GameController.cs b/Samples/Monofoxe.Samples/GameController.cs index 85e78cd..cf6374c 100644 --- a/Samples/Monofoxe.Samples/GameController.cs +++ b/Samples/Monofoxe.Samples/GameController.cs @@ -12,7 +12,7 @@ namespace Monofoxe.Samples { public class GameController : Entity { - public Camera2D MainCamera = new Camera2D(800, 600); + public static Camera2D MainCamera = new Camera2D(800, 600); Layer _guiLayer; diff --git a/Samples/Monofoxe.Samples/SceneSwitcher.cs b/Samples/Monofoxe.Samples/SceneSwitcher.cs index 82e3094..200d473 100644 --- a/Samples/Monofoxe.Samples/SceneSwitcher.cs +++ b/Samples/Monofoxe.Samples/SceneSwitcher.cs @@ -15,6 +15,7 @@ public class SceneSwitcher : Entity { public List Factories = new List { + new SceneFactory(typeof(CollisionsDemo)), new SceneFactory(typeof(ShapeDemo)), new SceneFactory(typeof(PrimitiveDemo), PrimitiveDemo.Description), new SceneFactory(typeof(SpriteDemo)), From 12f724b67e96d8e6e0564dd6d10119250ba76f95 Mon Sep 17 00:00:00 2001 From: minkberry Date: Sat, 3 Feb 2024 02:21:19 +0100 Subject: [PATCH 11/22] Added Bayazip decomposer. --- .../Algorithms/BayazitDecomposer.cs | 383 ++++++++++++++++++ Monofoxe/Monofoxe.Engine/Utils/GameMath.cs | 57 +++ 2 files changed, 440 insertions(+) create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs new file mode 100644 index 0000000..43b7543 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs @@ -0,0 +1,383 @@ +/* Original source Farseer Physics Engine: + * Copyright (c) 2014 Ian Qvist, http://farseerphysics.codeplex.com + * Microsoft Permissive License (Ms-PL) v1.1 + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Utils; + +namespace Monofoxe.Engine.Collisions.Algorithms +{ + //From phed rev 36: http://code.google.com/p/phed/source/browse/trunk/Polygon.cpp + + /// + /// Convex decomposition algorithm created by Mark Bayazit (http://mnbayazit.com/) + /// + /// Properties: + /// - Tries to decompose using polygons instead of triangles. + /// - Tends to produce optimal results with low processing time. + /// - Running time is O(nr), n = number of vertices, r = reflex vertices. + /// - Does not support holes. + /// + /// For more information about this algorithm, see http://mnbayazit.com/406/bayazit + /// + internal static class BayazitDecomposer + { + /// + /// Decompose the polygon into several smaller non-concave polygon. + /// If the polygon is already convex, it will return the original polygon, unless it is over Settings.MaxPolygonVertices. + /// + public static List> ConvexPartition(List vertices) + { + Debug.Assert(vertices.Count > 3); + Debug.Assert(GameMath.IsCounterClockWise(vertices)); + + return TriangulatePolygon(vertices); + } + + private static List> TriangulatePolygon(List vertices) + { + List> list = new List>(); + Vector2 lowerInt = new Vector2(); + Vector2 upperInt = new Vector2(); // intersection points + int lowerIndex = 0, upperIndex = 0; + List lowerPoly, upperPoly; + + for (int i = 0; i < vertices.Count; ++i) + { + if (Reflex(i, vertices)) + { + float upperDist; + float lowerDist = upperDist = float.MaxValue; + for (int j = 0; j < vertices.Count; ++j) + { + // if line intersects with an edge + float d; + Vector2 p; + if (Left(At(i - 1, vertices), At(i, vertices), At(j, vertices)) && RightOn(At(i - 1, vertices), At(i, vertices), At(j - 1, vertices))) + { + // find the point of intersection + p = LineIntersect(At(i - 1, vertices), At(i, vertices), At(j, vertices), At(j - 1, vertices)); + + if (Right(At(i + 1, vertices), At(i, vertices), p)) + { + // make sure it's inside the poly + d = SquareDist(At(i, vertices), p); + if (d < lowerDist) + { + // keep only the closest intersection + lowerDist = d; + lowerInt = p; + lowerIndex = j; + } + } + } + + if (Left(At(i + 1, vertices), At(i, vertices), At(j + 1, vertices)) && RightOn(At(i + 1, vertices), At(i, vertices), At(j, vertices))) + { + p = LineIntersect(At(i + 1, vertices), At(i, vertices), At(j, vertices), At(j + 1, vertices)); + + if (Left(At(i - 1, vertices), At(i, vertices), p)) + { + d = SquareDist(At(i, vertices), p); + if (d < upperDist) + { + upperDist = d; + upperIndex = j; + upperInt = p; + } + } + } + } + + // if there are no vertices to connect to, choose a point in the middle + if (lowerIndex == (upperIndex + 1) % vertices.Count) + { + Vector2 p = (lowerInt + upperInt) / 2; + + lowerPoly = Copy(i, upperIndex, vertices); + lowerPoly.Add(p); + upperPoly = Copy(lowerIndex, i, vertices); + upperPoly.Add(p); + } + else + { + double highestScore = 0, bestIndex = lowerIndex; + while (upperIndex < lowerIndex) + upperIndex += vertices.Count; + + for (int j = lowerIndex; j <= upperIndex; ++j) + { + if (CanSee(i, j, vertices)) + { + double score = 1 / (SquareDist(At(i, vertices), At(j, vertices)) + 1); + if (Reflex(j, vertices)) + { + if (RightOn(At(j - 1, vertices), At(j, vertices), At(i, vertices)) && LeftOn(At(j + 1, vertices), At(j, vertices), At(i, vertices))) + score += 3; + else + score += 2; + } + else + { + score += 1; + } + if (score > highestScore) + { + bestIndex = j; + highestScore = score; + } + } + } + lowerPoly = Copy(i, (int)bestIndex, vertices); + upperPoly = Copy((int)bestIndex, i, vertices); + } + list.AddRange(TriangulatePolygon(lowerPoly)); + list.AddRange(TriangulatePolygon(upperPoly)); + return list; + } + } + + // polygon is already convex + if (vertices.Count > Settings.MaxPolygonVertices) + { + lowerPoly = Copy(0, vertices.Count / 2, vertices); + upperPoly = Copy(vertices.Count / 2, 0, vertices); + list.AddRange(TriangulatePolygon(lowerPoly)); + list.AddRange(TriangulatePolygon(upperPoly)); + } + else + list.Add(vertices); + + return list; + } + + private static Vector2 At(int i, List vertices) + { + int s = vertices.Count; + return vertices[i < 0 ? s - 1 - (-i - 1) % s : i % s]; + } + + private static List Copy(int i, int j, List vertices) + { + while (j < i) + j += vertices.Count; + + List p = new List(j); + + for (; i <= j; ++i) + { + p.Add(At(i, vertices)); + } + return p; + } + + private static bool CanSee(int i, int j, List vertices) + { + if (Reflex(i, vertices)) + { + if (LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices)) && RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices))) + return false; + } + else + { + if (RightOn(At(i, vertices), At(i + 1, vertices), At(j, vertices)) || LeftOn(At(i, vertices), At(i - 1, vertices), At(j, vertices))) + return false; + } + if (Reflex(j, vertices)) + { + if (LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices)) && RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices))) + return false; + } + else + { + if (RightOn(At(j, vertices), At(j + 1, vertices), At(i, vertices)) || LeftOn(At(j, vertices), At(j - 1, vertices), At(i, vertices))) + return false; + } + for (int k = 0; k < vertices.Count; ++k) + { + if ((k + 1) % vertices.Count == i || k == i || (k + 1) % vertices.Count == j || k == j) + continue; // ignore incident edges + + Vector2 intersectionPoint; + + if (LineIntersect(At(i, vertices), At(j, vertices), At(k, vertices), At(k + 1, vertices), out intersectionPoint)) + return false; + } + return true; + } + + private static bool Reflex(int i, List vertices) + { + return Right(i, vertices); + } + + private static bool Right(int i, List vertices) + { + return Right(At(i - 1, vertices), At(i, vertices), At(i + 1, vertices)); + } + + private static bool Left(Vector2 a, Vector2 b, Vector2 c) + { + return Area(ref a, ref b, ref c) > 0; + } + + private static bool LeftOn(Vector2 a, Vector2 b, Vector2 c) + { + return Area(ref a, ref b, ref c) >= 0; + } + + private static bool Right(Vector2 a, Vector2 b, Vector2 c) + { + return Area(ref a, ref b, ref c) < 0; + } + + private static bool RightOn(Vector2 a, Vector2 b, Vector2 c) + { + return Area(ref a, ref b, ref c) <= 0; + } + + private static float SquareDist(Vector2 a, Vector2 b) + { + float dx = b.X - a.X; + float dy = b.Y - a.Y; + return dx * dx + dy * dy; + } + + + //From Mark Bayazit's convex decomposition algorithm + public static Vector2 LineIntersect(Vector2 p1, Vector2 p2, Vector2 q1, Vector2 q2) + { + Vector2 i = Vector2.Zero; + float a1 = p2.Y - p1.Y; + float b1 = p1.X - p2.X; + float c1 = a1 * p1.X + b1 * p1.Y; + float a2 = q2.Y - q1.Y; + float b2 = q1.X - q2.X; + float c2 = a2 * q1.X + b2 * q1.Y; + float det = a1 * b2 - a2 * b1; + + if (!FloatEquals(det, 0)) + { + // lines are not parallel + i.X = (b2 * c1 - b1 * c2) / det; + i.Y = (a1 * c2 - a2 * c1) / det; + } + return i; + } + + /// + /// This method detects if two line segments (or lines) intersect, + /// and, if so, the point of intersection. Use the and + /// parameters to set whether the intersection point + /// must be on the first and second line segments. Setting these + /// both to true means you are doing a line-segment to line-segment + /// intersection. Setting one of them to true means you are doing a + /// line to line-segment intersection test, and so on. + /// Note: If two line segments are coincident, then + /// no intersection is detected (there are actually + /// infinite intersection points). + /// Author: Jeremy Bell + /// + /// The first point of the first line segment. + /// The second point of the first line segment. + /// The first point of the second line segment. + /// The second point of the second line segment. + /// This is set to the intersection + /// point if an intersection is detected. + /// Set this to true to require that the + /// intersection point be on the first line segment. + /// Set this to true to require that the + /// intersection point be on the second line segment. + /// True if an intersection is detected, false otherwise. + public static bool LineIntersect(ref Vector2 point1, ref Vector2 point2, ref Vector2 point3, ref Vector2 point4, bool firstIsSegment, bool secondIsSegment, out Vector2 point) + { + point = new Vector2(); + + // these are reused later. + // each lettered sub-calculation is used twice, except + // for b and d, which are used 3 times + float a = point4.Y - point3.Y; + float b = point2.X - point1.X; + float c = point4.X - point3.X; + float d = point2.Y - point1.Y; + + // denominator to solution of linear system + float denom = a * b - c * d; + + // if denominator is 0, then lines are parallel + if (!(denom >= -Settings.Epsilon && denom <= Settings.Epsilon)) + { + float e = point1.Y - point3.Y; + float f = point1.X - point3.X; + float oneOverDenom = 1.0f / denom; + + // numerator of first equation + float ua = c * e - a * f; + ua *= oneOverDenom; + + // check if intersection point of the two lines is on line segment 1 + if (!firstIsSegment || ua >= 0.0f && ua <= 1.0f) + { + // numerator of second equation + float ub = b * e - d * f; + ub *= oneOverDenom; + + // check if intersection point of the two lines is on line segment 2 + // means the line segments intersect, since we know it is on + // segment 1 as well. + if (!secondIsSegment || ub >= 0.0f && ub <= 1.0f) + { + // check if they are coincident (no collision in this case) + if (ua != 0f || ub != 0f) + { + //There is an intersection + point.X = point1.X + ua * b; + point.Y = point1.Y + ua * d; + return true; + } + } + } + } + + return false; + } + + /// + /// This method detects if two line segments intersect, + /// and, if so, the point of intersection. + /// Note: If two line segments are coincident, then + /// no intersection is detected (there are actually + /// infinite intersection points). + /// + /// The first point of the first line segment. + /// The second point of the first line segment. + /// The first point of the second line segment. + /// The second point of the second line segment. + /// This is set to the intersection + /// point if an intersection is detected. + /// True if an intersection is detected, false otherwise. + public static bool LineIntersect(Vector2 point1, Vector2 point2, Vector2 point3, Vector2 point4, out Vector2 intersectionPoint) + { + return LineIntersect(ref point1, ref point2, ref point3, ref point4, true, true, out intersectionPoint); + } + + public static bool FloatEquals(float value1, float value2) + { + return Math.Abs(value1 - value2) <= Settings.Epsilon; + } + + /// + /// Returns a positive number if c is to the left of the line going from a to b. + /// + /// Positive number if point is left, negative if point is right, + /// and 0 if points are collinear. + public static float Area(ref Vector2 a, ref Vector2 b, ref Vector2 c) + { + return a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y); + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs index 75adb25..55c722f 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; using Microsoft.Xna.Framework; namespace Monofoxe.Engine.Utils @@ -150,5 +152,60 @@ public static int LinesCross(Vector2 line1Pt1, Vector2 line1Pt2, Vector2 line2Pt } #endregion Intersections. + + + + /// + /// Indicates if the vertices are in counter clockwise order. + /// Warning: If the area of the polygon is 0, it is unable to determine the winding. + /// + public static bool IsCounterClockWise(List vertices) + { + //The simplest polygon which can exist in the Euclidean plane has 3 sides. + if (vertices.Count < 3) + return false; + + return (GetSignedArea(vertices) > 0.0f); + } + + + /// + /// Gets the signed area. + /// If the area is less than 0, it indicates that the polygon is clockwise winded. + /// + /// The signed area + public static float GetSignedArea(List vertices) + { + //The simplest polygon which can exist in the Euclidean plane has 3 sides. + if (vertices.Count < 3) + return 0; + + int i; + float area = 0; + + for (i = 0; i < vertices.Count; i++) + { + int j = (i + 1) % vertices.Count; + + Vector2 vi = vertices[i]; + Vector2 vj = vertices[j]; + + area += vi.X * vj.Y; + area -= vi.Y * vj.X; + } + area /= 2.0f; + return area; + } + + + /// + /// Gets the area. + /// + /// + public static float GetArea(List vertices) + { + float area = GetSignedArea(vertices); + return (area < 0 ? -area : area); + } } } From 0965a11adcad9e271f55e9cc5577f6fba5ecabdf Mon Sep 17 00:00:00 2001 From: minkberry Date: Sun, 4 Feb 2024 00:16:01 +0100 Subject: [PATCH 12/22] Fixed body winding. --- .../Collisions/Algorithms/BayazitDecomposer.cs | 2 +- Monofoxe/Monofoxe.Engine/Utils/GameMath.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs index 43b7543..20f5b6d 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs @@ -33,7 +33,7 @@ internal static class BayazitDecomposer public static List> ConvexPartition(List vertices) { Debug.Assert(vertices.Count > 3); - Debug.Assert(GameMath.IsCounterClockWise(vertices)); + Debug.Assert(GameMath.IsClockWise(vertices)); return TriangulatePolygon(vertices); } diff --git a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs index 55c722f..a2437b4 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/GameMath.cs @@ -156,10 +156,10 @@ public static int LinesCross(Vector2 line1Pt1, Vector2 line1Pt2, Vector2 line2Pt /// - /// Indicates if the vertices are in counter clockwise order. + /// Indicates if the vertices are in clockwise order. /// Warning: If the area of the polygon is 0, it is unable to determine the winding. /// - public static bool IsCounterClockWise(List vertices) + public static bool IsClockWise(List vertices) { //The simplest polygon which can exist in the Euclidean plane has 3 sides. if (vertices.Count < 3) @@ -171,7 +171,7 @@ public static bool IsCounterClockWise(List vertices) /// /// Gets the signed area. - /// If the area is less than 0, it indicates that the polygon is clockwise winded. + /// If the area is less than 0, it indicates that the polygon is couter clockwise winded. /// /// The signed area public static float GetSignedArea(List vertices) From a912f4860d78d541b420b49a37941cbf54dd045f Mon Sep 17 00:00:00 2001 From: minkberry Date: Sun, 4 Feb 2024 00:37:58 +0100 Subject: [PATCH 13/22] Added colliders. --- .../Algorithms/BayazitDecomposer.cs | 2 + .../Monofoxe.Engine/Collisions/Collider.cs | 127 ++++++++++++++++++ .../Collisions/ColliderFactory.cs | 88 ++++++++++++ .../Collisions/ColliderPool.cs | 21 +++ .../Collisions/CollisionChecker.cs | 60 +++------ .../Collisions/ShapeCollisionChecker.cs | 58 ++++++++ .../Collisions/Shapes/Polygon.cs | 16 +-- .../Monofoxe.Samples/Demos/CollisionsDemo.cs | 95 ++++++++++++- 8 files changed, 411 insertions(+), 56 deletions(-) create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Collider.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs index 20f5b6d..bddeb60 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs @@ -9,6 +9,8 @@ using Microsoft.Xna.Framework; using Monofoxe.Engine.Utils; +// TODO: Add inlines. + namespace Monofoxe.Engine.Collisions.Algorithms { //From phed rev 36: http://code.google.com/p/phed/source/browse/trunk/Polygon.cpp diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs b/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs new file mode 100644 index 0000000..c1f3548 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs @@ -0,0 +1,127 @@ +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Collisions.Shapes; +using Monofoxe.Engine.Utils; +using Monofoxe.Engine.Utils.CustomCollections; +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace Monofoxe.Engine.Collisions +{ + public class Collider : IPoolable + { + public bool InPool { get ; set; } + + private List _shapes = new List(); + private List _relativeShapePositions = new List(); + + + public Vector2 Position; + public Vector2 Origin; + public Vector2 _rotatedOrigin; + public float Rotation; + public float _cachedRotation = 0; + private Vector2 _rotationCosSin = new Vector2(1, 0); + + + public int ShapesCount => _shapes.Count; + + private const float _radianConversion = MathF.PI / 180f; + + public void AddShape(Vector2 relativePosition, IShape shape) + { + _shapes.Add(shape); + _relativeShapePositions.Add(relativePosition); + } + + public void RemoveShape(IShape shape) + { + var index = _shapes.IndexOf(shape); + if (index != -1) + { + _shapes.RemoveAt(index); + _relativeShapePositions.RemoveAt(index); + } + } + + + public IShape GetShape(int index = 0) + { + return _shapes[index]; + } + + + public Vector2 GetShapeRelativePosition(int index = 0) + { + return _relativeShapePositions[index]; + } + + + public void SetShapeRelativePosition(Vector2 relativePosition, int index = 0) + { + _relativeShapePositions[index] = relativePosition; + } + + + public void UpdateTransform() + { + if (Rotation != _cachedRotation) + { + _rotationCosSin.X = MathF.Cos(Rotation * _radianConversion); + _rotationCosSin.Y = MathF.Sin(Rotation * _radianConversion); + _cachedRotation = Rotation; + } + + _rotatedOrigin = new Vector2( + -Origin.X * _rotationCosSin.X + Origin.Y * _rotationCosSin.Y, + -Origin.X * _rotationCosSin.Y - Origin.Y * _rotationCosSin.X + ); + + + for (var i = 0; i < _shapes.Count; i += 1) + { + switch(_shapes[i].Type) + { + case ShapeType.Circle: + var circle = (Circle)_shapes[i]; + // TODO: Add rotation. + circle.Position = _rotatedOrigin + Position; + break; + case ShapeType.Polygon: + var poly = (Polygon)_shapes[i]; + poly.Position = _rotatedOrigin + Position + _relativeShapePositions[i]; + poly.Rotation = Rotation; + //poly.Origin = _relativeShapePositions[i]; + poly.UpdateTransform(); + break; + } + } + } + + public AABB GetBoundingBox() + { + // TODO: Add AABB caching? + var aabb = _shapes[0].GetBoundingBox(); + for(var i = 1; i < _shapes.Count; i += 1) + { + var aabb1 = _shapes[i].GetBoundingBox(); + aabb.Combine(ref aabb1); + } + + return aabb; + } + + + public void OnReturnedToPool() + { + _shapes.Clear(); + for(var i = 0; i < _shapes.Count; i += 1) + { + ShapePool.Return(_shapes[i]); + } + _relativeShapePositions.Clear(); + } + + public void OnTakenFromPool() {} + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs b/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs new file mode 100644 index 0000000..850c87b --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs @@ -0,0 +1,88 @@ +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Collisions.Algorithms; +using Monofoxe.Engine.Utils; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Monofoxe.Engine.Collisions +{ + public static class ColliderFactory + { + + public static Collider CreateCircle(float r) + { + var collider = ColliderPool.GetCollider(); + + var circle = ShapePool.GetCircle(); + circle.Radius = r; + collider.AddShape(Vector2.Zero, circle); + + return collider; + } + + + public static Collider CreateRectangle(Vector2 size) + { + var collider = ColliderPool.GetCollider(); + var poly = ShapePool.GetPolygon(); + + var halfSize = size / 2; + + // 0---1 + // | \ | + // 3---2 + poly.Add(new Vector2(-halfSize.X, -halfSize.Y)); + poly.Add(new Vector2( halfSize.X, -halfSize.Y)); + poly.Add(new Vector2( halfSize.X, halfSize.Y)); + poly.Add(new Vector2(-halfSize.X, halfSize.Y)); + + collider.AddShape(Vector2.Zero, poly); + + var isit = GameMath.IsClockWise(poly.RelativeVertices.ToList()); + + return collider; + } + + + public static Collider CreateLine(float length) + { + throw new NotImplementedException(); + + } + + + public static Collider CreateRing(float arc, float r, float thickness) + { + throw new NotImplementedException(); + } + + + public static Collider CreateSector(float arc, float r) + { + throw new NotImplementedException(); + } + + + public static Collider CreatePolygon(List vertices) + { + var collider = ColliderPool.GetCollider(); + + var polys = BayazitDecomposer.ConvexPartition(vertices); + + for (var i = 0; i < polys.Count; i += 1) + { + var poly = ShapePool.GetPolygon(); + + for (var k = 0; k < polys[i].Count; k += 1) + { + poly.Add(polys[i][k]); + } + + collider.AddShape(Vector2.Zero, poly); + } + + return collider; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs b/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs new file mode 100644 index 0000000..b810349 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs @@ -0,0 +1,21 @@ +using Monofoxe.Engine.Utils.CustomCollections; + +namespace Monofoxe.Engine.Collisions +{ + public static class ColliderPool + { + private static Pool _colliderPool = new Pool(); + + + public static Collider GetCollider() => + _colliderPool.Get(); + + + public static void Return(Collider collider) + { + _colliderPool.Return(collider); + } + + + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs index 765a40b..3085a2b 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs @@ -1,55 +1,33 @@ -using Monofoxe.Engine.Collisions.Algorithms; -using Monofoxe.Engine.Collisions.Shapes; -using Monofoxe.Engine.Utils; +using Monofoxe.Engine.Collisions.Shapes; namespace Monofoxe.Engine.Collisions { public static class CollisionChecker { - public static bool CheckCollision(IShape a, IShape b) + public static bool CheckCollision(Collider a, Collider b) { - if (a.Type == ShapeType.Circle && b.Type == ShapeType.Circle) + for (var i = 0; i < a.ShapesCount; i += 1) { - // AABB check for this case is kinda redundant. - var circle1 = (Circle)a; - var circle2 = (Circle)b; - return SimpleCollisionDetection.CheckCircleCircle(circle1.Position, circle1.Radius, circle2.Position, circle2.Radius); - } - - var aabb1 = a.GetBoundingBox(); - var aabb2 = b.GetBoundingBox(); - if (!AABB.TestOverlap(ref aabb2, ref aabb1)) - { - return false; - } - - switch (a.Type) - { - case ShapeType.Circle: - switch (b.Type) + for (var k = 0; k < b.ShapesCount; k += 1) + { + if (ShapeCollisionChecker.CheckCollision(a.GetShape(i), b.GetShape(k))) { - // No circle-circle case because it is handled in the beginning. - case ShapeType.Polygon: - var circle = (Circle)a; - var poly = (Polygon)b; - return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); + return true; } - break; - case ShapeType.Polygon: - switch (b.Type) - { - case ShapeType.Circle: - var circle = (Circle)b; - var poly = (Polygon)a; - return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); - case ShapeType.Polygon: - var poly1 = (Polygon)a; - var poly2 = (Polygon)b; - return GJK.CheckPolyPoly(poly1.Vertices, poly1.Count, poly2.Vertices, poly2.Count); - } - break; + } } + return false; + } + public static bool CheckCollision(Collider a, IShape b) + { + for (var i = 0; i < a.ShapesCount; i += 1) + { + if (ShapeCollisionChecker.CheckCollision(a.GetShape(i), b)) + { + return true; + } + } return false; } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs b/Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs new file mode 100644 index 0000000..9e7af31 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs @@ -0,0 +1,58 @@ +using Monofoxe.Engine.Collisions.Algorithms; +using Monofoxe.Engine.Collisions.Shapes; +using Monofoxe.Engine.Utils; +using System.Runtime.CompilerServices; + +namespace Monofoxe.Engine.Collisions +{ + public static class ShapeCollisionChecker + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckCollision(IShape a, IShape b) + { + if (a.Type == ShapeType.Circle && b.Type == ShapeType.Circle) + { + // AABB check for this case is kinda redundant. + var circle1 = (Circle)a; + var circle2 = (Circle)b; + return SimpleCollisionDetection.CheckCircleCircle(circle1.Position, circle1.Radius, circle2.Position, circle2.Radius); + } + + var aabb1 = a.GetBoundingBox(); + var aabb2 = b.GetBoundingBox(); + if (!AABB.TestOverlap(ref aabb2, ref aabb1)) + { + return false; + } + + switch (a.Type) + { + case ShapeType.Circle: + switch (b.Type) + { + // No circle-circle case because it is handled in the beginning. + case ShapeType.Polygon: + var circle = (Circle)a; + var poly = (Polygon)b; + return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); + } + break; + case ShapeType.Polygon: + switch (b.Type) + { + case ShapeType.Circle: + var circle = (Circle)b; + var poly = (Polygon)a; + return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); + case ShapeType.Polygon: + var poly1 = (Polygon)a; + var poly2 = (Polygon)b; + return GJK.CheckPolyPoly(poly1.Vertices, poly1.Count, poly2.Vertices, poly2.Count); + } + break; + } + + return false; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs index 6b3f46b..658110e 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs @@ -2,6 +2,7 @@ using Monofoxe.Engine.Utils; using Monofoxe.Engine.Utils.CustomCollections; using System; +using System.Runtime.CompilerServices; namespace Monofoxe.Engine.Collisions.Shapes { @@ -10,7 +11,6 @@ public class Polygon : IShape, IPoolable public ShapeType Type => ShapeType.Polygon; public Vector2 Position; - public Vector2 Origin; public float Rotation = 0; public float _cachedRotation = 0; private Vector2 _rotationCosSin = new Vector2(1, 0); @@ -85,22 +85,19 @@ public void UpdateTransform() } for (var i = 0; i < Count; i += 1) { - Vertices[i] = RotateAroundOrigin(RelativeVertices[i]) + Position; + Vertices[i] = RotateVertex(RelativeVertices[i]) + Position; } } } - private Vector2 RotateAroundOrigin(Vector2 p) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Vector2 RotateVertex(Vector2 p) { - p -= Origin; - - p = new Vector2( + return new Vector2( p.X * _rotationCosSin.X - p.Y * _rotationCosSin.Y, p.X * _rotationCosSin.Y + p.Y * _rotationCosSin.X - ) + Origin; - - return p; + ); } public bool InPool { get; set; } @@ -114,7 +111,6 @@ public void OnReturnedToPool() Count = 0; _cachedAABBStale = true; Position = Vector2.Zero; - Origin = Vector2.Zero; Rotation = 0; } } diff --git a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs index c7f3234..2530b4d 100644 --- a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs @@ -10,14 +10,21 @@ using Monofoxe.Engine.Utils; using Monofoxe.Engine.Collisions.Shapes; using System; +using System.Collections.Generic; namespace Monofoxe.Samples.Demos { public class CollisionsDemo : Entity { + private Collider _circleCollider1; + private Collider _circleCollider2; + private Circle _circle; private Polygon _squarePolygon; private bool _circleAndSquarePolygonCollided; + private bool _collidersCollided; + private Collider _polyCollider; + private Collider _rectCollider; public CollisionsDemo(Layer layer) : base(layer) { @@ -26,15 +33,29 @@ public CollisionsDemo(Layer layer) : base(layer) _circle = ShapePool.GetCircle(); // All collider measuremenets must be converted to meters, which is basically 1 / WorldScale. // This is done for performance reasons. Collision detection works best with smaller shapes. - _circle.Radius = 30.ToMeters(); + _circle.Radius = 3.ToMeters(); _squarePolygon = ShapePool.GetPolygon(); // Note that we don't add vertices directly to the RelativeVertices array. // It is possible to do that, however, you also must increment Count by 1. _squarePolygon.Add(new Vector2(-32, -32).ToMeters()); - _squarePolygon.Add(new Vector2(-32, 32).ToMeters()); - _squarePolygon.Add(new Vector2(32, 32).ToMeters()); _squarePolygon.Add(new Vector2(32, -32).ToMeters() * 3); + _squarePolygon.Add(new Vector2(32, 32).ToMeters()); + _squarePolygon.Add(new Vector2(-32, 32).ToMeters()); + + _circleCollider1 = ColliderFactory.CreateCircle(32.ToMeters()); + _circleCollider2 = ColliderFactory.CreateCircle(64.ToMeters()); + + + var verts = new List(); + for(var i = 0; i < 8; i += 1) + { + verts.Add(new Angle(360 / 8f * i).ToVector2() * (80 + MathF.Cos(i * 0.4f) * 6).ToMeters()); + } + + _polyCollider = ColliderFactory.CreatePolygon(verts); + _rectCollider = ColliderFactory.CreateRectangle(new Vector2(32, 32).ToMeters()); + _rectCollider.Origin = new Vector2(-16, -16).ToMeters(); } @@ -43,10 +64,18 @@ public override void Update() { base.Update(); + _rectCollider.Position = new Vector2(500, 400).ToMeters(); + _rectCollider.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _rectCollider.UpdateTransform(); + _squarePolygon.Position = new Vector2(200, 100).ToMeters(); _squarePolygon.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; _squarePolygon.UpdateTransform(); // Generating the correctly translated vertices. - + + _circleCollider1.Origin = new Vector2(32, 0).ToMeters(); + _circleCollider2.Origin = new Vector2(0, -64).ToMeters(); + _circleCollider1.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _circle.Position = new Vector2( 100 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), 100 @@ -54,7 +83,24 @@ public override void Update() _circle.Position = GameController.MainCamera.GetRelativeMousePosition().ToMeters(); - _circleAndSquarePolygonCollided = CollisionChecker.CheckCollision(_circle, _squarePolygon); + _circleAndSquarePolygonCollided = ShapeCollisionChecker.CheckCollision(_circle, _squarePolygon); + + _circleCollider1.Position = new Vector2( + 100 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), + 250 + ).ToMeters(); + _circleCollider1.UpdateTransform(); + _circleCollider2.Position = new Vector2( + 100 - 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), + 250 + ).ToMeters(); + _circleCollider2.UpdateTransform(); + + _polyCollider.Position = new Vector2(500, 300).ToMeters(); + _polyCollider.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _polyCollider.UpdateTransform(); + + _collidersCollided = CollisionChecker.CheckCollision(_circleCollider1, _circleCollider2); } @@ -73,6 +119,21 @@ public override void Draw() DrawAABB(_circle); DrawAABB(_squarePolygon); + + + GraphicsMgr.CurrentColor = Color.White; + if (_collidersCollided) + { + GraphicsMgr.CurrentColor = Color.Red; + } + DrawCollider(_circleCollider1); + DrawCollider(_circleCollider2); + + + //GraphicsMgr.CurrentColor = Color.White; + DrawCollider(_polyCollider); + + DrawCollider(_rectCollider); } @@ -85,6 +146,25 @@ public override void Destroy() ShapePool.Return(_squarePolygon); } + + private void DrawCollider(Collider collider) + { + for(var i = 0; i < collider.ShapesCount; i += 1) + { + var shape = collider.GetShape(i); + if (shape.Type == ShapeType.Circle) + { + DrawCircle((Circle)shape); + } + if (shape.Type == ShapeType.Polygon) + { + DrawPolygon((Polygon)shape); + } + } + CircleShape.Draw(collider.Position.ToPixels(), 4, true); + } + + private void DrawCircle(Circle circle) { CircleShape.Draw(circle.Position.ToPixels(), circle.Radius.ToPixels(), true); @@ -97,6 +177,11 @@ private void DrawPolygon(Polygon poly) LineShape.Draw(poly.Vertices[i].ToPixels(), poly.Vertices[i + 1].ToPixels()); } LineShape.Draw(poly.Vertices[0].ToPixels(), poly.Vertices[poly.Count - 1].ToPixels()); + + for (var i = 0; i < poly.Count; i += 1) + { + CircleShape.Draw(poly.Vertices[i].ToPixels(), i * 2, false); + } } private void DrawAABB(IShape shape) From 2b810e6700c854c1dec610cc1b9efd8f206c803b Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 01:14:58 +0100 Subject: [PATCH 14/22] Added docs. --- .../Algorithms/BayazitDecomposer.cs | 23 +++++-- .../Monofoxe.Engine/Collisions/Collider.cs | 60 +++++++++++------- .../Collisions/ColliderFactory.cs | 19 +++--- .../Collisions/ColliderPool.cs | 2 - .../Collisions/CollisionChecker.cs | 59 +++++++++++++++++- .../{Settings.cs => CollisionSettings.cs} | 12 ++-- .../Collisions/ShapeCollisionChecker.cs | 58 ----------------- .../Collisions/Shapes/Circle.cs | 26 ++++++-- .../Collisions/Shapes/IShape.cs | 3 + .../Collisions/Shapes/Polygon.cs | 62 ++++++++++++++++--- .../Collisions/Shapes/ShapeType.cs | 6 ++ .../Collisions/UnitConversionExtensions.cs | 16 ++--- .../Utils/CustomCollections/IPoolable.cs | 10 +++ .../Monofoxe.Samples/Demos/CollisionsDemo.cs | 4 +- 14 files changed, 230 insertions(+), 130 deletions(-) rename Monofoxe/Monofoxe.Engine/Collisions/{Settings.cs => CollisionSettings.cs} (50%) delete mode 100644 Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs index bddeb60..82ed47b 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Algorithms/BayazitDecomposer.cs @@ -6,11 +6,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using Microsoft.Xna.Framework; using Monofoxe.Engine.Utils; -// TODO: Add inlines. - namespace Monofoxe.Engine.Collisions.Algorithms { //From phed rev 36: http://code.google.com/p/phed/source/browse/trunk/Polygon.cpp @@ -26,7 +25,7 @@ namespace Monofoxe.Engine.Collisions.Algorithms /// /// For more information about this algorithm, see http://mnbayazit.com/406/bayazit /// - internal static class BayazitDecomposer + public static class BayazitDecomposer { /// /// Decompose the polygon into several smaller non-concave polygon. @@ -144,7 +143,7 @@ private static List> TriangulatePolygon(List vertices) } // polygon is already convex - if (vertices.Count > Settings.MaxPolygonVertices) + if (vertices.Count > CollisionSettings.MaxPolygonVertices) { lowerPoly = Copy(0, vertices.Count / 2, vertices); upperPoly = Copy(vertices.Count / 2, 0, vertices); @@ -157,12 +156,14 @@ private static List> TriangulatePolygon(List vertices) return list; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector2 At(int i, List vertices) { int s = vertices.Count; return vertices[i < 0 ? s - 1 - (-i - 1) % s : i % s]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static List Copy(int i, int j, List vertices) { while (j < i) @@ -177,6 +178,7 @@ private static List Copy(int i, int j, List vertices) return p; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool CanSee(int i, int j, List vertices) { if (Reflex(i, vertices)) @@ -212,36 +214,43 @@ private static bool CanSee(int i, int j, List vertices) return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Reflex(int i, List vertices) { return Right(i, vertices); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Right(int i, List vertices) { return Right(At(i - 1, vertices), At(i, vertices), At(i + 1, vertices)); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Left(Vector2 a, Vector2 b, Vector2 c) { return Area(ref a, ref b, ref c) > 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool LeftOn(Vector2 a, Vector2 b, Vector2 c) { return Area(ref a, ref b, ref c) >= 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool Right(Vector2 a, Vector2 b, Vector2 c) { return Area(ref a, ref b, ref c) < 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool RightOn(Vector2 a, Vector2 b, Vector2 c) { return Area(ref a, ref b, ref c) <= 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static float SquareDist(Vector2 a, Vector2 b) { float dx = b.X - a.X; @@ -311,7 +320,7 @@ public static bool LineIntersect(ref Vector2 point1, ref Vector2 point2, ref Vec float denom = a * b - c * d; // if denominator is 0, then lines are parallel - if (!(denom >= -Settings.Epsilon && denom <= Settings.Epsilon)) + if (!(denom >= -CollisionSettings.Epsilon && denom <= CollisionSettings.Epsilon)) { float e = point1.Y - point3.Y; float f = point1.X - point3.X; @@ -367,9 +376,10 @@ public static bool LineIntersect(Vector2 point1, Vector2 point2, Vector2 point3, return LineIntersect(ref point1, ref point2, ref point3, ref point4, true, true, out intersectionPoint); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool FloatEquals(float value1, float value2) { - return Math.Abs(value1 - value2) <= Settings.Epsilon; + return Math.Abs(value1 - value2) <= CollisionSettings.Epsilon; } /// @@ -377,6 +387,7 @@ public static bool FloatEquals(float value1, float value2) /// /// Positive number if point is left, negative if point is right, /// and 0 if points are collinear. + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Area(ref Vector2 a, ref Vector2 b, ref Vector2 c) { return a.X * (b.Y - c.Y) + b.X * (c.Y - a.Y) + c.X * (a.Y - b.Y); diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs b/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs index c1f3548..f1a4cfa 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs @@ -4,43 +4,60 @@ using Monofoxe.Engine.Utils.CustomCollections; using System; using System.Collections.Generic; -using System.ComponentModel; namespace Monofoxe.Engine.Collisions { + /// + /// Defines a collection of convex shapes that act as a single shape. + /// public class Collider : IPoolable { - public bool InPool { get ; set; } - private List _shapes = new List(); - private List _relativeShapePositions = new List(); + /// + /// Absolute position of the collider, measured in meters. + /// NOTE: You should always call UpdateTransform() after changing this field. + /// public Vector2 Position; + + /// + /// Origin point for the collider rotation, measured in meters. + /// NOTE: You should always call UpdateTransform() after changing this field. + /// public Vector2 Origin; public Vector2 _rotatedOrigin; + + /// + /// Rotation of the collider, measured in degreen. + /// NOTE: You should always call UpdateTransform() after changing this field. + /// public float Rotation; public float _cachedRotation = 0; private Vector2 _rotationCosSin = new Vector2(1, 0); + /// + /// NOTE: It is recommended to use ColliderPool to get new instances of this class. + /// + public Collider() { } public int ShapesCount => _shapes.Count; private const float _radianConversion = MathF.PI / 180f; - public void AddShape(Vector2 relativePosition, IShape shape) + + public void AddShape(IShape shape) { _shapes.Add(shape); - _relativeShapePositions.Add(relativePosition); } + public void RemoveShape(IShape shape) { var index = _shapes.IndexOf(shape); if (index != -1) { _shapes.RemoveAt(index); - _relativeShapePositions.RemoveAt(index); } } @@ -51,18 +68,10 @@ public IShape GetShape(int index = 0) } - public Vector2 GetShapeRelativePosition(int index = 0) - { - return _relativeShapePositions[index]; - } - - - public void SetShapeRelativePosition(Vector2 relativePosition, int index = 0) - { - _relativeShapePositions[index] = relativePosition; - } - - + /// + /// Applies Position, Rotation, Origin to the shapes. + /// Must be called after any changes to the collider have been made. + /// public void UpdateTransform() { if (Rotation != _cachedRotation) @@ -84,20 +93,19 @@ public void UpdateTransform() { case ShapeType.Circle: var circle = (Circle)_shapes[i]; - // TODO: Add rotation. circle.Position = _rotatedOrigin + Position; break; case ShapeType.Polygon: var poly = (Polygon)_shapes[i]; - poly.Position = _rotatedOrigin + Position + _relativeShapePositions[i]; + poly.Position = _rotatedOrigin + Position; poly.Rotation = Rotation; - //poly.Origin = _relativeShapePositions[i]; poly.UpdateTransform(); break; } } } + public AABB GetBoundingBox() { // TODO: Add AABB caching? @@ -112,6 +120,11 @@ public AABB GetBoundingBox() } + /// + public bool InPool { get; set; } + + + /// public void OnReturnedToPool() { _shapes.Clear(); @@ -119,9 +132,10 @@ public void OnReturnedToPool() { ShapePool.Return(_shapes[i]); } - _relativeShapePositions.Clear(); } + + /// public void OnTakenFromPool() {} } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs b/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs index 850c87b..872ce06 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs @@ -1,9 +1,7 @@ using Microsoft.Xna.Framework; using Monofoxe.Engine.Collisions.Algorithms; -using Monofoxe.Engine.Utils; using System; using System.Collections.Generic; -using System.Linq; namespace Monofoxe.Engine.Collisions { @@ -16,7 +14,7 @@ public static Collider CreateCircle(float r) var circle = ShapePool.GetCircle(); circle.Radius = r; - collider.AddShape(Vector2.Zero, circle); + collider.AddShape(circle); return collider; } @@ -37,18 +35,23 @@ public static Collider CreateRectangle(Vector2 size) poly.Add(new Vector2( halfSize.X, halfSize.Y)); poly.Add(new Vector2(-halfSize.X, halfSize.Y)); - collider.AddShape(Vector2.Zero, poly); + collider.AddShape(poly); - var isit = GameMath.IsClockWise(poly.RelativeVertices.ToList()); - return collider; } public static Collider CreateLine(float length) { - throw new NotImplementedException(); + var collider = ColliderPool.GetCollider(); + var poly = ShapePool.GetPolygon(); + poly.Add(new Vector2(0, 0)); + poly.Add(new Vector2(length, 0)); + + collider.AddShape(poly); + + return collider; } @@ -79,7 +82,7 @@ public static Collider CreatePolygon(List vertices) poly.Add(polys[i][k]); } - collider.AddShape(Vector2.Zero, poly); + collider.AddShape(poly); } return collider; diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs b/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs index b810349..849ff0d 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs @@ -15,7 +15,5 @@ public static void Return(Collider collider) { _colliderPool.Return(collider); } - - } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs index 3085a2b..ea31b7c 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs @@ -1,4 +1,7 @@ -using Monofoxe.Engine.Collisions.Shapes; +using Monofoxe.Engine.Collisions.Algorithms; +using Monofoxe.Engine.Collisions.Shapes; +using Monofoxe.Engine.Utils; +using System.Runtime.CompilerServices; namespace Monofoxe.Engine.Collisions { @@ -10,7 +13,7 @@ public static bool CheckCollision(Collider a, Collider b) { for (var k = 0; k < b.ShapesCount; k += 1) { - if (ShapeCollisionChecker.CheckCollision(a.GetShape(i), b.GetShape(k))) + if (CheckCollision(a.GetShape(i), b.GetShape(k))) { return true; } @@ -19,16 +22,66 @@ public static bool CheckCollision(Collider a, Collider b) return false; } + public static bool CheckCollision(Collider a, IShape b) { for (var i = 0; i < a.ShapesCount; i += 1) { - if (ShapeCollisionChecker.CheckCollision(a.GetShape(i), b)) + if (CheckCollision(a.GetShape(i), b)) { return true; } } return false; } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool CheckCollision(IShape a, IShape b) + { + if (a.Type == ShapeType.Circle && b.Type == ShapeType.Circle) + { + // AABB check for this case is kinda redundant. + var circle1 = (Circle)a; + var circle2 = (Circle)b; + return SimpleCollisionDetection.CheckCircleCircle(circle1.Position, circle1.Radius, circle2.Position, circle2.Radius); + } + + var aabb1 = a.GetBoundingBox(); + var aabb2 = b.GetBoundingBox(); + if (!AABB.TestOverlap(ref aabb2, ref aabb1)) + { + return false; + } + + switch (a.Type) + { + case ShapeType.Circle: + switch (b.Type) + { + // No circle-circle case because it is handled in the beginning. + case ShapeType.Polygon: + var circle = (Circle)a; + var poly = (Polygon)b; + return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); + } + break; + case ShapeType.Polygon: + switch (b.Type) + { + case ShapeType.Circle: + var circle = (Circle)b; + var poly = (Polygon)a; + return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); + case ShapeType.Polygon: + var poly1 = (Polygon)a; + var poly2 = (Polygon)b; + return GJK.CheckPolyPoly(poly1.Vertices, poly1.Count, poly2.Vertices, poly2.Count); + } + break; + } + + return false; + } } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Settings.cs b/Monofoxe/Monofoxe.Engine/Collisions/CollisionSettings.cs similarity index 50% rename from Monofoxe/Monofoxe.Engine/Collisions/Settings.cs rename to Monofoxe/Monofoxe.Engine/Collisions/CollisionSettings.cs index f6f45fb..570ba5a 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Settings.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/CollisionSettings.cs @@ -1,19 +1,17 @@ namespace Monofoxe.Engine.Collisions { - public static class Settings + public static class CollisionSettings { - public const float MaxFloat = 3.402823466e+38f; public const float Epsilon = 1.192092896e-07f; - /// - /// The maximum number of vertices on a convex polygon. + /// The maximum number of vertices on a convex polygon. It is recommended to keep this number low for performance reasons. /// public static int MaxPolygonVertices = 8; /// - /// It is recommended to downscale your colliders and not make them too large, that's why collider sizes are measured in meters and not pixels. /// This value signifies how many pixels there are in one meter. + /// Making colliders too large will result in performance issues, that's why collider sizes are downscaled and measured in meters. /// public static float WorldScale { @@ -26,6 +24,10 @@ public static float WorldScale } private static float _worldScale = 64f; + /// + /// This value signifies how many meters there are in one pixel. + /// Making colliders too large will result in performance issues, that's why collider sizes are downscaled and measured in meters. + /// public static float OneOverWorldScale { get; private set; } = 1 / _worldScale; } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs b/Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs deleted file mode 100644 index 9e7af31..0000000 --- a/Monofoxe/Monofoxe.Engine/Collisions/ShapeCollisionChecker.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Monofoxe.Engine.Collisions.Algorithms; -using Monofoxe.Engine.Collisions.Shapes; -using Monofoxe.Engine.Utils; -using System.Runtime.CompilerServices; - -namespace Monofoxe.Engine.Collisions -{ - public static class ShapeCollisionChecker - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CheckCollision(IShape a, IShape b) - { - if (a.Type == ShapeType.Circle && b.Type == ShapeType.Circle) - { - // AABB check for this case is kinda redundant. - var circle1 = (Circle)a; - var circle2 = (Circle)b; - return SimpleCollisionDetection.CheckCircleCircle(circle1.Position, circle1.Radius, circle2.Position, circle2.Radius); - } - - var aabb1 = a.GetBoundingBox(); - var aabb2 = b.GetBoundingBox(); - if (!AABB.TestOverlap(ref aabb2, ref aabb1)) - { - return false; - } - - switch (a.Type) - { - case ShapeType.Circle: - switch (b.Type) - { - // No circle-circle case because it is handled in the beginning. - case ShapeType.Polygon: - var circle = (Circle)a; - var poly = (Polygon)b; - return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); - } - break; - case ShapeType.Polygon: - switch (b.Type) - { - case ShapeType.Circle: - var circle = (Circle)b; - var poly = (Polygon)a; - return GJK.CheckCirclePoly(circle.Position, circle.Radius, poly.Vertices, poly.Count); - case ShapeType.Polygon: - var poly1 = (Polygon)a; - var poly2 = (Polygon)b; - return GJK.CheckPolyPoly(poly1.Vertices, poly1.Count, poly2.Vertices, poly2.Count); - } - break; - } - - return false; - } - } -} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs index 7c77142..c691c9e 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Circle.cs @@ -1,32 +1,48 @@ using Microsoft.Xna.Framework; using Monofoxe.Engine.Utils; using Monofoxe.Engine.Utils.CustomCollections; -using System.Runtime.InteropServices; namespace Monofoxe.Engine.Collisions.Shapes { + /// + /// Defines a perfect circle with a positive radius. + /// public class Circle : IShape, IPoolable { public ShapeType Type => ShapeType.Circle; + /// + /// Radius of the circle, measured in meters. + /// public float Radius; + + /// + /// Absolute position of the shape, measured in meters. + /// public Vector2 Position; + /// + /// NOTE: It is recommended to use ShapePool to get new instances of this class. + /// + public Circle() { } + public AABB GetBoundingBox() { - var a = new AABB(Position - new Vector2(Radius, Radius), Position + new Vector2(Radius, Radius)); - return a; + return new AABB(Position - new Vector2(Radius, Radius), Position + new Vector2(Radius, Radius)); } + /// public bool InPool { get; set; } + /// public void OnTakenFromPool() {} - public void OnReturnedToPool() + /// + public void OnReturnedToPool() { Position = Vector2.Zero; - Radius = 0; + Radius = 0; } } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs index 9d76174..dcc872c 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/IShape.cs @@ -2,6 +2,9 @@ namespace Monofoxe.Engine.Collisions.Shapes { + /// + /// Defines a concave shape for the collision system to use. + /// public interface IShape { ShapeType Type { get; } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs index 658110e..3785a32 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/Polygon.cs @@ -6,30 +6,62 @@ namespace Monofoxe.Engine.Collisions.Shapes { + /// + /// Defines a convex polygon with clockwise winding (assuming the up vector is -1;0). Minimum possible number of vertices is 2. + /// public class Polygon : IShape, IPoolable { public ShapeType Type => ShapeType.Polygon; + /// + /// Absolute position of the shape, measured in meters. + /// NOTE: You should always call UpdateTransform() after changing this field. + /// public Vector2 Position; + + /// + /// Polygon rotation around the local 0;0 point, measured in degrees. + /// NOTE: You should always call UpdateTransform() after changing this field. + /// public float Rotation = 0; public float _cachedRotation = 0; private Vector2 _rotationCosSin = new Vector2(1, 0); /// - /// Polygon verts in relative space, before any transformation gets applied. + /// Polygon vertices in relative space, before any transformation is applied, measured in meters. + /// NOTE: This array is initialized with CollisionSettings.MaxPolygonVertices number of vertices and should always stay the same size. + /// NOTE: You should always call UpdateTransform() after changing vertices. + /// Use the Count field to keep track of the amount of vertices in a shape. + /// RESTRICTIONS: + /// - Vertices must form a convex shape. + /// - The number of vertices should not be higher than CollisionSettings.MaxPolygonVertices. + /// - Vertices must be arranged clockwise (assuming the up vector is 0;-1) /// - public readonly Vector2[] RelativeVertices = new Vector2[Settings.MaxPolygonVertices]; + public readonly Vector2[] RelativeVertices = new Vector2[CollisionSettings.MaxPolygonVertices]; /// - /// Polygon verts in absolute space. + /// Polygon vertices in absolute space, measured in meters. /// NOTE: These vertices should ONLY be generated from RelativeVertices by calling UpdateTransform() + /// NOTE: This array is initialized with CollisionSettings.MaxPolygonVertices number of vertices and should always stay the same size. + /// Use the Count field to keep track of the amount of vertices in a shape. + /// RESTRICTIONS: + /// - Vertices must form a convex shape. + /// - The number of vertices should not be higher than CollisionSettings.MaxPolygonVertices. + /// - Vertices must be arranged clockwise (assuming the up vector is 0;-1) /// - public readonly Vector2[] Vertices = new Vector2[Settings.MaxPolygonVertices]; + public readonly Vector2[] Vertices = new Vector2[CollisionSettings.MaxPolygonVertices]; + /// + /// Number of vertices in a shape. You should ALWAYS use this value instead of Vertices.Length, which always stays constant. + /// public int Count = 0; private const float _radianConversion = MathF.PI / 180f; + /// + /// NOTE: It is recommended to use ShapePool to get new instances of this class. + /// + public Polygon() { } private AABB _cachedAABB; private bool _cachedAABBStale = true; @@ -53,17 +85,24 @@ public AABB GetBoundingBox() } + /// + /// Adds a new vertex + /// public void Add(Vector2 v) { - if (Count >= Settings.MaxPolygonVertices) + if (Count >= CollisionSettings.MaxPolygonVertices) { - throw new InvalidOperationException("Cannot add another vertex! Maximum allowed number of vertices per polygon is " + Settings.MaxPolygonVertices + ". Consider splitting your polygon."); + throw new InvalidOperationException("Cannot add another vertex! Maximum allowed number of vertices per polygon is " + CollisionSettings.MaxPolygonVertices + ". Consider splitting your polygon."); } RelativeVertices[Count] = v; Count += 1; } + /// + /// Applies Position, Rotation to RelativeVertices and writes the result to Vertices. + /// Must be called after any changes to the shape have been made. + /// public void UpdateTransform() { _cachedAABBStale = true; @@ -100,13 +139,16 @@ private Vector2 RotateVertex(Vector2 p) ); } + /// public bool InPool { get; set; } - public void OnTakenFromPool() - { - } - public void OnReturnedToPool() + /// + public void OnTakenFromPool() {} + + + /// + public void OnReturnedToPool() { Count = 0; _cachedAABBStale = true; diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs index a304474..94cfe4a 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Shapes/ShapeType.cs @@ -2,7 +2,13 @@ { public enum ShapeType { + /// + /// Circle shape is represented by a single point and radius. + /// Circle, + /// + /// Polygon shape is represented by a set of points. + /// Polygon } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs b/Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs index 0570843..74de2cc 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/UnitConversionExtensions.cs @@ -8,48 +8,48 @@ public static class UnitConversionExtensions /// Converts pixels to meters which are used by the collision detection. /// public static Vector2 ToMeters(this Vector2 v) => - v * Settings.OneOverWorldScale; + v * CollisionSettings.OneOverWorldScale; /// /// Converts meters to pixels which are used for rendering and everything else. /// public static Vector2 ToPixels(this Vector2 v) => - v * Settings.WorldScale; + v * CollisionSettings.WorldScale; /// /// Converts pixels to meters which are used by the collision detection. /// public static float ToMeters(this int v) => - v * Settings.OneOverWorldScale; + v * CollisionSettings.OneOverWorldScale; /// /// Converts meters to pixels which are used for rendering and everything else. /// public static float ToPixels(this int v) => - v * Settings.WorldScale; + v * CollisionSettings.WorldScale; /// /// Converts pixels to meters which are used by the collision detection. /// public static float ToMeters(this float v) => - v * Settings.OneOverWorldScale; + v * CollisionSettings.OneOverWorldScale; /// /// Converts meters to pixels which are used for rendering and everything else. /// public static float ToPixels(this float v) => - v * Settings.WorldScale; + v * CollisionSettings.WorldScale; /// /// Converts pixels to meters which are used by the collision detection. /// public static double ToMeters(this double v) => - v * Settings.OneOverWorldScale; + v * CollisionSettings.OneOverWorldScale; /// /// Converts meters to pixels which are used for rendering and everything else. /// public static double ToPixels(this double v) => - v * Settings.WorldScale; + v * CollisionSettings.WorldScale; } } diff --git a/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs index 0a8beb4..b85a612 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/CustomCollections/IPoolable.cs @@ -2,9 +2,19 @@ { public interface IPoolable { + /// + /// Signifies that the object is currently in a pool. + /// bool InPool { get; set; } + /// + /// Called when the object is taken from a pool. + /// void OnTakenFromPool(); + + /// + /// Called when the object is returned to a pool. + /// void OnReturnedToPool(); } } diff --git a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs index 2530b4d..90b40b3 100644 --- a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs @@ -28,7 +28,7 @@ public class CollisionsDemo : Entity public CollisionsDemo(Layer layer) : base(layer) { - Settings.WorldScale = 16; + CollisionSettings.WorldScale = 16; _circle = ShapePool.GetCircle(); // All collider measuremenets must be converted to meters, which is basically 1 / WorldScale. @@ -83,7 +83,7 @@ public override void Update() _circle.Position = GameController.MainCamera.GetRelativeMousePosition().ToMeters(); - _circleAndSquarePolygonCollided = ShapeCollisionChecker.CheckCollision(_circle, _squarePolygon); + _circleAndSquarePolygonCollided = CollisionChecker.CheckCollision(_circle, _squarePolygon); _circleCollider1.Position = new Vector2( 100 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), From df26cf02080e15888950b4a4f664af9a74f8875c Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 02:08:51 +0100 Subject: [PATCH 15/22] Demo docs. --- .../Monofoxe.Samples/Demos/CollisionsDemo.cs | 109 ++++++++++-------- 1 file changed, 61 insertions(+), 48 deletions(-) diff --git a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs index 90b40b3..61a63af 100644 --- a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs @@ -1,14 +1,11 @@ using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using Monofoxe.Engine.Collisions; using Monofoxe.Engine; -using Monofoxe.Engine.Cameras; +using Monofoxe.Engine.Collisions; +using Monofoxe.Engine.Collisions.Shapes; using Monofoxe.Engine.Drawing; using Monofoxe.Engine.EC; -using Monofoxe.Engine.Resources; using Monofoxe.Engine.SceneSystem; using Monofoxe.Engine.Utils; -using Monofoxe.Engine.Collisions.Shapes; using System; using System.Collections.Generic; @@ -16,33 +13,57 @@ namespace Monofoxe.Samples.Demos { public class CollisionsDemo : Entity { + private Circle _circle; + private Polygon _polygon; + private bool _circleAndPolygonCollided; + + private Collider _circleCollider1; private Collider _circleCollider2; - - private Circle _circle; - private Polygon _squarePolygon; - private bool _circleAndSquarePolygonCollided; - private bool _collidersCollided; private Collider _polyCollider; private Collider _rectCollider; + private bool _collidersCollided; + public CollisionsDemo(Layer layer) : base(layer) { - CollisionSettings.WorldScale = 16; + CollisionSettings.WorldScale = 64; + + // Setting up shapes. ----------------------------------------- + + // Shapes are the most basic unit you can use for collision detection. + // However, they have more restrictions than colliders, so in most cases, it is recommended to just use those instead. + // Currently, there are only two types of shapes - circle and polygon. + + _circle = ShapePool.GetCircle(); // Instead of creating an instance directly, we take it from the shape pool. This prevents GC overhead if you require creating and destroying many shapes. - _circle = ShapePool.GetCircle(); - // All collider measuremenets must be converted to meters, which is basically 1 / WorldScale. + // All collision system measuremenets must be converted to meters, which is basically 1 / WorldScale. // This is done for performance reasons. Collision detection works best with smaller shapes. - _circle.Radius = 3.ToMeters(); + _circle.Radius = 32.ToMeters(); - _squarePolygon = ShapePool.GetPolygon(); + _polygon = ShapePool.GetPolygon(); // Note that we don't add vertices directly to the RelativeVertices array. // It is possible to do that, however, you also must increment Count by 1. - _squarePolygon.Add(new Vector2(-32, -32).ToMeters()); - _squarePolygon.Add(new Vector2(32, -32).ToMeters() * 3); - _squarePolygon.Add(new Vector2(32, 32).ToMeters()); - _squarePolygon.Add(new Vector2(-32, 32).ToMeters()); + // There are some restrictions to the polygon shape: + // - The vrtices should form a convex shape. + // - The polygon vertices should have clockwise winding. + // - Single polygon cannot have more than CollisionSettings.MaxPolygonVertices (which is 8 by default). + _polygon.Add(new Vector2(-32, -32).ToMeters()); + _polygon.Add(new Vector2(32, -32).ToMeters()); + _polygon.Add(new Vector2(32, 32).ToMeters()); + _polygon.Add(new Vector2(-32, 32).ToMeters()); + + _polygon.Position = new Vector2(200, 100).ToMeters(); + // Setting up shapes. ----------------------------------------- + + + + // Setting up colliders. ----------------------------------------- + + // Colliders are essentially collections of shapes that act a single shape. + // Because of that, they can be convex and have an unlimited number of vertices by internally splitting themselves into smaller convex parts. + // Colliders are created using ColliderFactory. It already has a bunch of predefined shapes. _circleCollider1 = ColliderFactory.CreateCircle(32.ToMeters()); _circleCollider2 = ColliderFactory.CreateCircle(64.ToMeters()); @@ -56,6 +77,7 @@ public CollisionsDemo(Layer layer) : base(layer) _polyCollider = ColliderFactory.CreatePolygon(verts); _rectCollider = ColliderFactory.CreateRectangle(new Vector2(32, 32).ToMeters()); _rectCollider.Origin = new Vector2(-16, -16).ToMeters(); + // Setting up colliders. ----------------------------------------- } @@ -64,27 +86,28 @@ public override void Update() { base.Update(); + // Updating colliders. ----------------------------------------- + _polygon.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _polygon.UpdateTransform(); // For polygon shapes, it is required to apply new transform after every change. + + _circle.Position = new Vector2( + 100 + 50 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), + 100 + ).ToMeters(); + + _circleAndPolygonCollided = CollisionChecker.CheckCollision(_circle, _polygon); + // Updating colliders. ----------------------------------------- + + + _rectCollider.Position = new Vector2(500, 400).ToMeters(); _rectCollider.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; _rectCollider.UpdateTransform(); - _squarePolygon.Position = new Vector2(200, 100).ToMeters(); - _squarePolygon.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; - _squarePolygon.UpdateTransform(); // Generating the correctly translated vertices. - _circleCollider1.Origin = new Vector2(32, 0).ToMeters(); _circleCollider2.Origin = new Vector2(0, -64).ToMeters(); _circleCollider1.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; - _circle.Position = new Vector2( - 100 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), - 100 - ).ToMeters(); - - _circle.Position = GameController.MainCamera.GetRelativeMousePosition().ToMeters(); - - _circleAndSquarePolygonCollided = CollisionChecker.CheckCollision(_circle, _squarePolygon); - _circleCollider1.Position = new Vector2( 100 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), 250 @@ -108,20 +131,20 @@ public override void Draw() { base.Draw(); - GraphicsMgr.CurrentColor = Color.White; - if (_circleAndSquarePolygonCollided) + GraphicsMgr.CurrentColor = Color.CornflowerBlue; + if (_circleAndPolygonCollided) { GraphicsMgr.CurrentColor = Color.Red; } DrawCircle(_circle); - DrawPolygon(_squarePolygon); + DrawPolygon(_polygon); DrawAABB(_circle); - DrawAABB(_squarePolygon); + DrawAABB(_polygon); - GraphicsMgr.CurrentColor = Color.White; + GraphicsMgr.CurrentColor = Color.DeepPink; if (_collidersCollided) { GraphicsMgr.CurrentColor = Color.Red; @@ -143,7 +166,7 @@ public override void Destroy() // Returning used shapes back to the pool. ShapePool.Return(_circle); - ShapePool.Return(_squarePolygon); + ShapePool.Return(_polygon); } @@ -177,11 +200,6 @@ private void DrawPolygon(Polygon poly) LineShape.Draw(poly.Vertices[i].ToPixels(), poly.Vertices[i + 1].ToPixels()); } LineShape.Draw(poly.Vertices[0].ToPixels(), poly.Vertices[poly.Count - 1].ToPixels()); - - for (var i = 0; i < poly.Count; i += 1) - { - CircleShape.Draw(poly.Vertices[i].ToPixels(), i * 2, false); - } } private void DrawAABB(IShape shape) @@ -189,11 +207,6 @@ private void DrawAABB(IShape shape) GraphicsMgr.CurrentColor = Color.Gray; var aabb = shape.GetBoundingBox(); RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), true); - GraphicsMgr.CurrentColor = Color.Red; - CircleShape.Draw(aabb.BottomRight.ToPixels(), 3, true); - - GraphicsMgr.CurrentColor = Color.Green; - CircleShape.Draw(aabb.TopLeft.ToPixels(), 3, true); } } From 229ce9ec0cf9e56f0c2c0ab41632aacb5b7e9d1f Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 19:22:45 +0100 Subject: [PATCH 16/22] Finalized collision system. --- CHANGELOG.md | 9 +- .../Collisions/ColliderFactory.cs | 85 ++++----- .../Collisions/ColliderPool.cs | 22 ++- .../Collisions/Colliders/CircleCollider.cs | 18 ++ .../Collisions/{ => Colliders}/Collider.cs | 20 +-- .../Collisions/Colliders/LineCollider.cs | 32 ++++ .../Collisions/Colliders/RectangleCollider.cs | 48 +++++ .../Collisions/CollisionChecker.cs | 1 + .../Collisions/CollisionSettings.cs | 1 + .../Monofoxe.Samples/Demos/CollisionsDemo.cs | 164 ++++++++++++++---- Samples/Monofoxe.Samples/SceneSwitcher.cs | 2 +- 11 files changed, 303 insertions(+), 99 deletions(-) create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Colliders/CircleCollider.cs rename Monofoxe/Monofoxe.Engine/Collisions/{ => Colliders}/Collider.cs (91%) create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs create mode 100644 Monofoxe/Monofoxe.Engine/Collisions/Colliders/RectangleCollider.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index b04fdbf..ce9c3ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,14 +8,19 @@ - Added `Clear()` method to `ResourceBox`. - Added `Offset()` method to linear dampers. - Added `OnFrameStart`, `OnFrameFinish` and `OnAfterDraw` events to `GraphicsMgr`. +- Added `Pool` collection. +- Added `AccumulationBuffer` collection. +- Added `UnorderedList` collection. +- Added `GetArea()`, `GetSignedArea()`, `IsClockwise()` methods to `GameMath`. +- Added new collision system. ### Changed -- **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` shoulw now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` +- **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` should now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` ### Fixed - Fixed `AddComponent<>()` not taking generic type into account. -- DirectoryResourceBox` now ignores non-xnb files properly. +- `DirectoryResourceBox` now ignores non-xnb files properly. ### Removed - **BREAKING CHANGE:** Removed camera layer filters. diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs b/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs index 872ce06..47333d3 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/ColliderFactory.cs @@ -1,17 +1,44 @@ using Microsoft.Xna.Framework; using Monofoxe.Engine.Collisions.Algorithms; -using System; +using Monofoxe.Engine.Collisions.Colliders; using System.Collections.Generic; namespace Monofoxe.Engine.Collisions { public static class ColliderFactory { - - public static Collider CreateCircle(float r) + /// + /// Creates a free-form polygon with clockwise winding. The polygon can be convex or concave and has no limit on the amount of vertices. + /// + public static Collider CreatePolygon(List vertices) { var collider = ColliderPool.GetCollider(); + var polys = BayazitDecomposer.ConvexPartition(vertices); + + for (var i = 0; i < polys.Count; i += 1) + { + var poly = ShapePool.GetPolygon(); + + for (var k = 0; k < polys[i].Count; k += 1) + { + poly.Add(polys[i][k]); + } + + collider.AddShape(poly); + } + + return collider; + } + + + /// + /// Creates a perfect circle. + /// + public static CircleCollider CreateCircle(float r) + { + var collider = ColliderPool.GetCircleCollider(); + var circle = ShapePool.GetCircle(); circle.Radius = r; collider.AddShape(circle); @@ -20,9 +47,15 @@ public static Collider CreateCircle(float r) } - public static Collider CreateRectangle(Vector2 size) + /// + /// Creates a rectangle with its center inthe middle, consisting of four vertices that form the following shape: + /// 0---1 + /// | \ | + /// 3---2 + /// + public static RectangleCollider CreateRectangle(Vector2 size) { - var collider = ColliderPool.GetCollider(); + var collider = ColliderPool.GetRectangleCollider(); var poly = ShapePool.GetPolygon(); var halfSize = size / 2; @@ -41,9 +74,13 @@ public static Collider CreateRectangle(Vector2 size) } - public static Collider CreateLine(float length) + /// + /// Creates a simple line that consists of two vertices that form the following shape with its center at vertex 0: + /// 0---1 + /// + public static LineCollider CreateLine(float length) { - var collider = ColliderPool.GetCollider(); + var collider = ColliderPool.GetLineCollider(); var poly = ShapePool.GetPolygon(); poly.Add(new Vector2(0, 0)); @@ -53,39 +90,5 @@ public static Collider CreateLine(float length) return collider; } - - - public static Collider CreateRing(float arc, float r, float thickness) - { - throw new NotImplementedException(); - } - - - public static Collider CreateSector(float arc, float r) - { - throw new NotImplementedException(); - } - - - public static Collider CreatePolygon(List vertices) - { - var collider = ColliderPool.GetCollider(); - - var polys = BayazitDecomposer.ConvexPartition(vertices); - - for (var i = 0; i < polys.Count; i += 1) - { - var poly = ShapePool.GetPolygon(); - - for (var k = 0; k < polys[i].Count; k += 1) - { - poly.Add(polys[i][k]); - } - - collider.AddShape(poly); - } - - return collider; - } } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs b/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs index 849ff0d..0161387 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/ColliderPool.cs @@ -1,19 +1,25 @@ -using Monofoxe.Engine.Utils.CustomCollections; +using Monofoxe.Engine.Collisions.Colliders; +using Monofoxe.Engine.Utils.CustomCollections; namespace Monofoxe.Engine.Collisions { public static class ColliderPool { private static Pool _colliderPool = new Pool(); - + private static Pool _circleColliderPool = new Pool(); + private static Pool _rectangleColliderPool = new Pool(); + private static Pool _lineColliderPool = new Pool(); + - public static Collider GetCollider() => - _colliderPool.Get(); + public static Collider GetCollider() => _colliderPool.Get(); + public static CircleCollider GetCircleCollider() => _circleColliderPool.Get(); + public static RectangleCollider GetRectangleCollider() => _rectangleColliderPool.Get(); + public static LineCollider GetLineCollider() => _lineColliderPool.Get(); - public static void Return(Collider collider) - { - _colliderPool.Return(collider); - } + public static void Return(Collider collider) => _colliderPool.Return(collider); + public static void Return(CircleCollider collider) => _circleColliderPool.Return(collider); + public static void Return(RectangleCollider collider) => _rectangleColliderPool.Return(collider); + public static void Return(LineCollider collider) => _lineColliderPool.Return(collider); } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Colliders/CircleCollider.cs b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/CircleCollider.cs new file mode 100644 index 0000000..b566c12 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/CircleCollider.cs @@ -0,0 +1,18 @@ +using Monofoxe.Engine.Collisions.Shapes; + +namespace Monofoxe.Engine.Collisions.Colliders +{ + /// + /// Represents a perfect circle. + /// + public class CircleCollider : Collider + { + private Circle _circle => (Circle)_shapes[0]; + + public float Radius + { + get => _circle.Radius; + set => _circle.Radius = value; + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/Collider.cs similarity index 91% rename from Monofoxe/Monofoxe.Engine/Collisions/Collider.cs rename to Monofoxe/Monofoxe.Engine/Collisions/Colliders/Collider.cs index f1a4cfa..db01f9c 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Collider.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/Collider.cs @@ -5,14 +5,14 @@ using System; using System.Collections.Generic; -namespace Monofoxe.Engine.Collisions +namespace Monofoxe.Engine.Collisions.Colliders { /// /// Defines a collection of convex shapes that act as a single shape. /// public class Collider : IPoolable { - private List _shapes = new List(); + protected List _shapes = new List(); /// @@ -45,7 +45,7 @@ public Collider() { } private const float _radianConversion = MathF.PI / 180f; - + public void AddShape(IShape shape) { _shapes.Add(shape); @@ -55,7 +55,7 @@ public void AddShape(IShape shape) public void RemoveShape(IShape shape) { var index = _shapes.IndexOf(shape); - if (index != -1) + if (index != -1) { _shapes.RemoveAt(index); } @@ -80,16 +80,16 @@ public void UpdateTransform() _rotationCosSin.Y = MathF.Sin(Rotation * _radianConversion); _cachedRotation = Rotation; } - + _rotatedOrigin = new Vector2( -Origin.X * _rotationCosSin.X + Origin.Y * _rotationCosSin.Y, -Origin.X * _rotationCosSin.Y - Origin.Y * _rotationCosSin.X ); - + for (var i = 0; i < _shapes.Count; i += 1) { - switch(_shapes[i].Type) + switch (_shapes[i].Type) { case ShapeType.Circle: var circle = (Circle)_shapes[i]; @@ -110,7 +110,7 @@ public AABB GetBoundingBox() { // TODO: Add AABB caching? var aabb = _shapes[0].GetBoundingBox(); - for(var i = 1; i < _shapes.Count; i += 1) + for (var i = 1; i < _shapes.Count; i += 1) { var aabb1 = _shapes[i].GetBoundingBox(); aabb.Combine(ref aabb1); @@ -128,7 +128,7 @@ public AABB GetBoundingBox() public void OnReturnedToPool() { _shapes.Clear(); - for(var i = 0; i < _shapes.Count; i += 1) + for (var i = 0; i < _shapes.Count; i += 1) { ShapePool.Return(_shapes[i]); } @@ -136,6 +136,6 @@ public void OnReturnedToPool() /// - public void OnTakenFromPool() {} + public void OnTakenFromPool() { } } } diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs new file mode 100644 index 0000000..d0a0b60 --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs @@ -0,0 +1,32 @@ +using Monofoxe.Engine.Collisions.Shapes; + +namespace Monofoxe.Engine.Collisions.Colliders +{ + /// + /// Represents a simple line that consists of two vertices that form the following shape with its center at vertex 0: + /// 0---1 + /// + /// NOTE: While it is possible to directly manipulate vertices of this collider, it is NOT recommended to do unless you really know what you are doing. Use base Collider instead. + /// + public class LineCollider : Collider + { + private Polygon _line => (Polygon)_shapes[0]; + + /// + /// Length of the line, measured in meters. + /// + public float Length + { + get + { + return (_line.RelativeVertices[1] - _line.RelativeVertices[0]).Length(); + } + + set + { + var e = (_line.RelativeVertices[1] - _line.RelativeVertices[0]).GetSafeNormalize(); + _line.RelativeVertices[1] = _line.RelativeVertices[0] + e * value; + } + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Colliders/RectangleCollider.cs b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/RectangleCollider.cs new file mode 100644 index 0000000..e6c2dad --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/RectangleCollider.cs @@ -0,0 +1,48 @@ +using Microsoft.Xna.Framework; +using Monofoxe.Engine.Collisions.Shapes; + +namespace Monofoxe.Engine.Collisions.Colliders +{ + /// + /// Creates a rectangle with its center inthe middle, consisting of four vertices that form the following shape: + /// 0---1 + /// | \ | + /// 3---2 + /// NOTE: While it is possible to directly manipulate vertices of this collider, it is NOT recommended to do unless you really know what you are doing. Use base Collider instead. + /// + public class RectangleCollider : Collider + { + private Polygon _rectangle => (Polygon)_shapes[0]; + + /// + /// Rectangle size measured in meters. + /// + public Vector2 Size + { + get + { + return new Vector2( + _rectangle.RelativeVertices[1].X - _rectangle.RelativeVertices[0].X, + _rectangle.RelativeVertices[3].Y - _rectangle.RelativeVertices[0].Y + ); + } + + set + { + var halfSize = value / 2; + + _rectangle.RelativeVertices[0].X = -halfSize.X; + _rectangle.RelativeVertices[0].Y = -halfSize.Y; + + _rectangle.RelativeVertices[1].X = halfSize.X; + _rectangle.RelativeVertices[1].Y = -halfSize.Y; + + _rectangle.RelativeVertices[2].X = halfSize.X; + _rectangle.RelativeVertices[2].Y = halfSize.Y; + + _rectangle.RelativeVertices[3].X = -halfSize.X; + _rectangle.RelativeVertices[3].Y = halfSize.Y; + } + } + } +} diff --git a/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs index ea31b7c..b9487f9 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/CollisionChecker.cs @@ -1,4 +1,5 @@ using Monofoxe.Engine.Collisions.Algorithms; +using Monofoxe.Engine.Collisions.Colliders; using Monofoxe.Engine.Collisions.Shapes; using Monofoxe.Engine.Utils; using System.Runtime.CompilerServices; diff --git a/Monofoxe/Monofoxe.Engine/Collisions/CollisionSettings.cs b/Monofoxe/Monofoxe.Engine/Collisions/CollisionSettings.cs index 570ba5a..21937dc 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/CollisionSettings.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/CollisionSettings.cs @@ -6,6 +6,7 @@ public static class CollisionSettings /// /// The maximum number of vertices on a convex polygon. It is recommended to keep this number low for performance reasons. + /// NOTE: Settign this value below 4 will result in collider logic breaking. /// public static int MaxPolygonVertices = 8; diff --git a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs index 61a63af..53fafb0 100644 --- a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs @@ -1,6 +1,7 @@ using Microsoft.Xna.Framework; using Monofoxe.Engine; using Monofoxe.Engine.Collisions; +using Monofoxe.Engine.Collisions.Colliders; using Monofoxe.Engine.Collisions.Shapes; using Monofoxe.Engine.Drawing; using Monofoxe.Engine.EC; @@ -18,12 +19,17 @@ public class CollisionsDemo : Entity private bool _circleAndPolygonCollided; - private Collider _circleCollider1; - private Collider _circleCollider2; + private CircleCollider _circleCollider1; + private CircleCollider _circleCollider2; private Collider _polyCollider; - private Collider _rectCollider; + private LineCollider _lineCollider; - private bool _collidersCollided; + private Collider _polyCollider2; + private RectangleCollider _rectCollider; + + private bool _circleCollidersCollided; + private bool _lineAndPolyCollidersCollided; + private bool _rectAndPolyCollidersCollided; public CollisionsDemo(Layer layer) : base(layer) { @@ -51,6 +57,7 @@ public CollisionsDemo(Layer layer) : base(layer) _polygon.Add(new Vector2(-32, -32).ToMeters()); _polygon.Add(new Vector2(32, -32).ToMeters()); _polygon.Add(new Vector2(32, 32).ToMeters()); + _polygon.Add(new Vector2(32, 32).ToMeters()); _polygon.Add(new Vector2(-32, 32).ToMeters()); _polygon.Position = new Vector2(200, 100).ToMeters(); @@ -63,67 +70,121 @@ public CollisionsDemo(Layer layer) : base(layer) // Colliders are essentially collections of shapes that act a single shape. // Because of that, they can be convex and have an unlimited number of vertices by internally splitting themselves into smaller convex parts. - // Colliders are created using ColliderFactory. It already has a bunch of predefined shapes. + // Colliders are created using ColliderFactory. It already has a bunch of predefined shapes. + // Note that all these colliders are taken from ColliderPool and should be returned to it afterwards. _circleCollider1 = ColliderFactory.CreateCircle(32.ToMeters()); - _circleCollider2 = ColliderFactory.CreateCircle(64.ToMeters()); + _circleCollider2 = ColliderFactory.CreateCircle(32.ToMeters()); - - var verts = new List(); - for(var i = 0; i < 8; i += 1) + _polyCollider = ColliderPool.GetCollider(); // Getting an empty collider directly form the pool. + for (var i = 1; i <= 360 / 8; i += 1) { - verts.Add(new Angle(360 / 8f * i).ToVector2() * (80 + MathF.Cos(i * 0.4f) * 6).ToMeters()); + var triangle = ShapePool.GetPolygon(); + triangle.Add(Vector2.Zero); + triangle.Add(new Angle(i * 8).ToVector2() * 80.ToMeters()); + triangle.Add(new Angle((i + 1) * 8).ToVector2() * 80.ToMeters()); + _polyCollider.AddShape(triangle); } + _lineCollider = ColliderFactory.CreateLine(50.ToMeters()); + //ColliderFactory.CreatePolygon(verts); + + + + var verts = new List(); + + // Colliders to not have restrictions to how many vertices they can have. + // They also can be concave - they will be partitioned into simple shapes. + verts.Add(new Vector2(0, 0).ToMeters()); + verts.Add(new Vector2(32, 0).ToMeters()); + verts.Add(new Vector2(32, 32).ToMeters()); + verts.Add(new Vector2(64, 32).ToMeters()); + verts.Add(new Vector2(64, 64).ToMeters()); + verts.Add(new Vector2(0, 64).ToMeters()); + + + _polyCollider2 = ColliderFactory.CreatePolygon(verts); - _polyCollider = ColliderFactory.CreatePolygon(verts); - _rectCollider = ColliderFactory.CreateRectangle(new Vector2(32, 32).ToMeters()); - _rectCollider.Origin = new Vector2(-16, -16).ToMeters(); + _rectCollider = ColliderFactory.CreateRectangle(new Vector2(64, 64).ToMeters()); // Setting up colliders. ----------------------------------------- } - + public override void Update() { base.Update(); - // Updating colliders. ----------------------------------------- + // Updating shapes. ----------------------------------------- _polygon.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; _polygon.UpdateTransform(); // For polygon shapes, it is required to apply new transform after every change. - + _circle.Position = new Vector2( - 100 + 50 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), + 100 + 50 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), 100 ).ToMeters(); - - _circleAndPolygonCollided = CollisionChecker.CheckCollision(_circle, _polygon); - // Updating colliders. ----------------------------------------- + _circleAndPolygonCollided = CollisionChecker.CheckCollision(_circle, _polygon); + // Updating shapes. ----------------------------------------- - _rectCollider.Position = new Vector2(500, 400).ToMeters(); - _rectCollider.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; - _rectCollider.UpdateTransform(); - _circleCollider1.Origin = new Vector2(32, 0).ToMeters(); - _circleCollider2.Origin = new Vector2(0, -64).ToMeters(); - _circleCollider1.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; - + // Updating circle colliders. ----------------------------------------- _circleCollider1.Position = new Vector2( - 100 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), + 150 + 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), 250 ).ToMeters(); + _circleCollider1.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _circleCollider1.Origin = new Vector2(32, 0).ToMeters(); _circleCollider1.UpdateTransform(); + _circleCollider2.Position = new Vector2( - 100 - 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), + 150 - 100 * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2), 250 ).ToMeters(); + _circleCollider2.Radius = (32 + MathF.Sin((float)GameMgr.ElapsedTimeTotal * 4) * 8).ToMeters(); _circleCollider2.UpdateTransform(); - _polyCollider.Position = new Vector2(500, 300).ToMeters(); - _polyCollider.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _circleCollidersCollided = CollisionChecker.CheckCollision(_circleCollider1, _circleCollider2); + // Updating circle colliders. ----------------------------------------- + + + + // Updating colliders. ----------------------------------------- + _polyCollider.Position = new Vector2(550, 150).ToMeters(); + + // You can morph vertices directly - however, make sure your geometry allows for transformations you want. + for (var i = 0; i < _polyCollider.ShapesCount; i += 1) + { + var shape = (Polygon)_polyCollider.GetShape(i); + for (var k = 0; k < shape.Count; k += 1) + { + var angle = new Angle(shape.RelativeVertices[k]).RadiansF; + var e = shape.RelativeVertices[k].GetSafeNormalize(); + shape.RelativeVertices[k] = e * (80 + (MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2) * 12) * MathF.Sin(angle * 8)).ToMeters(); + } + } _polyCollider.UpdateTransform(); - _collidersCollided = CollisionChecker.CheckCollision(_circleCollider1, _circleCollider2); + _lineCollider.Position = new Vector2(370 + 50, 150 - 8).ToMeters(); + _lineCollider.UpdateTransform(); + _lineCollider.Length = 48.ToMeters(); + + _lineAndPolyCollidersCollided = CollisionChecker.CheckCollision(_polyCollider, _lineCollider); + // Updating colliders. ----------------------------------------- + + + + // Updating colliders. ----------------------------------------- + _rectCollider.Position = new Vector2(100, 400).ToMeters(); + _rectCollider.Size = (Vector2.One * 64 + new Vector2(-8, 8) * MathF.Sin((float)GameMgr.ElapsedTimeTotal * 4)).ToMeters(); + _rectCollider.Origin = _rectCollider.Size * 0; + _rectCollider.UpdateTransform(); + + _polyCollider2.Position = new Vector2(200, 400).ToMeters(); + _polyCollider2.Rotation = (float)GameMgr.ElapsedTimeTotal * 32; + _polyCollider2.UpdateTransform(); + + _rectAndPolyCollidersCollided = CollisionChecker.CheckCollision(_polyCollider2, _rectCollider); + // Updating colliders. ----------------------------------------- } @@ -145,34 +206,57 @@ public override void Draw() GraphicsMgr.CurrentColor = Color.DeepPink; - if (_collidersCollided) + if (_circleCollidersCollided) { GraphicsMgr.CurrentColor = Color.Red; } DrawCollider(_circleCollider1); DrawCollider(_circleCollider2); + DrawAABB(_circleCollider1); + DrawAABB(_circleCollider2); - //GraphicsMgr.CurrentColor = Color.White; + GraphicsMgr.CurrentColor = Color.Cyan; + if (_lineAndPolyCollidersCollided) + { + GraphicsMgr.CurrentColor = Color.Red; + } DrawCollider(_polyCollider); + DrawCollider(_lineCollider); + DrawAABB(_polyCollider); + + GraphicsMgr.CurrentColor = Color.ForestGreen; + if (_rectAndPolyCollidersCollided) + { + GraphicsMgr.CurrentColor = Color.Red; + } + DrawCollider(_polyCollider2); DrawCollider(_rectCollider); + DrawAABB(_polyCollider2); } - + public override void Destroy() { base.Destroy(); - // Returning used shapes back to the pool. + // Returning used shapes and colliders back to the pool. ShapePool.Return(_circle); ShapePool.Return(_polygon); + + ColliderPool.Return(_circleCollider1); + ColliderPool.Return(_circleCollider2); + ColliderPool.Return(_polyCollider); + ColliderPool.Return(_lineCollider); + ColliderPool.Return(_polyCollider2); + ColliderPool.Return(_rectCollider); } private void DrawCollider(Collider collider) { - for(var i = 0; i < collider.ShapesCount; i += 1) + for (var i = 0; i < collider.ShapesCount; i += 1) { var shape = collider.GetShape(i); if (shape.Type == ShapeType.Circle) @@ -209,5 +293,11 @@ private void DrawAABB(IShape shape) RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), true); } + private void DrawAABB(Collider collider) + { + GraphicsMgr.CurrentColor = Color.Gray; + var aabb = collider.GetBoundingBox(); + RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), true); + } } } diff --git a/Samples/Monofoxe.Samples/SceneSwitcher.cs b/Samples/Monofoxe.Samples/SceneSwitcher.cs index 200d473..0ea6283 100644 --- a/Samples/Monofoxe.Samples/SceneSwitcher.cs +++ b/Samples/Monofoxe.Samples/SceneSwitcher.cs @@ -15,7 +15,6 @@ public class SceneSwitcher : Entity { public List Factories = new List { - new SceneFactory(typeof(CollisionsDemo)), new SceneFactory(typeof(ShapeDemo)), new SceneFactory(typeof(PrimitiveDemo), PrimitiveDemo.Description), new SceneFactory(typeof(SpriteDemo)), @@ -26,6 +25,7 @@ public class SceneSwitcher : Entity new SceneFactory(typeof(TiledDemo)), new SceneFactory(typeof(VertexBatchDemo)), new SceneFactory(typeof(CoroutinesDemo)), + new SceneFactory(typeof(CollisionsDemo)), }; public int CurrentSceneID {get; private set;} = 0; From a87189883f46cf2fea6d9a1445f68bcee5af09f6 Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 20:15:19 +0100 Subject: [PATCH 17/22] Renamed GetSafeNormalize() to SafeNormalize(). --- CHANGELOG.md | 1 + Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs | 2 +- Monofoxe/Monofoxe.Engine/Vector2Extensions.cs | 2 +- Monofoxe/Monofoxe.Engine/Vector3Extensions.cs | 2 +- Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs | 2 +- Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce9c3ef..ba74d0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### Changed - **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` should now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` +- **BREAKING CHANGE:** Renamed `GetSafeNormalize()` to `SafeNormalize()`. ### Fixed diff --git a/Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs index d0a0b60..d9ebd44 100644 --- a/Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs +++ b/Monofoxe/Monofoxe.Engine/Collisions/Colliders/LineCollider.cs @@ -24,7 +24,7 @@ public float Length set { - var e = (_line.RelativeVertices[1] - _line.RelativeVertices[0]).GetSafeNormalize(); + var e = (_line.RelativeVertices[1] - _line.RelativeVertices[0]).SafeNormalize(); _line.RelativeVertices[1] = _line.RelativeVertices[0] + e * value; } } diff --git a/Monofoxe/Monofoxe.Engine/Vector2Extensions.cs b/Monofoxe/Monofoxe.Engine/Vector2Extensions.cs index 40e4cb6..500a25f 100644 --- a/Monofoxe/Monofoxe.Engine/Vector2Extensions.cs +++ b/Monofoxe/Monofoxe.Engine/Vector2Extensions.cs @@ -35,7 +35,7 @@ public static Vector2 CeilingV(this Vector2 v) => /// Returns vector with the same direction and length of 1. /// If original vector is (0;0), returns zero vector. /// - public static Vector2 GetSafeNormalize(this Vector2 v) + public static Vector2 SafeNormalize(this Vector2 v) { if (v == Vector2.Zero) { diff --git a/Monofoxe/Monofoxe.Engine/Vector3Extensions.cs b/Monofoxe/Monofoxe.Engine/Vector3Extensions.cs index 5707bbe..308399a 100644 --- a/Monofoxe/Monofoxe.Engine/Vector3Extensions.cs +++ b/Monofoxe/Monofoxe.Engine/Vector3Extensions.cs @@ -40,7 +40,7 @@ public static Vector3 CeilingV(this Vector3 v) => /// Returns vector with the same direction and length of 1. /// If original vector is (0;0;0), returns zero vector. /// - public static Vector3 GetSafeNormalize(this Vector3 v) + public static Vector3 SafeNormalize(this Vector3 v) { if (v == Vector3.Zero) { diff --git a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs index 53fafb0..12cb52c 100644 --- a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs @@ -158,7 +158,7 @@ public override void Update() for (var k = 0; k < shape.Count; k += 1) { var angle = new Angle(shape.RelativeVertices[k]).RadiansF; - var e = shape.RelativeVertices[k].GetSafeNormalize(); + var e = shape.RelativeVertices[k].SafeNormalize(); shape.RelativeVertices[k] = e * (80 + (MathF.Sin((float)GameMgr.ElapsedTimeTotal * 2) * 12) * MathF.Sin(angle * 8)).ToMeters(); } } diff --git a/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs b/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs index f5b2241..e8aca58 100644 --- a/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs @@ -193,7 +193,7 @@ public override void Draw() var delta = _linestrip.Vertices[i - 1].Position - vertex.Position; if (delta.Length() > 8) { - var e = delta.GetSafeNormalize(); + var e = delta.SafeNormalize(); vertex.Position = _linestrip.Vertices[i - 1].Position - e * 8; _linestrip.Vertices[i] = vertex; } From 8c7071f08909b50c4ff67a2a61040da3ecf5e006 Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 20:16:23 +0100 Subject: [PATCH 18/22] Removed Drawable class and non-static shape fields and methods. --- CHANGELOG.md | 1 + .../Monofoxe.Engine/Drawing/CircleShape.cs | 19 +--------- Monofoxe/Monofoxe.Engine/Drawing/Drawable.cs | 20 ----------- Monofoxe/Monofoxe.Engine/Drawing/Frame.cs | 6 ++-- Monofoxe/Monofoxe.Engine/Drawing/LineShape.cs | 27 ++------------ .../Monofoxe.Engine/Drawing/Primitive2D.cs | 10 +++--- .../Monofoxe.Engine/Drawing/RectangleShape.cs | 31 ++-------------- Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs | 6 ++-- Monofoxe/Monofoxe.Engine/Drawing/Surface.cs | 6 ++-- Monofoxe/Monofoxe.Engine/Drawing/Text.cs | 6 ++-- .../Monofoxe.Engine/Drawing/ThickLineShape.cs | 34 ++---------------- .../Monofoxe.Engine/Drawing/TriangleShape.cs | 36 +------------------ Samples/Monofoxe.Samples/Demos/ShapeDemo.cs | 13 ++----- 13 files changed, 33 insertions(+), 182 deletions(-) delete mode 100644 Monofoxe/Monofoxe.Engine/Drawing/Drawable.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index ba74d0b..36cca5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ ### Removed - **BREAKING CHANGE:** Removed camera layer filters. +- **BREAKING CHANGE:** Removed `Drawable` class and non-static shape fields and methods. ## [v2.2.0] - *17.09.2022* diff --git a/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs index 0dfd815..737226a 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs @@ -7,30 +7,13 @@ namespace Monofoxe.Engine.Drawing /// /// Drawable circle shape. Can be drawn by using static methods or be instantiated. /// - public class CircleShape : Drawable + public static class CircleShape { - - public float Radius = 1; - - /// - /// If false, circle will be filled with solid color. If true, only outline will be drawn. - /// - public bool IsOutline = false; - - public Color Color = Color.White; - - public float ZDepth = 0; - static CircleShape() { CircleVerticesCount = 16; } - public override void Draw() => - Draw(Position, Radius, IsOutline, Color, ZDepth); - - - /// /// Amount of vertices in one circle. /// diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Drawable.cs b/Monofoxe/Monofoxe.Engine/Drawing/Drawable.cs deleted file mode 100644 index c2ca5e4..0000000 --- a/Monofoxe/Monofoxe.Engine/Drawing/Drawable.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.Xna.Framework; - -namespace Monofoxe.Engine.Drawing -{ - /// - /// Anything that can be drawn. - /// - public abstract class Drawable - { - /// - /// Drawable object's global position. - /// - public Vector2 Position; - - /// - /// Draws the object. - /// - public abstract void Draw(); - } -} diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs b/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs index efef8f2..9a76bdb 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs @@ -8,8 +8,10 @@ namespace Monofoxe.Engine.Drawing /// /// Drawable frame. /// - public class Frame : Drawable, ICloneable + public class Frame : ICloneable { + public Vector2 Position; + /// /// Texture atlas where frame is stored. /// @@ -67,7 +69,7 @@ public Frame(Texture2D texture, RectangleF texturePosition, Vector2 origin) Origin = origin; } - public override void Draw() => + public void Draw() => Draw(Position, Origin, Scale, Rotation, Color, ZDepth); diff --git a/Monofoxe/Monofoxe.Engine/Drawing/LineShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/LineShape.cs index 654152a..fcaa230 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/LineShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/LineShape.cs @@ -5,34 +5,13 @@ namespace Monofoxe.Engine.Drawing { /// - /// Drawable line shape. Can be drawn by using static methods or be instantiated. + /// Drawable line shape. /// NOTE: The line has no width. /// - public class LineShape : Drawable + public static class LineShape { - /// - /// First line point. - /// NOTE: all line points treat position as an origin point; - /// - public Vector2 Point1; - - /// - /// Second line point. - /// NOTE: all line points treat position as an origin point; - /// - public Vector2 Point2; - - public Color Color = Color.White; - - public float ZDepth = 0; - private static VertexPositionColorTexture[] _lineVertices = new VertexPositionColorTexture[2]; private static short[] _lineIndices = { 0, 1 }; - - - public override void Draw() => - Draw(Point1 + Position, Point2 + Position, Color, Color, ZDepth); - /// /// Draws a line. @@ -53,7 +32,5 @@ public static void Draw(Vector2 p1, Vector2 p2, Color c1, Color c2, float zDepth GraphicsMgr.VertexBatch.Texture = null; GraphicsMgr.VertexBatch.AddPrimitive(PrimitiveType.LineList, _lineVertices, _lineIndices); } - - } } diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Primitive2D.cs b/Monofoxe/Monofoxe.Engine/Drawing/Primitive2D.cs index b2e7bc6..f65a760 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Primitive2D.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Primitive2D.cs @@ -1,15 +1,15 @@ -using System.Collections.Generic; -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Monofoxe.Engine.Utils; namespace Monofoxe.Engine.Drawing { /// /// Base 2D primitive class. Can be used to create other types of primitives. /// - public abstract class Primitive2D : Drawable + public abstract class Primitive2D { + public Vector2 Position; + /// /// List of all primitive's vertices. /// NOTE: all vertices treat position as an origin point; @@ -103,7 +103,7 @@ protected VertexPositionColorTexture[] GetConvertedVertices() } - public override void Draw() + public void Draw() { GraphicsMgr.VertexBatch.Texture = _texture; GraphicsMgr.VertexBatch.AddPrimitive(_primitiveType, GetConvertedVertices(), GetIndices()); diff --git a/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs index 2dea335..ac56302 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs @@ -3,33 +3,8 @@ namespace Monofoxe.Engine.Drawing { - public class RectangleShape : Drawable - { - public Vector2 Size = Vector2.One; - - /// - /// If false, circle will be filled with solid color. If true, only outline will be drawn. - /// - public bool IsOutline = false; - - public Color Color = Color.White; - - public float ZDepth = 0; - - /// - /// Draws a rectangle using instance properties. - /// - public override void Draw() - { - Draw( - Position - Size / 2, Position + Size / 2, - IsOutline, - Color, Color, Color, Color, - ZDepth - ); - } - - + public static class RectangleShape + { private static VertexPositionColorTexture[] _rectangleVertices = new VertexPositionColorTexture[4]; private static short[] _filledRectangleIndices = { 0, 1, 3, 1, 2, 3 }; @@ -89,7 +64,5 @@ public static void DrawBySize(Vector2 p, Vector2 size, bool isOutline) => public static void DrawBySize(Vector2 p, Vector2 size, bool isOutline, Color c1, Color c2, Color c3, Color c4) => Draw(p - size / 2f, p + size / 2f, isOutline, c1, c2, c3, c4); - - } } diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs b/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs index 08f0c33..9bb53e7 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs @@ -8,8 +8,10 @@ namespace Monofoxe.Engine.Drawing /// /// Drawable sprite. /// - public class Sprite : Drawable, ICloneable + public class Sprite : ICloneable { + public Vector2 Position; + public string Name { get; internal set; } /// @@ -141,7 +143,7 @@ private Frame GetFrame(double animation) => _frames[Math.Max(0, Math.Min(_frames.Length - 1, (int)(animation * _frames.Length)))]; - public override void Draw() => + public void Draw() => GetFrame(Animation).Draw(Position, Origin, Scale, Rotation, Color, ZDepth); // Vectors. diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs b/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs index 6eb704d..c98d559 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs @@ -6,8 +6,10 @@ namespace Monofoxe.Engine.Drawing { - public class Surface : Drawable, IDisposable + public class Surface : IDisposable { + public Vector2 Position; + public Vector2 Scale = Vector2.One; public Vector2 Origin; @@ -79,7 +81,7 @@ private RenderTarget2D CreateRenderTarget(int w, int h) } - public override void Draw() => + public void Draw() => Draw(Position, Origin, Scale, Rotation, Color, ZDepth); diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Text.cs b/Monofoxe/Monofoxe.Engine/Drawing/Text.cs index 7f10f37..957cedc 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Text.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Text.cs @@ -4,8 +4,10 @@ namespace Monofoxe.Engine.Drawing { - public class Text : Drawable + public class Text { + public Vector2 Position; + public Vector2 Scale; public Vector2 Origin; @@ -35,7 +37,7 @@ public Text(string str, Vector2 position, Vector2 scale, Vector2 origin, Angle r // Text. - public override void Draw() + public void Draw() { var oldColor = GraphicsMgr.CurrentColor; GraphicsMgr.CurrentColor = Color; diff --git a/Monofoxe/Monofoxe.Engine/Drawing/ThickLineShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/ThickLineShape.cs index 1f3c61d..2e4877a 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/ThickLineShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/ThickLineShape.cs @@ -1,40 +1,13 @@ -using System.Collections.Generic; -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -using Monofoxe.Engine.Utils; namespace Monofoxe.Engine.Drawing { /// /// Drawable thick line shape. Can be drawn by using static methods or be instantiated. /// - public class ThickLineShape : Drawable + public class ThickLineShape { - /// - /// First line point. - /// NOTE: all line points treat position as an origin point; - /// - public Vector2 Point1; - - /// - /// Second line point. - /// NOTE: all line points treat position as an origin point; - /// - public Vector2 Point2; - - /// - /// Line thickness. - /// - public float Thickness = 1; - - public Color Color = Color.White; - - public float ZDepth = 0; - - public override void Draw() => - Draw(Point1 + Position, Point2 + Position, Thickness, Color, Color, ZDepth); - - private static VertexPositionColorTexture[] _thickLineVertices = new VertexPositionColorTexture[4]; private static readonly short[] _thickLineIndices = new short[]{0, 1, 3, 1, 2, 3}; @@ -52,7 +25,7 @@ public static void Draw(Vector2 pt1, Vector2 pt2, float thickness, Color c1, Col { var normal = (pt2 - pt1).Rotate90(); - normal = normal.GetSafeNormalize(); // The result is a unit vector rotated by 90 degrees. + normal = normal.SafeNormalize(); // The result is a unit vector rotated by 90 degrees. normal *= thickness / 2; _thickLineVertices[0].Position = new Vector3(pt1 - normal, zDepth); @@ -70,6 +43,5 @@ public static void Draw(Vector2 pt1, Vector2 pt2, float thickness, Color c1, Col GraphicsMgr.VertexBatch.Texture = null; GraphicsMgr.VertexBatch.AddPrimitive(PrimitiveType.TriangleList, _thickLineVertices, _thickLineIndices); } - } } diff --git a/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs index 555c443..3ca0328 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs @@ -7,40 +7,8 @@ namespace Monofoxe.Engine.Drawing /// /// Drawable triangle shape. Can be drawn by using static methods or be instantiated. /// - public class TriangleShape : Drawable + public static class TriangleShape { - /// - /// First triangle point. - /// NOTE: all triangle points treat position as an origin point; - /// - public Vector2 Point1; - - /// - /// Second triangle point. - /// NOTE: all triangle points treat position as an origin point; - /// - public Vector2 Point2; - - /// - /// Third triangle point. - /// NOTE: all triangle points treat position as an origin point; - /// - public Vector2 Point3; - - /// - /// If false, circle will be filled with solid color. If true, only outline will be drawn. - /// - public bool IsOutline = false; - - public Color Color = Color.White; - - public float ZDepth = 0; - - public override void Draw() => - Draw(Point1 + Position, Point2 + Position, Point3 + Position, IsOutline, Color, Color, Color, ZDepth); - - - private static VertexPositionColorTexture[] _triangleVertices = new VertexPositionColorTexture[4]; private static short[] _filledTriangleIndices = { 0, 1, 2 }; @@ -74,8 +42,6 @@ public static void Draw(Vector2 p1, Vector2 p2, Vector2 p3, bool isOutline, Colo { GraphicsMgr.VertexBatch.AddPrimitive(PrimitiveType.TriangleList, _triangleVertices, _filledTriangleIndices); } - } - } } diff --git a/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs b/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs index 6e615ee..a5d31b8 100644 --- a/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs @@ -13,16 +13,10 @@ public class ShapeDemo : Entity Color _mainColor = Color.White; HsvColor _secondaryColor = new HsvColor(new Color(34, 65, 250)); - TriangleShape _triangle; - + public ShapeDemo(Layer layer) : base(layer) { - // You can instantiate shapes instead of using static methods. - _triangle = new TriangleShape(); - _triangle.Point1 = new Vector2(32, 32); - _triangle.Point2 = new Vector2(-32, 32); - _triangle.Point3 = new Vector2(-32, -32); - _triangle.IsOutline = true; + } public override void Draw() @@ -108,9 +102,6 @@ public override void Draw() // Triangles. - _triangle.Position = position; - _triangle.Draw(); // Drawing an instantiated triangle. - GraphicsMgr.CurrentColor = _mainColor; TriangleShape.Draw( From 028e52e83f8c432bbd63bc5c52da93e0a91c3e4d Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 20:25:06 +0100 Subject: [PATCH 19/22] Fixed division by zero in dampers. --- CHANGELOG.md | 1 + Monofoxe/Monofoxe.Engine/Utils/AngleDamper.cs | 8 ++++++-- Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs | 11 ++++++----- Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs | 11 ++++++----- Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs | 11 ++++++----- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36cca5c..71081b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Fixed `AddComponent<>()` not taking generic type into account. - `DirectoryResourceBox` now ignores non-xnb files properly. +- Fixed division by zero in dampers. ### Removed - **BREAKING CHANGE:** Removed camera layer filters. diff --git a/Monofoxe/Monofoxe.Engine/Utils/AngleDamper.cs b/Monofoxe/Monofoxe.Engine/Utils/AngleDamper.cs index d6b389c..19185ff 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/AngleDamper.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/AngleDamper.cs @@ -142,8 +142,12 @@ public Angle Update(Angle value) var unboundValue = _oldValue + d; // Estimating velocity. - var dt = (float)Time.Time(); - var speed = d / dt; + float num = (float)Time.Time(); + var speed = 0.0; + if (num != 0) + { + speed = d / num; + } _oldValue = unboundValue; return Update(unboundValue, speed); diff --git a/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs b/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs index 4bd06a6..3866bf3 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/FloatDamper.cs @@ -132,12 +132,13 @@ private FloatDamper( public float Update(float value) { - var dt = (float)Time.Time(); - - // Estimating velocity. - var speed = (value - _oldValue) / dt; + float num = (float)Time.Time(); + var speed = 0f; + if (num != 0) + { + speed = (value - _oldValue) / num; + } _oldValue = value; - return Update(value, speed); } diff --git a/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs b/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs index db4edc4..f52b6c1 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/Vector2Damper.cs @@ -132,12 +132,13 @@ private Vector2Damper( public Vector2 Update(Vector2 value) { - var dt = (float)Time.Time(); - - // Estimating velocity. - var speed = (value - _oldValue) / dt; + float num = (float)Time.Time(); + var speed = Vector2.Zero; + if (num != 0) + { + speed = (value - _oldValue) / num; + } _oldValue = value; - return Update(value, speed); } diff --git a/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs b/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs index 18c1db4..19b0f8f 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/Vector3Damper.cs @@ -132,12 +132,13 @@ private Vector3Damper( public Vector3 Update(Vector3 value) { - var dt = (float)Time.Time(); - - // Estimating velocity. - var speed = (value - _oldValue) / dt; + float num = (float)Time.Time(); + var speed = Vector3.Zero; + if (num != 0) + { + speed = (value - _oldValue) / num; + } _oldValue = value; - return Update(value, speed); } From 962e019e91407c392d7b9cbbd0f55c73b8102ace Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 21:05:32 +0100 Subject: [PATCH 20/22] Changed boolean isOutline to ShapeFill enum for shapes. --- CHANGELOG.md | 5 ++ .../Monofoxe.Engine/Drawing/CircleShape.cs | 8 +- Monofoxe/Monofoxe.Engine/Drawing/Frame.cs | 15 +--- .../Monofoxe.Engine/Drawing/GraphicsMgr.cs | 18 ++--- .../Monofoxe.Engine/Drawing/RectangleShape.cs | 16 ++-- Monofoxe/Monofoxe.Engine/Drawing/ShapeFill.cs | 8 ++ Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs | 49 +++---------- Monofoxe/Monofoxe.Engine/Drawing/Surface.cs | 9 +++ .../Monofoxe.Engine/Drawing/TextureFont.cs | 8 +- .../Monofoxe.Engine/Drawing/TriangleShape.cs | 14 ++-- .../Utils/Tilemaps/BasicTilemap.cs | 2 +- Monofoxe/Monofoxe.Engine/WindowMgr.cs | 65 ++++------------- README.md | 8 +- .../Monofoxe.Samples/Demos/CollisionsDemo.cs | 8 +- .../Monofoxe.Samples/Demos/CoroutinesDemo.cs | 10 +-- Samples/Monofoxe.Samples/Demos/InputDemo.cs | 43 ++++++++--- .../Monofoxe.Samples/Demos/PrimitiveDemo.cs | 4 +- Samples/Monofoxe.Samples/Demos/ShapeDemo.cs | 26 +++---- Samples/Monofoxe.Samples/Demos/SpriteDemo.cs | 8 +- Samples/Monofoxe.Samples/Demos/UtilsDemo.cs | 73 ++++++++++++++----- Samples/Monofoxe.Samples/Misc/Ball.cs | 2 +- Samples/Monofoxe.Samples/SceneSwitcher.cs | 4 +- 22 files changed, 202 insertions(+), 201 deletions(-) create mode 100644 Monofoxe/Monofoxe.Engine/Drawing/ShapeFill.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 71081b5..e57fec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,14 @@ - Added `UnorderedList` collection. - Added `GetArea()`, `GetSignedArea()`, `IsClockwise()` methods to `GameMath`. - Added new collision system. +- Added new drawing methods. ### Changed + - **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` should now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` - **BREAKING CHANGE:** Renamed `GetSafeNormalize()` to `SafeNormalize()`. +- **BREAKING CHANGE:** Removed instances of `Width` and `Height` in `Sprite`, `Frame`, `WindowMgr`, and replaced them with `Size`. +- **BREAKING CHANGE:** Changed boolean `isOutline` to `ShapeFill` enum for shapes. ### Fixed @@ -25,6 +29,7 @@ - Fixed division by zero in dampers. ### Removed + - **BREAKING CHANGE:** Removed camera layer filters. - **BREAKING CHANGE:** Removed `Drawable` class and non-static shape fields and methods. diff --git a/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs index 737226a..3e4f8ab 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/CircleShape.cs @@ -52,14 +52,14 @@ public static int CircleVerticesCount /// /// Draws a circle. /// - public static void Draw(Vector2 p, float r, bool isOutline) => - Draw(p, r, isOutline, GraphicsMgr.CurrentColor); + public static void Draw(Vector2 p, float r, ShapeFill fill) => + Draw(p, r, fill, GraphicsMgr.CurrentColor); /// /// Draws a circle. /// - public static void Draw(Vector2 p, float r, bool isOutline, Color color, float zDepth = 0) + public static void Draw(Vector2 p, float r, ShapeFill fill, Color color, float zDepth = 0) { for(var i = 0; i < _circleVerticesCount; i += 1) @@ -72,7 +72,7 @@ public static void Draw(Vector2 p, float r, bool isOutline, Color color, float z _circleVertices[i].Color = color; } GraphicsMgr.VertexBatch.Texture = null; - if (isOutline) + if (fill == ShapeFill.Outline) { GraphicsMgr.VertexBatch.AddPrimitive(PrimitiveType.LineList, _circleVertices, _outlineCircleIndices); } diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs b/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs index 9a76bdb..d50316a 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Frame.cs @@ -23,16 +23,9 @@ public class Frame : ICloneable public readonly RectangleF TexturePosition; /// - /// Width of the frame. + /// Size of the frame. /// - public float Width => TexturePosition.Width; - - /// - /// Height of the frame. - /// - public float Height => TexturePosition.Height; - - + public Vector2 Size => TexturePosition.Size; public Vector2 Scale = Vector2.One; @@ -113,14 +106,14 @@ public void Draw(Vector2 position, Vector2 origin, Vector2 scale, Angle rotation { flipFlags = flipFlags | SpriteFlipFlags.FlipHorizontally; scale.X *= -1; - origin.X = Width - origin.X; + origin.X = Size.X - origin.X; } if (scale.Y < 0) { flipFlags = flipFlags | SpriteFlipFlags.FlipVertically; scale.Y *= -1; - origin.Y = Height - origin.Y; + origin.Y = Size.Y - origin.Y; } // Proper negative scaling. diff --git a/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs b/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs index c049e48..b1b566b 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/GraphicsMgr.cs @@ -102,8 +102,8 @@ public static void Update(GameTime gameTime) { CanvasMatrix = Matrix.CreateScale( new Vector3( - windowManager.PreferredBackBufferWidth / (float)windowManager.CanvasWidth, - windowManager.PreferredBackBufferHeight / (float)windowManager.CanvasHeight, + windowManager.PreferredBackBufferWidth / windowManager.CanvasSize.X, + windowManager.PreferredBackBufferHeight / windowManager.CanvasSize.Y, 1 ) ); @@ -113,26 +113,22 @@ public static void Update(GameTime gameTime) // Scales display to match canvas, but keeps aspect ratio. if (windowManager.CanvasMode == CanvasMode.KeepAspectRatio) { - var backbufferSize = new Vector2( - windowManager.PreferredBackBufferWidth, - windowManager.PreferredBackBufferHeight - ); float ratio, offsetX = 0, offsetY = 0; float backbufferRatio = windowManager.PreferredBackBufferWidth / (float)windowManager.PreferredBackBufferHeight; - float canvasRatio = windowManager.CanvasWidth / (float)windowManager.CanvasHeight; + float canvasRatio = windowManager.CanvasSize.X / windowManager.CanvasSize.Y; if (canvasRatio > backbufferRatio) { - ratio = windowManager.PreferredBackBufferWidth / (float)windowManager.CanvasWidth; - offsetY = (windowManager.PreferredBackBufferHeight - (windowManager.CanvasHeight * ratio)) / 2f; + ratio = windowManager.PreferredBackBufferWidth / windowManager.CanvasSize.X; + offsetY = (windowManager.PreferredBackBufferHeight - (windowManager.CanvasSize.Y * ratio)) / 2f; } else { - ratio = windowManager.PreferredBackBufferHeight / (float)windowManager.CanvasHeight; - offsetX = (windowManager.PreferredBackBufferWidth - (windowManager.CanvasWidth * ratio)) / 2f; + ratio = windowManager.PreferredBackBufferHeight / windowManager.CanvasSize.Y; + offsetX = (windowManager.PreferredBackBufferWidth - (windowManager.CanvasSize.X * ratio)) / 2f; } CanvasMatrix = Matrix.CreateScale(new Vector3(ratio, ratio, 1)) * Matrix.CreateTranslation(new Vector3(offsetX, offsetY, 0)); diff --git a/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs index ac56302..7bebd0f 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/RectangleShape.cs @@ -14,8 +14,8 @@ public static class RectangleShape /// /// Draws a rectangle using top left and bottom right point. /// - public static void Draw(Vector2 p1, Vector2 p2, bool isOutline) => - Draw(p1, p2, isOutline, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor); + public static void Draw(Vector2 p1, Vector2 p2, ShapeFill fill) => + Draw(p1, p2, fill, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor); /// /// Draws a rectangle using top left and bottom right point with specified colors for each corner. @@ -23,7 +23,7 @@ public static void Draw(Vector2 p1, Vector2 p2, bool isOutline) => public static void Draw( Vector2 p1, Vector2 p2, - bool isOutline, + ShapeFill fill, Color c1, Color c2, Color c3, @@ -41,7 +41,7 @@ public static void Draw( _rectangleVertices[3].Color = c4; GraphicsMgr.VertexBatch.Texture = null; - if (isOutline) + if (fill == ShapeFill.Outline) { GraphicsMgr.VertexBatch.AddPrimitive(PrimitiveType.LineList, _rectangleVertices, _outlineRectangleIndices); } @@ -55,14 +55,14 @@ public static void Draw( /// /// Draws a rectangle using center point and size. /// - public static void DrawBySize(Vector2 p, Vector2 size, bool isOutline) => - Draw(p - size / 2, p + size / 2f, isOutline, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor); + public static void DrawBySize(Vector2 p, Vector2 size, ShapeFill fill) => + Draw(p - size / 2, p + size / 2f, fill, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor); /// /// Draws a rectangle using center point and size with specified colors for each corner. /// - public static void DrawBySize(Vector2 p, Vector2 size, bool isOutline, Color c1, Color c2, Color c3, Color c4) => - Draw(p - size / 2f, p + size / 2f, isOutline, c1, c2, c3, c4); + public static void DrawBySize(Vector2 p, Vector2 size, ShapeFill fill, Color c1, Color c2, Color c3, Color c4) => + Draw(p - size / 2f, p + size / 2f, fill, c1, c2, c3, c4); } } diff --git a/Monofoxe/Monofoxe.Engine/Drawing/ShapeFill.cs b/Monofoxe/Monofoxe.Engine/Drawing/ShapeFill.cs new file mode 100644 index 0000000..9f04bfe --- /dev/null +++ b/Monofoxe/Monofoxe.Engine/Drawing/ShapeFill.cs @@ -0,0 +1,8 @@ +namespace Monofoxe.Engine.Drawing +{ + public enum ShapeFill + { + Solid, + Outline + } +} diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs b/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs index 9bb53e7..79f329f 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Sprite.cs @@ -22,33 +22,17 @@ public class Sprite : ICloneable /// /// Sprite width. Can be accessed only if all sprite frames have the same size. /// - public float Width + public Vector2 Size { get { if (SingleFrameSize) { - return _frames[0].Width; + return _frames[0].Size; } throw new Exception("To use this variable, all frame sizes must be identical!"); } } - - /// - /// Sprite height. Can be accessed only if all sprite frames have the same size. - /// - public float Height - { - get - { - if (SingleFrameSize) - { - return _frames[0].Height; - } - throw new Exception("To use this variable, all frame sizes must be identical!"); - } - } - public Vector2 Scale = Vector2.One; @@ -81,21 +65,6 @@ public Frame this[int id] get => _frames[id]; } - - public Sprite(Frame[] frames, int originX, int originY, string name = "") - { - _frames = new Frame[frames.Length]; - foreach(var frame in frames) - { - frame.ParentSprite = this; - } - Array.Copy(frames, _frames, frames.Length); - Origin = new Vector2(originX, originY); - - SingleFrameSize = CheckIdenticalFrameSizes(); - Name = name; - } - public Sprite(Frame[] frames, Vector2 origin, string name = "") { _frames = new Frame[frames.Length]; @@ -109,11 +78,11 @@ public Sprite(Frame[] frames, Vector2 origin, string name = "") Name = name; } - public Sprite(Frame frame, int originX, int originY, string name = "") + public Sprite(Frame frame, Vector2 origin, string name = "") { _frames = new Frame[]{frame}; frame.ParentSprite = this; - Origin = new Vector2(originX, originY); + Origin = origin; SingleFrameSize = true; Name = name; @@ -127,7 +96,7 @@ private bool CheckIdenticalFrameSizes() { for(var i = 1; i < _frames.Length; i += 1) { - if (_frames[0].Width != _frames[i].Width || _frames[0].Height != _frames[i].Height) + if (_frames[0].Size.X != _frames[i].Size.X || _frames[0].Size.Y != _frames[i].Size.Y) { return false; } @@ -151,9 +120,15 @@ public void Draw() => public void Draw(Vector2 position) => _frames[0].Draw(position, Origin, Scale, Rotation, Color, ZDepth); + public void Draw(Vector2 position, Vector2 scale, Angle rotation) => + _frames[0].Draw(position, Origin, scale, rotation, Color, ZDepth); + public void Draw(Vector2 position, double animation) => GetFrame(animation).Draw(position, Origin, Scale, Rotation, Color, ZDepth); - + + public void Draw(Vector2 position, double animation, Vector2 scale, Angle rotation, Color color) => + GetFrame(animation).Draw(position, Origin, scale, rotation, color, ZDepth); + public void Draw(Vector2 position, double animation, Vector2 origin, Vector2 scale, Angle rotation, Color color) => GetFrame(animation).Draw(position, origin, scale, rotation, color, ZDepth); diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs b/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs index c98d559..d7421f6 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs @@ -100,6 +100,15 @@ Color color ) => Draw(position, origin, scale, rotation, color, ZDepth); + + public void Draw( + Vector2 position, + Vector2 scale, + Angle rotation + ) => + Draw(position, Origin, scale, rotation, Color, ZDepth); + + public void Draw( Vector2 position, Vector2 origin, diff --git a/Monofoxe/Monofoxe.Engine/Drawing/TextureFont.cs b/Monofoxe/Monofoxe.Engine/Drawing/TextureFont.cs index f8e6ca6..fff6d29 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/TextureFont.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/TextureFont.cs @@ -101,7 +101,7 @@ public TextureFont(Sprite sprite, int spacing, int lineSpacing, string character { if (data[frame.Texture.Width * y + x].A != 0) { - rightBearing = (int)(frame.Width - (x + frame.Origin.X - frame.TexturePosition.X + 1)); + rightBearing = (int)(frame.Size.X - (x + frame.Origin.X - frame.TexturePosition.X + 1)); break; } } @@ -118,8 +118,8 @@ public TextureFont(Sprite sprite, int spacing, int lineSpacing, string character { Character = ch, BoundsInTexture = frame.TexturePosition.ToRectangle(), - Width = frame.Width - leftBearing - rightBearing, - WidthIncludingBearings = frame.Width, + Width = frame.Size.X - leftBearing - rightBearing, + WidthIncludingBearings = frame.Size.X, LeftSideBearing = leftBearing, RightSideBearing = rightBearing }; @@ -197,7 +197,7 @@ public float MeasureStringHeight(string text) { string[] lines = text.Split(new []{Environment.NewLine}, StringSplitOptions.None); - return lines.Length * (_frames[(char)DefaultCharacter].Height + LineSpacing) - LineSpacing; + return lines.Length * (_frames[(char)DefaultCharacter].Size.Y + LineSpacing) - LineSpacing; } diff --git a/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs b/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs index 3ca0328..ab61540 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/TriangleShape.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace Monofoxe.Engine.Drawing @@ -18,13 +17,14 @@ public static class TriangleShape /// /// Draws a triangle. /// - public static void Draw(Vector2 p1, Vector2 p2, Vector2 p3, bool isOutline) => - Draw(p1, p2, p3, isOutline, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor); - + public static void Draw(Vector2 p1, Vector2 p2, Vector2 p3, ShapeFill fill, float zDepth = 0) => + Draw(p1, p2, p3, fill, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, GraphicsMgr.CurrentColor, zDepth); + + /// /// Draws a triangle with specified colors. /// - public static void Draw(Vector2 p1, Vector2 p2, Vector2 p3, bool isOutline, Color c1, Color c2, Color c3, float zDepth = 0) + public static void Draw(Vector2 p1, Vector2 p2, Vector2 p3, ShapeFill fill, Color c1, Color c2, Color c3, float zDepth = 0) { _triangleVertices[0].Position = new Vector3(p1.X, p1.Y, zDepth); _triangleVertices[0].Color = c1; @@ -34,7 +34,7 @@ public static void Draw(Vector2 p1, Vector2 p2, Vector2 p3, bool isOutline, Colo _triangleVertices[2].Color = c3; GraphicsMgr.VertexBatch.Texture = null; - if (isOutline) + if (fill == ShapeFill.Outline) { GraphicsMgr.VertexBatch.AddPrimitive(PrimitiveType.LineList, _triangleVertices, _outlineTriangleIndices); } diff --git a/Monofoxe/Monofoxe.Engine/Utils/Tilemaps/BasicTilemap.cs b/Monofoxe/Monofoxe.Engine/Utils/Tilemaps/BasicTilemap.cs index 799716c..e895f5a 100644 --- a/Monofoxe/Monofoxe.Engine/Utils/Tilemaps/BasicTilemap.cs +++ b/Monofoxe/Monofoxe.Engine/Utils/Tilemaps/BasicTilemap.cs @@ -128,7 +128,7 @@ public override void Draw() if (tilesetTile != null) { var flip = SpriteFlipFlags.None; - var offset = Vector2.UnitY * (tilesetTile.Frame.Height - TileHeight); + var offset = Vector2.UnitY * (tilesetTile.Frame.Size.Y - TileHeight); var rotation = 0; // A bunch of Tiled magic. diff --git a/Monofoxe/Monofoxe.Engine/WindowMgr.cs b/Monofoxe/Monofoxe.Engine/WindowMgr.cs index 382c58d..5af4a85 100644 --- a/Monofoxe/Monofoxe.Engine/WindowMgr.cs +++ b/Monofoxe/Monofoxe.Engine/WindowMgr.cs @@ -12,54 +12,11 @@ public class WindowMgr : GraphicsDeviceManager #region Window properties. - /// - /// Width of the screen. - /// - public int ScreenWidth => GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width; - - /// - /// Height of the screen. - /// - public int ScreenHeight => GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height; - /// /// Size of the screen. /// - public Vector2 ScreenSize => new Vector2(ScreenWidth, ScreenHeight); - - /// - /// Window width. - /// - public int CanvasWidth - { - get => _canvasWidth; - set - { - _canvasWidth = value; - if (!IsFullScreen) - { - PreferredBackBufferWidth = _canvasWidth; - } - } - } - private int _canvasWidth; + public Vector2 ScreenSize => new Vector2(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height); - /// - /// Window height. - /// - public int CanvasHeight - { - get => _canvasHeight; - set - { - _canvasHeight = value; - if (!IsFullScreen) - { - PreferredBackBufferHeight = _canvasHeight; - } - } - } - private int _canvasHeight; /// /// Tells how canvas will be drawn on the backbuffer. @@ -71,13 +28,20 @@ public int CanvasHeight /// public Vector2 CanvasSize { - get => new Vector2(CanvasWidth, CanvasHeight); + get => new Vector2(_canvasWidth, _canvasHeight); set { - CanvasWidth = (int)value.X; - CanvasHeight = (int)value.Y; + _canvasWidth = (int)value.X; + _canvasHeight = (int)value.Y; + if (!IsFullScreen) + { + PreferredBackBufferWidth = _canvasWidth; + PreferredBackBufferHeight = _canvasHeight; + } } } + private int _canvasWidth; + private int _canvasHeight; @@ -128,8 +92,8 @@ public void SetFullScreen(bool fullscreen) { if (fullscreen) { - PreferredBackBufferWidth = ScreenWidth; - PreferredBackBufferHeight = ScreenHeight; + PreferredBackBufferWidth = (int)ScreenSize.X; + PreferredBackBufferHeight = (int)ScreenSize.Y; } else { @@ -151,8 +115,5 @@ public void SetFullScreen(bool fullscreen) /// public void CenterWindow() => WindowPosision = ((ScreenSize - CanvasSize) / 2).ToPoint(); - - - } } diff --git a/README.md b/README.md index fd271ea..bcbdfef 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![logo](logo/logo_transparent.png) +![logo](logo/logo_transparent.png) # What am I looking at? This is Monofoxe - a game engine based on the [Monogame Framework](http://monogame.net). @@ -36,13 +36,13 @@ Everything Monogame does, plus: * Useful math for collisions and other game stuff. * Timers, alarms, cameras, state machines, tilemaps, foxes! * Coroutines. -* FMOD audio support (As a standalone [library](https://github.com/Martenfur/ChaiFoxes.FMODAudio/)). +* FMOD audio support (As a standalone [library](https://github.com/Martenfur/FmodForFoxes/)). * Hybrid EC. * Scene system (with layers!). * Tiled maps support. * Enhanced content management via [Nopipeline](https://github.com/Martenfur/Nopipeline). * Hot reload (VS2022 and Rider only) - +* Lightweight collision detection. Coming in the future: @@ -70,4 +70,4 @@ That's the spirit. Check out if I need any help on my [Quire board](https://quir - [MirrorOfSun](https://github.com/MirrorOfSUn) - [Shazan](https://bitbucket.org/%7B07c29368-d971-4ab1-8ec5-1a89d56bfa43%7D/) -*don't forget to pet your foxes* \ No newline at end of file +*don't forget to pet your foxes* diff --git a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs index 12cb52c..76e4535 100644 --- a/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/CollisionsDemo.cs @@ -268,13 +268,13 @@ private void DrawCollider(Collider collider) DrawPolygon((Polygon)shape); } } - CircleShape.Draw(collider.Position.ToPixels(), 4, true); + CircleShape.Draw(collider.Position.ToPixels(), 4, ShapeFill.Outline); } private void DrawCircle(Circle circle) { - CircleShape.Draw(circle.Position.ToPixels(), circle.Radius.ToPixels(), true); + CircleShape.Draw(circle.Position.ToPixels(), circle.Radius.ToPixels(), ShapeFill.Outline); } private void DrawPolygon(Polygon poly) @@ -290,14 +290,14 @@ private void DrawAABB(IShape shape) { GraphicsMgr.CurrentColor = Color.Gray; var aabb = shape.GetBoundingBox(); - RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), true); + RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), ShapeFill.Outline); } private void DrawAABB(Collider collider) { GraphicsMgr.CurrentColor = Color.Gray; var aabb = collider.GetBoundingBox(); - RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), true); + RectangleShape.Draw(aabb.BottomRight.ToPixels(), aabb.TopLeft.ToPixels(), ShapeFill.Outline); } } } diff --git a/Samples/Monofoxe.Samples/Demos/CoroutinesDemo.cs b/Samples/Monofoxe.Samples/Demos/CoroutinesDemo.cs index e67f7c2..5cfc9b1 100644 --- a/Samples/Monofoxe.Samples/Demos/CoroutinesDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/CoroutinesDemo.cs @@ -228,12 +228,12 @@ private void DrawClocks() { var clockSize = 32; - CircleShape.Draw(_basicUpdateClockPosition, 32, true); + CircleShape.Draw(_basicUpdateClockPosition, 32, ShapeFill.Outline); LineShape.Draw( _basicUpdateClockPosition, _basicUpdateClockPosition + _basicUpdateClock.ToVector2() * clockSize ); - CircleShape.Draw(_fixedUpdateClockPosition, 32, true); + CircleShape.Draw(_fixedUpdateClockPosition, 32, ShapeFill.Outline); LineShape.Draw( _fixedUpdateClockPosition, _fixedUpdateClockPosition + _fixedUpdateClock.ToVector2() * clockSize @@ -246,11 +246,11 @@ private void DrawSequence() GraphicsMgr.CurrentColor = _sequenceColor; if (_sequenceStage == 1) { - CircleShape.Draw(_sequencePosition, 32, false); + CircleShape.Draw(_sequencePosition, 32, ShapeFill.Solid); } if (_sequenceStage == 2) { - RectangleShape.DrawBySize(_sequencePosition, Vector2.One * 32, false); + RectangleShape.DrawBySize(_sequencePosition, Vector2.One * 32, ShapeFill.Solid); } if (_sequenceStage == 3) { @@ -258,7 +258,7 @@ private void DrawSequence() _sequencePosition - Vector2.UnitY * 32, _sequencePosition + Vector2.UnitX * 32, _sequencePosition - Vector2.UnitX * 32, - false + ShapeFill.Solid ); } } diff --git a/Samples/Monofoxe.Samples/Demos/InputDemo.cs b/Samples/Monofoxe.Samples/Demos/InputDemo.cs index 914a7a9..12d84cc 100644 --- a/Samples/Monofoxe.Samples/Demos/InputDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/InputDemo.cs @@ -89,11 +89,11 @@ public override void Draw() // This position only accounts for screen transformation. // When the camera will move, it will offset. - CircleShape.Draw(Input.ScreenMousePosition, 8, true); + CircleShape.Draw(Input.ScreenMousePosition, 8, ShapeFill.Outline); // You can also get mouse position from any camera. // This method can be used in Update, when no camera is active. - CircleShape.Draw(GraphicsMgr.CurrentCamera.GetRelativeMousePosition(), 12, true); + CircleShape.Draw(GraphicsMgr.CurrentCamera.GetRelativeMousePosition(), 12, ShapeFill.Outline); Text.CurrentFont = ResourceHub.GetResource("Fonts", "Arial"); @@ -103,11 +103,32 @@ public override void Draw() // Gamepad, mouse and keyboard buttons are using the same method. position += Vector2.UnitY * 64; - CircleShape.Draw(position, 16, Input.CheckButton(KeyboardTestButton)); + if (Input.CheckButton(KeyboardTestButton)) + { + CircleShape.Draw(position, 16, ShapeFill.Solid); + } + else + { + CircleShape.Draw(position, 16, ShapeFill.Outline); + } position += Vector2.UnitX * 64; - CircleShape.Draw(position, 16, Input.CheckButton(GamepadTestButton)); + if (Input.CheckButton(GamepadTestButton)) + { + CircleShape.Draw(position, 16, ShapeFill.Solid); + } + else + { + CircleShape.Draw(position, 16, ShapeFill.Outline); + } position += Vector2.UnitX * 64; - CircleShape.Draw(position, 16, Input.CheckButton(MouseTestButton)); + if (Input.CheckButton(MouseTestButton)) + { + CircleShape.Draw(position, 16, ShapeFill.Solid); + } + else + { + CircleShape.Draw(position, 16, ShapeFill.Outline); + } position = new Vector2(200, 200); @@ -124,18 +145,18 @@ public override void Draw() // Sticks. position += Vector2.UnitY * 96; - CircleShape.Draw(position, 64, true); - CircleShape.Draw(position + Input.GamepadGetLeftStick(0) * 64 * new Vector2(1, -1), 16, false); + CircleShape.Draw(position, 64, ShapeFill.Outline); + CircleShape.Draw(position + Input.GamepadGetLeftStick(0) * 64 * new Vector2(1, -1), 16, ShapeFill.Solid); position += Vector2.UnitX * (128 + 64); - CircleShape.Draw(position, 64, true); - CircleShape.Draw(position + Input.GamepadGetRightStick(0) * 64 * new Vector2(1, -1), 16, false); + CircleShape.Draw(position, 64, ShapeFill.Outline); + CircleShape.Draw(position + Input.GamepadGetRightStick(0) * 64 * new Vector2(1, -1), 16, ShapeFill.Solid); // Triggers. position -= Vector2.UnitX * (64 + 16); - RectangleShape.DrawBySize(position + Vector2.UnitY * Input.GamepadGetRightTrigger(0) * 64, Vector2.One * 8, false); + RectangleShape.DrawBySize(position + Vector2.UnitY * Input.GamepadGetRightTrigger(0) * 64, Vector2.One * 8, ShapeFill.Solid); LineShape.Draw(position, position + Vector2.UnitY * 64); position -= Vector2.UnitX * 32; - RectangleShape.DrawBySize(position + Vector2.UnitY * Input.GamepadGetLeftTrigger(0) * 64, Vector2.One * 8, false); + RectangleShape.DrawBySize(position + Vector2.UnitY * Input.GamepadGetLeftTrigger(0) * 64, Vector2.One * 8, ShapeFill.Solid); LineShape.Draw(position, position + Vector2.UnitY * 64); } diff --git a/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs b/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs index e8aca58..a9d82ed 100644 --- a/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/PrimitiveDemo.cs @@ -152,8 +152,8 @@ public override void Draw() _mesh.Position = position; var cell = new Vector2( - _autismCatSprite.Width / (float)_mesh.Width, - _autismCatSprite.Height / (float)_mesh.Height + _autismCatSprite.Size.X / _mesh.Width, + _autismCatSprite.Size.Y / _mesh.Height ) * _meshRepeat; var c = 0; for(var k = 0; k < _mesh.Height; k += 1) diff --git a/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs b/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs index a5d31b8..49cdf4d 100644 --- a/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/ShapeDemo.cs @@ -38,10 +38,10 @@ public override void Draw() // Circles. GraphicsMgr.CurrentColor = _mainColor; // Setting current color. It's active for all shapes and sprites. - CircleShape.Draw(position, 24, false); // Filled circle. + CircleShape.Draw(position, 24, ShapeFill.Solid); // Filled circle. GraphicsMgr.CurrentColor = _secondaryColor.ToColor(); - CircleShape.Draw(position, 32, true); // Outline. + CircleShape.Draw(position, 32, ShapeFill.Outline); // Outline. position += Vector2.UnitX * spacing; @@ -50,10 +50,10 @@ public override void Draw() CircleShape.CircleVerticesCount = 8; // Changing the amount of circle vertices. GraphicsMgr.CurrentColor = _mainColor; - CircleShape.Draw(position, 24, false); + CircleShape.Draw(position, 24, ShapeFill.Solid); GraphicsMgr.CurrentColor = _secondaryColor.ToColor(); - CircleShape.Draw(position, 32, true); + CircleShape.Draw(position, 32, ShapeFill.Outline); CircleShape.CircleVerticesCount = 32; // Circles. @@ -66,18 +66,18 @@ public override void Draw() // Rectangles. // You can draw rectangle using its top left and bottom right point... - RectangleShape.Draw(position - Vector2.One * 24, position + Vector2.One * 24, false); + RectangleShape.Draw(position - Vector2.One * 24, position + Vector2.One * 24, ShapeFill.Solid); GraphicsMgr.CurrentColor = _mainColor; // ...or its center position and size! - RectangleShape.DrawBySize(position, Vector2.One * 64, true); + RectangleShape.DrawBySize(position, Vector2.One * 64, ShapeFill.Outline); position += Vector2.UnitX * spacing; RectangleShape.Draw( // We can also manually set colors for each vertex. position - Vector2.One * 24, - position + Vector2.One * 24, - false, + position + Vector2.One * 24, + ShapeFill.Solid, _mainColor, _mainColor, _mainColor, @@ -86,8 +86,8 @@ public override void Draw() RectangleShape.DrawBySize( position, - Vector2.One * 64, - true, + Vector2.One * 64, + ShapeFill.Outline, _mainColor, _secondaryColor.ToColor(), _mainColor, @@ -107,13 +107,13 @@ public override void Draw() TriangleShape.Draw( position + new Vector2(-24, -24), position + new Vector2(24, -24), - position + new Vector2(24, 24), - false + position + new Vector2(24, 24), + ShapeFill.Solid ); // Be aware of culling. This triangle, for example, will be culled. // You can disable culling, if you don't want to deal with it. - TriangleShape.Draw(new Vector2(-24, -24), new Vector2(24, 24), new Vector2(24, -24), false); + TriangleShape.Draw(new Vector2(-24, -24), new Vector2(24, 24), new Vector2(24, -24), ShapeFill.Solid); // Triangles. diff --git a/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs b/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs index 16d32ce..6aa64df 100644 --- a/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs @@ -83,7 +83,7 @@ public override void Draw() _fireSprite.Draw( position, 0.4f, - new Vector2(_fireSprite.Width, _fireSprite.Height) / 2, + _fireSprite.Size / 2, new Vector2(1, 2) * (float)Math.Sin(_animation * Math.PI * 2 * 2), new Angle(360 * _animation), Color.Red @@ -132,7 +132,7 @@ public override void Draw() _batch.End(); // After you're done, you can draw anything you like without switching graphics mode again. - RectangleShape.Draw(position, position + new Vector2(texture.Width, texture.Height), true); + RectangleShape.Draw(position, position + new Vector2(texture.Width, texture.Height), ShapeFill.Outline); position += Vector2.UnitX * 512; @@ -141,7 +141,7 @@ public override void Draw() Surface.SetTarget(_surface); var po = new Vector2(_surface.Width, _surface.Height) / 2 + new Angle(GameMgr.ElapsedTimeTotal * 10).ToVector2() * 64; - RectangleShape.DrawBySize(po, Vector2.One * 8, false); + RectangleShape.DrawBySize(po, Vector2.One * 8, ShapeFill.Solid); Surface.ResetTarget(); @@ -170,7 +170,7 @@ void InitSurface() GraphicsMgr.Device.Clear(_secondaryColor); GraphicsMgr.CurrentColor = _mainColor; - CircleShape.Draw(new Vector2(64, 64), 64, false); + CircleShape.Draw(new Vector2(64, 64), 64, ShapeFill.Solid); Surface.ResetTarget(); } diff --git a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs index 7544856..f2925ca 100644 --- a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs @@ -21,14 +21,14 @@ public enum TestStates public class UtilsDemo : Entity { - + Color _mainColor = Color.White; Color _secondaryColor = Color.Violet; double _animationSpeed = 1; - + Animation _fireAnimation; - + TimeKeeper _slowTimeKeeper; Alarm _autoAlarm; @@ -112,7 +112,7 @@ public UtilsDemo(Layer layer) : base(layer) // State machine. // State machines are very useful for animation and complex logic. _stateMachine = new StateMachine(TestStates.Green, this); - + // Filling up the state machine with events. _stateMachine.AddState(TestStates.Green, Green, GreenEnter, GreenExit); _stateMachine.AddState(TestStates.Blue, Blue); @@ -151,7 +151,7 @@ public void AlarmTrigger(Alarm alarm) // This demo shows comparison of Stop and Loop modes. // You will notice that circles will start blinking out of sync // over time. - alarm.Start(); + alarm.Start(); _slowAlarmSwitch = !_slowAlarmSwitch; } @@ -211,7 +211,7 @@ public override void Update() base.Update(); // All of those are not entities, so they have to be updated manually. - + // It needs to be updated automatically. _fireAnimation.Update(); @@ -250,32 +250,65 @@ public override void Draw() var spacing = 100; GraphicsMgr.CurrentColor = Color.White; - + _fireSprite.Draw(position, _fireAnimation.Progress, Vector2.Zero, Vector2.One, Angle.Right, Color.White); - + GraphicsMgr.CurrentColor = Color.SeaGreen; position += Vector2.UnitX * spacing; - CircleShape.Draw(position, 8, _autoAlarmSwitch); - + if (_autoAlarmSwitch) + { + CircleShape.Draw(position, 8, ShapeFill.Outline); + } + else + { + CircleShape.Draw(position, 8, ShapeFill.Solid); + } GraphicsMgr.CurrentColor = Color.Sienna; position += Vector2.UnitX * 32; - CircleShape.Draw(position, 8, _slowAlarmSwitch); - + if (_slowAlarmSwitch) + { + CircleShape.Draw(position, 8, ShapeFill.Outline); + } + else + { + CircleShape.Draw(position, 8, ShapeFill.Solid); + } GraphicsMgr.CurrentColor = Color.Thistle; position += Vector2.UnitX * 32; - CircleShape.Draw(position, 8, _counterSwitch); + if (_counterSwitch) + { + CircleShape.Draw(position, 8, ShapeFill.Outline); + } + else + { + CircleShape.Draw(position, 8, ShapeFill.Solid); + } position += Vector2.UnitX * 32; GraphicsMgr.CurrentColor = _color; if (_isRectangle) { - RectangleShape.DrawBySize(position, Vector2.One * 16, _isOutline); + if (_isOutline) + { + RectangleShape.DrawBySize(position, Vector2.One * 16, ShapeFill.Outline); + } + else + { + RectangleShape.DrawBySize(position, Vector2.One * 16, ShapeFill.Solid); + } } else { - CircleShape.Draw(position, 8, _isOutline); + if (_isOutline) + { + CircleShape.Draw(position, 8, ShapeFill.Outline); + } + else + { + CircleShape.Draw(position, 8, ShapeFill.Solid); + } } @@ -285,7 +318,7 @@ public override void Draw() private FloatDamper _floatDamper1 = new FloatDamper(0, 1, 1, 0); private FloatDamper _floatDamper2 = new FloatDamper(0, 1, 1f, 5); private FloatDamper _floatDamper3 = new FloatDamper(0, 1, 0.2f, -2); - + private Vector2Damper _positionDamper = new Vector2Damper(Vector2.Zero, 5, 0.2f, -2); private AngleDamper _angleDamper = new AngleDamper(Angle.Right, 1, 0.2f, -2); private Vector2 _baseAnglePos = new Vector2(200, 350); @@ -304,11 +337,11 @@ private void UpdateDampers() private void DrawDampers() { GraphicsMgr.CurrentColor = Color.White; - CircleShape.Draw(new Vector2(_floatDamper1.Value, 200), 8, false); - CircleShape.Draw(new Vector2(_floatDamper2.Value, 230), 8, false); - CircleShape.Draw(new Vector2(_floatDamper3.Value, 260), 8, false); + CircleShape.Draw(new Vector2(_floatDamper1.Value, 200), 8, ShapeFill.Solid); + CircleShape.Draw(new Vector2(_floatDamper2.Value, 230), 8, ShapeFill.Solid); + CircleShape.Draw(new Vector2(_floatDamper3.Value, 260), 8, ShapeFill.Solid); - CircleShape.Draw(_positionDamper.Value, 8, false); + CircleShape.Draw(_positionDamper.Value, 8, ShapeFill.Solid); LineShape.Draw(_baseAnglePos, _baseAnglePos + _angleDamper.Value.ToVector2() * 50); } diff --git a/Samples/Monofoxe.Samples/Misc/Ball.cs b/Samples/Monofoxe.Samples/Misc/Ball.cs index 05525ff..65fe13d 100644 --- a/Samples/Monofoxe.Samples/Misc/Ball.cs +++ b/Samples/Monofoxe.Samples/Misc/Ball.cs @@ -46,7 +46,7 @@ public override void Draw() { base.Draw(); GraphicsMgr.CurrentColor = _color; - CircleShape.Draw(_position, _r, false); + CircleShape.Draw(_position, _r, ShapeFill.Solid); } } diff --git a/Samples/Monofoxe.Samples/SceneSwitcher.cs b/Samples/Monofoxe.Samples/SceneSwitcher.cs index 0ea6283..14e9bde 100644 --- a/Samples/Monofoxe.Samples/SceneSwitcher.cs +++ b/Samples/Monofoxe.Samples/SceneSwitcher.cs @@ -100,7 +100,7 @@ public override void Draw() var textSize = Text.CurrentFont.MeasureString(CurrentFactory.Description); var origin = Vector2.UnitX * (canvasSize - (textSize + Vector2.One * padding * 2)); GraphicsMgr.CurrentColor = _barColor; - RectangleShape.Draw(origin, origin + textSize + Vector2.One * padding * 2, false); + RectangleShape.Draw(origin, origin + textSize + Vector2.One * padding * 2, ShapeFill.Solid); GraphicsMgr.CurrentColor = _textColor; Text.Draw(CurrentFactory.Description, Vector2.One * padding + origin); } @@ -113,7 +113,7 @@ public override void Draw() Matrix.CreateTranslation(new Vector3(0, canvasSize.Y - _barHeight, 0)) * GraphicsMgr.VertexBatch.View; GraphicsMgr.CurrentColor = _barColor; - RectangleShape.Draw(Vector2.Zero, canvasSize, false); + RectangleShape.Draw(Vector2.Zero, canvasSize, ShapeFill.Solid); GraphicsMgr.CurrentColor = _textColor; Text.Draw( From 54212296a2e0d35a034bd4396fa8cc38dfeacec1 Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 21:47:34 +0100 Subject: [PATCH 21/22] Finalized the release. --- CHANGELOG.md | 2 +- Installer/packInstaller.nsi | 4 +-- Monofoxe/Monofoxe.Engine/Cameras/Camera.cs | 22 ++++++++-------- Monofoxe/Monofoxe.Engine/Cameras/Camera2D.cs | 2 +- Monofoxe/Monofoxe.Engine/Drawing/Surface.cs | 25 +++++++++---------- PackRelease.ps1 | 1 + Packages.props | 2 +- Samples/Monofoxe.Samples/Demos/SpriteDemo.cs | 4 +-- Samples/Monofoxe.Samples/Demos/UtilsDemo.cs | 2 +- Samples/Monofoxe.Samples/GameController.cs | 2 +- .../Crossplatform/Library/GameController.cs | 10 ++++---- 11 files changed, 38 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e57fec1..6352857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ - **BREAKING CHANGE:** `ResourceInfoMgr` now accepts wildcards instead of directory names. For example, `ResourceInfoMgr.GetResourcePaths("Graphics/Trees");` should now be replaced with `ResourceInfoMgr.GetResourcePaths("Graphics/Trees/*");` - **BREAKING CHANGE:** Renamed `GetSafeNormalize()` to `SafeNormalize()`. -- **BREAKING CHANGE:** Removed instances of `Width` and `Height` in `Sprite`, `Frame`, `WindowMgr`, and replaced them with `Size`. +- **BREAKING CHANGE:** Removed instances of `Width` and `Height` in `Sprite`, `Frame`, `WindowMgr`, `Camera`, `Surface`, and replaced them with `Size`. - **BREAKING CHANGE:** Changed boolean `isOutline` to `ShapeFill` enum for shapes. ### Fixed diff --git a/Installer/packInstaller.nsi b/Installer/packInstaller.nsi index db35f02..5f29adf 100644 --- a/Installer/packInstaller.nsi +++ b/Installer/packInstaller.nsi @@ -3,8 +3,8 @@ !define APPNAME "Monofoxe" -!define APPVERSION "v2" -!define INSTALLERVERSION "2.2.0.0" +!define APPVERSION "v3-dev" +!define INSTALLERVERSION "3.0.0.0-dev.1" !define MUI_ICON "pics\icon.ico" !define MUI_UNICON "pics\icon.ico" diff --git a/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs b/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs index a845a30..6c87a86 100644 --- a/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs +++ b/Monofoxe/Monofoxe.Engine/Cameras/Camera.cs @@ -41,7 +41,7 @@ public int Priority /// /// View size. /// - public Vector2 Size => new Vector2(Surface.Width, Surface.Height); + public Vector2 Size => Surface.Size; /// /// Camera offset. @@ -150,7 +150,7 @@ public PostprocessingMode PostprocessingMode if (_postprocessingMode != PostprocessingMode.None) { - _postprocessorBuffer = new Surface(Surface.Width, Surface.Height); + _postprocessorBuffer = new Surface(Surface.Size); } else { @@ -160,7 +160,7 @@ public PostprocessingMode PostprocessingMode if (_postprocessingMode == PostprocessingMode.CameraAndLayers) { - _postprocessorLayerBuffer = new Surface(Surface.Width, Surface.Height); + _postprocessorLayerBuffer = new Surface(Surface.Size); } else { @@ -177,24 +177,24 @@ public PostprocessingMode PostprocessingMode internal Surface _postprocessorLayerBuffer; - public Camera(int w, int h, int priority = 0) + public Camera(Vector2 size, int priority = 0) { - Surface = new Surface(w, h); + Surface = new Surface(size); Priority = priority; // Also adds camera to camera list. } - public event Action OnResize; + public event Action OnResize; /// /// Resizes the view. /// - public void Resize(int w, int h) + public void Resize(Vector2 size) { - Surface.Resize(w, h); - _postprocessorBuffer?.Resize(w, h); - _postprocessorLayerBuffer?.Resize(w, h); - OnResize?.Invoke(w, h); + Surface.Resize(size); + _postprocessorBuffer?.Resize(size); + _postprocessorLayerBuffer?.Resize(size); + OnResize?.Invoke(size); } /// diff --git a/Monofoxe/Monofoxe.Engine/Cameras/Camera2D.cs b/Monofoxe/Monofoxe.Engine/Cameras/Camera2D.cs index 3bf9cf2..5f84d20 100644 --- a/Monofoxe/Monofoxe.Engine/Cameras/Camera2D.cs +++ b/Monofoxe/Monofoxe.Engine/Cameras/Camera2D.cs @@ -16,7 +16,7 @@ public Vector2 Position2D } - public Camera2D(int w, int h, int priority = 0) : base(w, h, priority) + public Camera2D(Vector2 size, int priority = 0) : base(size, priority) { } diff --git a/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs b/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs index d7421f6..db686b5 100644 --- a/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs +++ b/Monofoxe/Monofoxe.Engine/Drawing/Surface.cs @@ -22,8 +22,7 @@ public class Surface : IDisposable public RenderTarget2D RenderTarget {get; private set;} - public int Width => RenderTarget.Width; - public int Height => RenderTarget.Height; + public Vector2 Size => new Vector2(RenderTarget.Width, RenderTarget.Height); internal static bool SurfaceStackEmpty => _surfaceStack.Count == 0; @@ -35,21 +34,21 @@ public class Surface : IDisposable private static Surface _currentSurface; - public Surface(int w, int h, Vector2 position, Vector2 scale, Vector2 origin, Angle rotation) + public Surface(Vector2 size, Vector2 position, Vector2 scale, Vector2 origin, Angle rotation) { Position = position; Scale = scale; Origin = origin; Rotation = rotation; - RenderTarget = CreateRenderTarget(w, h); + RenderTarget = CreateRenderTarget(size); Color = GraphicsMgr.CurrentColor; } - public Surface(int w, int h) + public Surface(Vector2 size) { - RenderTarget = CreateRenderTarget(w, h); + RenderTarget = CreateRenderTarget(size); Color = GraphicsMgr.CurrentColor; } @@ -59,18 +58,18 @@ public Surface(RenderTarget2D renderTarget) Color = GraphicsMgr.CurrentColor; } - public void Resize(int w, int h) + public void Resize(Vector2 size) { RenderTarget.Dispose(); - RenderTarget = CreateRenderTarget(w, h); + RenderTarget = CreateRenderTarget(size); } - private RenderTarget2D CreateRenderTarget(int w, int h) + private RenderTarget2D CreateRenderTarget(Vector2 size) { return new RenderTarget2D( GraphicsMgr.Device, - w, h, + (int)size.X, (int)size.Y, false, GraphicsMgr.Device.PresentationParameters.BackBufferFormat, GraphicsMgr.Device.PresentationParameters.DepthStencilFormat, @@ -127,14 +126,14 @@ Vector4 zDepth { mirroring = mirroring | SpriteFlipFlags.FlipHorizontally; scale.X *= -1; - scaleOffset.X = Width; + scaleOffset.X = Size.X; } if (scale.Y < 0) { mirroring = mirroring | SpriteFlipFlags.FlipVertically; scale.Y *= -1; - scaleOffset.Y = Height; + scaleOffset.Y = Size.Y; } // Proper negative scaling. @@ -221,7 +220,7 @@ public static void SetTarget(Surface surf) => /// Sets surface as a render target. /// public static void SetTarget(Surface surf, Matrix view) => - SetTarget(surf, view, Matrix.CreateOrthographicOffCenter(0, surf.Width, surf.Height, 0, 0, 1)); + SetTarget(surf, view, Matrix.CreateOrthographicOffCenter(0, surf.Size.X, surf.Size.Y, 0, 0, 1)); /// diff --git a/PackRelease.ps1 b/PackRelease.ps1 index 54cacf0..511c173 100644 --- a/PackRelease.ps1 +++ b/PackRelease.ps1 @@ -39,6 +39,7 @@ $crossplatform = "Crossplatform" "Compiling shaders..." dotnet tool install -g dotnet-mgfxc +dotnet tool update -g dotnet-mgfxc mgfxc Monofoxe/Resources/AlphaBlend.fx Monofoxe/Resources/AlphaBlend_dx.mgfxo /Profile:DirectX_11 mgfxc Monofoxe/Resources/AlphaBlend.fx Monofoxe/Resources/AlphaBlend_gl.mgfxo /Profile:OpenGL diff --git a/Packages.props b/Packages.props index fb15554..355746b 100644 --- a/Packages.props +++ b/Packages.props @@ -1,6 +1,6 @@ - 2.2.0 + 3.0.0-dev.1 2.2.0 \ No newline at end of file diff --git a/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs b/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs index 6aa64df..44a05f5 100644 --- a/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/SpriteDemo.cs @@ -140,7 +140,7 @@ public override void Draw() GraphicsMgr.CurrentColor = Color.Red; Surface.SetTarget(_surface); - var po = new Vector2(_surface.Width, _surface.Height) / 2 + new Angle(GameMgr.ElapsedTimeTotal * 10).ToVector2() * 64; + var po = _surface.Size / 2 + new Angle(GameMgr.ElapsedTimeTotal * 10).ToVector2() * 64; RectangleShape.DrawBySize(po, Vector2.One * 8, ShapeFill.Solid); Surface.ResetTarget(); @@ -163,7 +163,7 @@ public override void Draw() /// void InitSurface() { - _surface = new Surface(128, 128); + _surface = new Surface(new Vector2(128, 128)); Surface.SetTarget(_surface); diff --git a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs index f2925ca..1755d64 100644 --- a/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs +++ b/Samples/Monofoxe.Samples/Demos/UtilsDemo.cs @@ -98,7 +98,7 @@ public UtilsDemo(Layer layer) : base(layer) // Camera. - _camera = new Camera2D(400, 600); + _camera = new Camera2D(new Vector2(400, 600)); _camera.PortPosition = new Vector2(400, 0); _camera.BackgroundColor = Color.Black; _camera.PostprocessorEffects.Add(_grayscale); diff --git a/Samples/Monofoxe.Samples/GameController.cs b/Samples/Monofoxe.Samples/GameController.cs index cf6374c..fab4371 100644 --- a/Samples/Monofoxe.Samples/GameController.cs +++ b/Samples/Monofoxe.Samples/GameController.cs @@ -12,7 +12,7 @@ namespace Monofoxe.Samples { public class GameController : Entity { - public static Camera2D MainCamera = new Camera2D(800, 600); + public static Camera2D MainCamera = new Camera2D(new Vector2(800, 600)); Layer _guiLayer; diff --git a/Templates/ProjectTemplates/Crossplatform/Library/GameController.cs b/Templates/ProjectTemplates/Crossplatform/Library/GameController.cs index d622cda..676c17d 100644 --- a/Templates/ProjectTemplates/Crossplatform/Library/GameController.cs +++ b/Templates/ProjectTemplates/Crossplatform/Library/GameController.cs @@ -11,7 +11,7 @@ namespace $safeprojectname$ { public class GameController : Entity { - public Camera2D Camera = new Camera2D(800, 600); + public Camera2D Camera = new Camera2D(new Vector2(800, 600)); private Sprite _monofoxe; public GameController() : base(SceneMgr.GetScene("default")["default"]) @@ -33,19 +33,19 @@ public GameController() : base(SceneMgr.GetScene("default")["default"]) Text.CurrentFont = ResourceHub.GetResource("Arial"); } - + + public override void Update() { - base.Update(); + base.Update(); } - + public override void Draw() { base.Draw(); _monofoxe.Draw(Camera.Size / 2f); } - } } \ No newline at end of file From f499d1b7351d267523eb25fe5104bc8aad8888e5 Mon Sep 17 00:00:00 2001 From: minkberry Date: Mon, 5 Feb 2024 21:49:09 +0100 Subject: [PATCH 22/22] Updated changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6352857..a58f8b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +## [v3.0.0-dev.1] - *05.02.2024* + ### Added - Added `RenderMask` as a replacement to camera filters to `Scene`, `Layer` and `Entity`.