From a9485475c7583a626801e7ae15550d34bd02f8dd Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 18:39:25 +0300 Subject: [PATCH 01/93] feat: Initial Physics Code From Previous Repo --- .gitignore | 484 ++++++++++++++++++ Engine.Physics2D/Abstract/ICollider2D.cs | 19 + .../Abstract/ICollisionResolver2D.cs | 6 + Engine.Physics2D/Abstract/IPhysicsEngine2D.cs | 11 + .../Abstract/IPhysicsMaterial2D.cs | 7 + Engine.Physics2D/Abstract/IRigidBody2D.cs | 14 + Engine.Physics2D/Collider2DAABBBehaviour.cs | 67 +++ Engine.Physics2D/CollisionInformation.cs | 9 + Engine.Physics2D/Engine.Physics2D.csproj | 13 + Engine.Physics2D/PhysicsEngine2D.cs | 75 +++ Engine.Physics2D/PhysicsMaterial2D.cs | 5 + Engine.Physics2D/PhysicsMaterial2DDefault.cs | 6 + Engine.Physics2D/PhysicsMath.cs | 118 +++++ Engine.Physics2D/Primitives/AABB.cs | 17 + Engine.Physics2D/Primitives/Circle.cs | 18 + Engine.Physics2D/Primitives/Line.cs | 146 ++++++ Engine.Physics2D/Primitives/LineEquation.cs | 8 + Engine.Physics2D/Primitives/Math.cs | 9 + Engine.Physics2D/Primitives/Shape.cs | 68 +++ Engine.Physics2D/Primitives/Triangle.cs | 54 ++ Engine.Physics2D/RigidBody2D.cs | 24 + Engine.sln | 6 + 22 files changed, 1184 insertions(+) create mode 100644 .gitignore create mode 100644 Engine.Physics2D/Abstract/ICollider2D.cs create mode 100644 Engine.Physics2D/Abstract/ICollisionResolver2D.cs create mode 100644 Engine.Physics2D/Abstract/IPhysicsEngine2D.cs create mode 100644 Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs create mode 100644 Engine.Physics2D/Abstract/IRigidBody2D.cs create mode 100644 Engine.Physics2D/Collider2DAABBBehaviour.cs create mode 100644 Engine.Physics2D/CollisionInformation.cs create mode 100644 Engine.Physics2D/Engine.Physics2D.csproj create mode 100644 Engine.Physics2D/PhysicsEngine2D.cs create mode 100644 Engine.Physics2D/PhysicsMaterial2D.cs create mode 100644 Engine.Physics2D/PhysicsMaterial2DDefault.cs create mode 100644 Engine.Physics2D/PhysicsMath.cs create mode 100644 Engine.Physics2D/Primitives/AABB.cs create mode 100644 Engine.Physics2D/Primitives/Circle.cs create mode 100644 Engine.Physics2D/Primitives/Line.cs create mode 100644 Engine.Physics2D/Primitives/LineEquation.cs create mode 100644 Engine.Physics2D/Primitives/Math.cs create mode 100644 Engine.Physics2D/Primitives/Shape.cs create mode 100644 Engine.Physics2D/Primitives/Triangle.cs create mode 100644 Engine.Physics2D/RigidBody2D.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..104b544 --- /dev/null +++ b/.gitignore @@ -0,0 +1,484 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# Mac bundle stuff +*.dmg +*.app + +# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs new file mode 100644 index 0000000..a88a19f --- /dev/null +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; + +namespace Syntriax.Engine.Physics2D.Abstract; + +public interface ICollider2D : IBehaviour, IAssignableTransform +{ + Action? OnCollisionPreResolve { get; set; } + + IRigidBody2D? RigidBody2D { get; } + + IList Vertices { get; } + + bool CheckCollision(Vector2D point); + void Recalculate(); +} diff --git a/Engine.Physics2D/Abstract/ICollisionResolver2D.cs b/Engine.Physics2D/Abstract/ICollisionResolver2D.cs new file mode 100644 index 0000000..83d54b4 --- /dev/null +++ b/Engine.Physics2D/Abstract/ICollisionResolver2D.cs @@ -0,0 +1,6 @@ +namespace Syntriax.Engine.Physics2D.Abstract; + +public interface ICollisionResolver2D +{ + void ResolveCollision(ICollider2D colliderA, ICollider2D colliderB); +} diff --git a/Engine.Physics2D/Abstract/IPhysicsEngine2D.cs b/Engine.Physics2D/Abstract/IPhysicsEngine2D.cs new file mode 100644 index 0000000..aed1d10 --- /dev/null +++ b/Engine.Physics2D/Abstract/IPhysicsEngine2D.cs @@ -0,0 +1,11 @@ +namespace Syntriax.Engine.Physics2D.Abstract; + +public interface IPhysicsEngine2D +{ + int IterationCount { get; set; } + + void AddRigidBody(IRigidBody2D rigidBody); + void RemoveRigidBody(IRigidBody2D rigidBody); + + void Step(float deltaTime); +} diff --git a/Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs b/Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs new file mode 100644 index 0000000..c533cda --- /dev/null +++ b/Engine.Physics2D/Abstract/IPhysicsMaterial2D.cs @@ -0,0 +1,7 @@ +namespace Syntriax.Engine.Physics2D.Abstract; + +public interface IPhysicsMaterial2D +{ + float Friction { get; } + float Restitution { get; } +} diff --git a/Engine.Physics2D/Abstract/IRigidBody2D.cs b/Engine.Physics2D/Abstract/IRigidBody2D.cs new file mode 100644 index 0000000..820caa6 --- /dev/null +++ b/Engine.Physics2D/Abstract/IRigidBody2D.cs @@ -0,0 +1,14 @@ +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; + +namespace Syntriax.Engine.Physics2D.Abstract; + +public interface IRigidBody2D : IBehaviour, IAssignableTransform +{ + IPhysicsMaterial2D Material { get; set; } + + Vector2D Velocity { get; set; } + float AngularVelocity { get; set; } + + float Mass { get; set; } +} diff --git a/Engine.Physics2D/Collider2DAABBBehaviour.cs b/Engine.Physics2D/Collider2DAABBBehaviour.cs new file mode 100644 index 0000000..79fc368 --- /dev/null +++ b/Engine.Physics2D/Collider2DAABBBehaviour.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Physics2D.Abstract; +using Syntriax.Engine.Physics2D.Primitives; + +namespace Syntriax.Engine.Physics2D; + +public class Collider2DAABBBehaviour : BehaviourOverride, ICollider2D +{ + public AABB AABBLocal { get; set; } = null!; + public AABB AABBWorld { get; private set; } = null!; + + private IRigidBody2D? _rigidBody2D = null; + private List _vertices = new List(4); + + public IRigidBody2D? RigidBody2D + { + get + { + if (_rigidBody2D is null) + BehaviourController.TryGetBehaviour(out _rigidBody2D); + + return _rigidBody2D; + } + } + + public Action? OnCollisionPreResolve { get; set; } = null; + + public Action? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } + ITransform IAssignableTransform.Transform => Transform; + public bool Assign(ITransform transform) => GameObject.Assign(transform); + + public IList Vertices => _vertices; + + public bool CheckCollision(Vector2D point) + { + return AABBWorld.Overlaps(point); + } + + public void Recalculate() + { + AABBWorld = new AABB( + AABBLocal.LowerBoundary.Scale(Transform.Scale) + Transform.Position, + AABBLocal.UpperBoundary.Scale(Transform.Scale) + Transform.Position + ); + + Vertices.Clear(); + Vertices.Add(AABBWorld.LowerBoundary); + Vertices.Add(new Vector2D(AABBWorld.LowerBoundary.X, AABBWorld.UpperBoundary.Y)); + Vertices.Add(AABBWorld.UpperBoundary); + Vertices.Add(new Vector2D(AABBWorld.UpperBoundary.X, AABBWorld.LowerBoundary.Y)); + } + public Collider2DAABBBehaviour(Vector2D lowerBoundary, Vector2D upperBoundary) + { + AABBLocal = new AABB(lowerBoundary, upperBoundary); + AABBWorld = new AABB(lowerBoundary, upperBoundary); + } + + public Collider2DAABBBehaviour() + { + AABBLocal = new(Vector2D.Zero, Vector2D.Zero); + AABBWorld = new(Vector2D.Zero, Vector2D.Zero); + } +} diff --git a/Engine.Physics2D/CollisionInformation.cs b/Engine.Physics2D/CollisionInformation.cs new file mode 100644 index 0000000..24d561d --- /dev/null +++ b/Engine.Physics2D/CollisionInformation.cs @@ -0,0 +1,9 @@ +using Microsoft.Xna.Framework; + +namespace Syntriax.Engine.Physics2D; + +public record CollisionInformation +( + Vector2 Normal, + Vector2 ContactPosition +); diff --git a/Engine.Physics2D/Engine.Physics2D.csproj b/Engine.Physics2D/Engine.Physics2D.csproj new file mode 100644 index 0000000..adb4974 --- /dev/null +++ b/Engine.Physics2D/Engine.Physics2D.csproj @@ -0,0 +1,13 @@ + + + + + + + + net8.0 + disable + enable + + + diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs new file mode 100644 index 0000000..684bdeb --- /dev/null +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Physics2D.Abstract; +using Syntriax.Engine.Physics2D.Primitives; + +namespace Syntriax.Engine.Physics2D; + +public class PhysicsEngine2D : IPhysicsEngine2D +{ + private List rigidBodies = new List(32); + private List colliders = new List(64); + + private int _iterationCount = 1; + + + public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } + + public void AddRigidBody(IRigidBody2D rigidBody) + { + if (rigidBodies.Contains(rigidBody)) + return; + + rigidBodies.Add(rigidBody); + + foreach (var collider2D in rigidBody.BehaviourController.GetBehaviours()) + colliders.Add(collider2D); + + rigidBody.BehaviourController.OnBehaviourAdded += OnBehaviourAdded; + rigidBody.BehaviourController.OnBehaviourRemoved += OnBehaviourRemoved; + } + + public void RemoveRigidBody(IRigidBody2D rigidBody) + { + rigidBodies.Remove(rigidBody); + } + + public void Step(float deltaTime) + { + float intervalDeltaTime = deltaTime / IterationCount; + + for (int iterationIndex = 0; iterationIndex < IterationCount; iterationIndex++) + { + foreach (var rigidBody in rigidBodies) + StepRigidBody(rigidBody, intervalDeltaTime); + + foreach (var collider in colliders) + collider.Recalculate(); + } + } + + private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime) + { + rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime; + rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime; + } + + private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour) + { + if (behaviour is not ICollider2D collider2D) + return; + + colliders.Add(collider2D); + } + + private void OnBehaviourRemoved(IBehaviourController controller, IBehaviour behaviour) + { + if (behaviour is not ICollider2D collider2D) + return; + + colliders.Remove(collider2D); + } +} diff --git a/Engine.Physics2D/PhysicsMaterial2D.cs b/Engine.Physics2D/PhysicsMaterial2D.cs new file mode 100644 index 0000000..878691a --- /dev/null +++ b/Engine.Physics2D/PhysicsMaterial2D.cs @@ -0,0 +1,5 @@ +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public record PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D { } diff --git a/Engine.Physics2D/PhysicsMaterial2DDefault.cs b/Engine.Physics2D/PhysicsMaterial2DDefault.cs new file mode 100644 index 0000000..61b4367 --- /dev/null +++ b/Engine.Physics2D/PhysicsMaterial2DDefault.cs @@ -0,0 +1,6 @@ +namespace Syntriax.Engine.Physics2D; + +public record PhysicsMaterial2DDefault : PhysicsMaterial2D +{ + public PhysicsMaterial2DDefault() : base(.1f, .1f) { } +} diff --git a/Engine.Physics2D/PhysicsMath.cs b/Engine.Physics2D/PhysicsMath.cs new file mode 100644 index 0000000..7ccd28c --- /dev/null +++ b/Engine.Physics2D/PhysicsMath.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; + +using Microsoft.Xna.Framework; +using Syntriax.Engine.Core; +using Syntriax.Engine.Physics2D.Primitives; + +namespace Syntriax.Engine.Physics2D; + +public static class PhysicsMath +{ + public static Vector2D Scale(this Vector2D original, Vector2D scale) + => new Vector2D(original.X * scale.X, original.Y * scale.Y); + + public static Triangle ToSuperTriangle(this IList vertices) + { + float minX = float.MaxValue, minY = float.MaxValue; + float maxX = float.MinValue, maxY = float.MinValue; + + foreach (Vector2D point in vertices) + { + minX = MathF.Min(minX, point.X); + minY = MathF.Min(minY, point.Y); + maxX = MathF.Max(maxX, point.X); + maxY = MathF.Max(maxY, point.Y); + } + + float dx = maxX - minX; + float dy = maxY - minY; + float deltaMax = MathF.Max(dx, dy); + float midX = (minX + maxX) / 2; + float midY = (minY + maxY) / 2; + + Vector2D p1 = new Vector2D((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax); + Vector2D p2 = new Vector2D((float)midX, (float)midY + 20 * (float)deltaMax); + Vector2D p3 = new Vector2D((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax); + + return new Triangle(p1, p2, p3); + } + + public static IList ToLines(this IList vertices) + { + List lines = new List(vertices.Count - 1); + ToLines(vertices, lines); + return lines; + } + + public static void ToLines(this IList vertices, IList lines) + { + lines.Clear(); + for (int i = 0; i < vertices.Count - 1; i++) + lines.Add(new(vertices[i], vertices[i + 1])); + lines.Add(new(vertices[^1], vertices[0])); + } + + public static bool LaysOn(this Vector2D point, Line line) + => line.Resolve(point.X).ApproximatelyEquals(point); + + + // Given three collinear points p, q, r, the function checks if + // point q lies on line segment 'pr' + public static bool OnSegment(Vector2D p, Vector2D q, Vector2D r) + { + if (q.X <= MathF.Max(p.X, r.X) && q.X >= MathF.Min(p.X, r.X) && + q.Y <= MathF.Max(p.Y, r.Y) && q.Y >= MathF.Min(p.Y, r.Y)) + return true; + + return false; + } + + // To find orientation of ordered triplet (p, q, r). + // The function returns following values + // 0 --> p, q and r are collinear + // 1 --> Clockwise + // 2 --> Counterclockwise + public static int Orientation(Vector2D p, Vector2D q, Vector2D r) + { + // See https://www.geeksforgeeks.org/orientation-3-ordered-points/ + // for details of below formula. + float val = (q.Y - p.Y) * (r.X - q.X) - + (q.X - p.X) * (r.Y - q.Y); + + if (val == 0) return 0; // collinear + + return (val > 0) ? 1 : 2; // clock or counterclock wise + } + + public static float IntersectionParameterT(Vector2D p0, Vector2D p1, Vector2D q0, Vector2D q1) + => ((q0.X - p0.X) * (p1.Y - p0.Y) - (q0.Y - p0.Y) * (p1.X - p0.X)) / + ((q1.Y - q0.Y) * (p1.X - p0.X) - (q1.X - q0.X) * (p1.Y - p0.Y)); + + + public static bool ApproximatelyEquals(this float a, float b) + => ApproximatelyEquals(a, b, float.Epsilon); + public static bool ApproximatelyEquals(this Vector2 a, Vector2 b) + => ApproximatelyEquals(a, b, float.Epsilon); + public static bool ApproximatelyEquals(this Vector2 a, Vector2 b, float epsilon) + => ApproximatelyEquals(a.X, b.X, epsilon) && ApproximatelyEquals(a.Y, b.Y, epsilon); + public static bool ApproximatelyEquals(this Vector2D a, Vector2D b) + => ApproximatelyEquals(a, b, float.Epsilon); + public static bool ApproximatelyEquals(this Vector2D a, Vector2D b, float epsilon) + => ApproximatelyEquals(a.X, b.X, epsilon) && ApproximatelyEquals(a.Y, b.Y, epsilon); + public static bool ApproximatelyEquals(this float a, float b, float epsilon) + { + if (a == b) + return true; + + const float floatNormal = (1 << 23) * float.Epsilon; + float absA = MathF.Abs(a); + float absB = MathF.Abs(b); + float diff = MathF.Abs(a - b); + + if (a == 0.0f || b == 0.0f || diff < floatNormal) + return diff < (epsilon * floatNormal); + + return diff / MathF.Min(absA + absB, float.MaxValue) < epsilon; + } +} diff --git a/Engine.Physics2D/Primitives/AABB.cs b/Engine.Physics2D/Primitives/AABB.cs new file mode 100644 index 0000000..96e1560 --- /dev/null +++ b/Engine.Physics2D/Primitives/AABB.cs @@ -0,0 +1,17 @@ +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Physics2D.Primitives; + +public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) +{ + public bool Overlaps(Vector2D point) + => point.X >= LowerBoundary.X && point.X <= UpperBoundary.X && + point.Y >= LowerBoundary.Y && point.Y <= UpperBoundary.Y; + + public bool Overlaps(AABB other) + => LowerBoundary.X <= other.UpperBoundary.X && UpperBoundary.X >= other.LowerBoundary.X && + LowerBoundary.Y <= other.UpperBoundary.Y && UpperBoundary.Y >= other.LowerBoundary.Y; + + public bool ApproximatelyEquals(AABB other) + => LowerBoundary.ApproximatelyEquals(other.LowerBoundary) && UpperBoundary.ApproximatelyEquals(other.UpperBoundary); +} diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs new file mode 100644 index 0000000..1d30cf2 --- /dev/null +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -0,0 +1,18 @@ +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Physics2D.Primitives; + +public record Circle(Vector2D Position, float Radius) +{ + public bool Intersects(Circle other) + { + float distanceSquared = (Position - other.Position).LengthSquared(); + float radiusSumSquared = Radius * Radius + other.Radius * other.Radius; + + return distanceSquared < radiusSumSquared; + } + + public bool Overlaps(Vector2D point) => (Position - point).LengthSquared() <= Radius * Radius; + public bool ApproximatelyEquals(Circle other) + => Position.ApproximatelyEquals(other.Position) && Radius.ApproximatelyEquals(other.Radius); +} diff --git a/Engine.Physics2D/Primitives/Line.cs b/Engine.Physics2D/Primitives/Line.cs new file mode 100644 index 0000000..bd3162c --- /dev/null +++ b/Engine.Physics2D/Primitives/Line.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Physics2D.Primitives; + +public record Line(Vector2D From, Vector2D To) +{ + public Line Reversed => new(To, From); + public Vector2D Direction => Vector2D.Normalize(To - From); + public float Length => (From - To).Length(); + public float LengthSquared => (From - To).LengthSquared(); + + public LineEquation LineEquation + { + get + { + Vector2D slopeVector = To - From; + float slope = slopeVector.Y / slopeVector.X; + + float yOffset = From.Y - (slope * From.X); + + return new LineEquation(slope, yOffset); + } + } + + public bool Intersects(Vector2D point) + => Resolve(point.X).ApproximatelyEquals(point); + + public float GetT(Vector2D point) + { + float fromX = MathF.Abs(From.X); + float toX = MathF.Abs(To.X); + float pointX = MathF.Abs(point.X); + + float min = MathF.Min(fromX, toX); + float max = MathF.Max(fromX, toX) - min; + + pointX -= min; + + float t = pointX / max; + + // FIXME + // I don't even know, apparently whatever I wrote up there doesn't take into account of the direction of the line + // Which... I can see how, but I am also not sure how I can make it take into account. Or actually I'm for some reason + // too unmotivated to find a solution. Future me, find a better way if possible, please. + if (!Lerp(t).ApproximatelyEquals(point)) + return 1f - t; + return t; + } + + public bool Exist(List vertices) + { + for (int i = 0; i < vertices.Count - 1; i++) + { + Vector2D vertexCurrent = vertices[i]; + Vector2D vertexNext = vertices[i]; + if (From == vertexCurrent && To == vertexNext) return true; + if (From == vertexNext && To == vertexCurrent) return true; + } + + Vector2D vertexFirst = vertices[0]; + Vector2D vertexLast = vertices[^1]; + if (From == vertexFirst && To == vertexLast) return true; + if (From == vertexLast && To == vertexFirst) return true; + return false; + } + + public float IntersectionParameterT(Line other) + { + float numerator = (From.X - other.From.X) * (other.From.Y - other.To.Y) - (From.Y - other.From.Y) * (other.From.X - other.To.X); + float denominator = (From.X - To.X) * (other.From.Y - other.To.Y) - (From.Y - To.Y) * (other.From.X - other.To.X); + + // Lines are parallel + if (denominator == 0) + return float.NaN; + + return numerator / denominator; + } + + public Vector2D Lerp(float t) + => new Vector2D( + From.X + (To.X - From.X) * t, + From.Y + (To.Y - From.Y) * t + ); + + public Vector2D Resolve(float x) + => new Vector2D(x, LineEquation.Resolve(x)); + + public Vector2D ClosestPointTo(Vector2D point) + { + // Convert edge points to vectors + var edgeVector = new Vector2D(To.X - From.X, To.Y - From.Y); + var pointVector = new Vector2D(point.X - From.X, point.Y - From.Y); + + // Calculate the projection of pointVector onto edgeVector + float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y); + + // Clamp t to the range [0, 1] to ensure the closest point is on the edge + t = MathF.Max(0, MathF.Min(1, t)); + + // Calculate the closest point on the edge + float closestX = From.X + t * edgeVector.X; + float closestY = From.Y + t * edgeVector.Y; + + return new Vector2D((float)closestX, (float)closestY); + } + + public Vector2D IntersectionPoint(Line other) + => Vector2D.Lerp(From, To, IntersectionParameterT(other)); + + public bool Intersects(Line other) + { + int o1 = PhysicsMath.Orientation(From, To, other.From); + int o2 = PhysicsMath.Orientation(From, To, other.To); + int o3 = PhysicsMath.Orientation(other.From, other.To, From); + int o4 = PhysicsMath.Orientation(other.From, other.To, To); + + if (o1 != o2 && o3 != o4) + return true; + + if (o1 == 0 && PhysicsMath.OnSegment(From, other.From, To)) return true; + if (o2 == 0 && PhysicsMath.OnSegment(From, other.To, To)) return true; + if (o3 == 0 && PhysicsMath.OnSegment(other.From, From, other.To)) return true; + if (o4 == 0 && PhysicsMath.OnSegment(other.From, To, other.To)) return true; + + return false; + } + + public bool Intersects(Line other, [NotNullWhen(returnValue: true)] out Vector2D? point) + { + point = null; + + bool result = Intersects(other); + + if (result) + point = IntersectionPoint(other); + + return result; + } + + public bool ApproximatelyEquals(Line other) + => From.ApproximatelyEquals(other.From) && To.ApproximatelyEquals(other.To); +} diff --git a/Engine.Physics2D/Primitives/LineEquation.cs b/Engine.Physics2D/Primitives/LineEquation.cs new file mode 100644 index 0000000..a60977b --- /dev/null +++ b/Engine.Physics2D/Primitives/LineEquation.cs @@ -0,0 +1,8 @@ +namespace Syntriax.Engine.Physics2D.Primitives; + +public record LineEquation(float Slope, float OffsetY) +{ + public float Resolve(float x) => Slope * x + OffsetY; // y = mx + b + public bool ApproximatelyEquals(LineEquation other) + => Slope.ApproximatelyEquals(other.Slope) && OffsetY.ApproximatelyEquals(other.OffsetY); +} diff --git a/Engine.Physics2D/Primitives/Math.cs b/Engine.Physics2D/Primitives/Math.cs new file mode 100644 index 0000000..79875d4 --- /dev/null +++ b/Engine.Physics2D/Primitives/Math.cs @@ -0,0 +1,9 @@ +namespace Syntriax.Engine.Physics2D.Primitives; + +public static class Math +{ + public const float RadianToDegree = 57.29577866666166f; + public const float DegreeToRadian = 0.01745329277777778f; + + public static float Clamp(float value, float min, float max) => (value < min) ? min : (value > max) ? max : value; +} diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs new file mode 100644 index 0000000..8ba8dbf --- /dev/null +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Physics2D.Primitives; + +public record Shape(IList Vertices) +{ + public Triangle SuperTriangle + { + get + { + float minX = float.MaxValue, minY = float.MaxValue; + float maxX = float.MinValue, maxY = float.MinValue; + + foreach (Vector2D point in Vertices) + { + minX = MathF.Min(minX, point.X); + minY = MathF.Min(minY, point.Y); + maxX = MathF.Max(maxX, point.X); + maxY = MathF.Max(maxY, point.Y); + } + + float dx = maxX - minX; + float dy = maxY - minY; + float deltaMax = MathF.Max(dx, dy); + float midX = (minX + maxX) / 2; + float midY = (minY + maxY) / 2; + + Vector2D p1 = new Vector2D((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax); + Vector2D p2 = new Vector2D((float)midX, (float)midY + 20 * (float)deltaMax); + Vector2D p3 = new Vector2D((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax); + + return new Triangle(p1, p2, p3); + } + } + + public List Lines + { + get + { + List lines = new List(Vertices.Count - 1); + GetLinesNonAlloc(lines); + return lines; + } + } + + public void GetLinesNonAlloc(IList lines) + { + lines.Clear(); + for (int i = 0; i < Vertices.Count - 1; i++) + lines.Add(new(Vertices[i], Vertices[i + 1])); + lines.Add(new(Vertices[^1], Vertices[0])); + } + + public bool ApproximatelyEquals(Shape other) + { + if (Vertices.Count != other.Vertices.Count) + return false; + + for (int i = 0; i < Vertices.Count; i++) + if (!Vertices[i].ApproximatelyEquals(other.Vertices[i])) + return false; + + return true; + } +} diff --git a/Engine.Physics2D/Primitives/Triangle.cs b/Engine.Physics2D/Primitives/Triangle.cs new file mode 100644 index 0000000..5ffcd6a --- /dev/null +++ b/Engine.Physics2D/Primitives/Triangle.cs @@ -0,0 +1,54 @@ +using System; + +using Syntriax.Engine.Core; + +namespace Syntriax.Engine.Physics2D.Primitives; + +public record Triangle(Vector2D A, Vector2D B, Vector2D C) +{ + public float Area => MathF.Abs(( + A.X * (B.Y - C.Y) + + B.X * (C.Y - A.Y) + + C.X * (A.Y - B.Y) + ) * .5f); + + public Circle CircumCircle + { + get + { + Vector2D midAB = (A + B) / 2; + Vector2D midBC = (B + C) / 2; + + float slopeAB = (B.Y - A.Y) / (B.X - A.X); + float slopeBC = (C.Y - B.Y) / (C.X - B.X); + + Vector2D center; + if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon) + { + float x = (slopeAB * slopeBC * (A.Y - C.Y) + slopeBC * (A.X + B.X) - slopeAB * (B.X + C.X)) / (2 * (slopeBC - slopeAB)); + float y = -(x - (A.X + B.X) / 2) / slopeAB + (A.Y + B.Y) / 2; + center = new Vector2D(x, y); + } + else + center = (midAB + midBC) * .5f; + + return new(center, Vector2D.Distance(center, A)); + } + } + + public bool Overlaps(Vector2D point) + { + float originalTriangleArea = Area; + + float pointTriangleArea1 = new Triangle(point, B, C).Area; + float pointTriangleArea2 = new Triangle(A, point, C).Area; + float pointTriangleArea3 = new Triangle(A, B, point).Area; + + float pointTriangleAreasSum = pointTriangleArea1 + pointTriangleArea2 + pointTriangleArea3; + + return originalTriangleArea.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f); + } + + public bool ApproximatelyEquals(Triangle other) + => A.ApproximatelyEquals(other.A) && B.ApproximatelyEquals(other.B) && C.ApproximatelyEquals(other.C); +} diff --git a/Engine.Physics2D/RigidBody2D.cs b/Engine.Physics2D/RigidBody2D.cs new file mode 100644 index 0000000..ed4fb4a --- /dev/null +++ b/Engine.Physics2D/RigidBody2D.cs @@ -0,0 +1,24 @@ +using System; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public class RigidBody2D : BehaviourOverride, IRigidBody2D +{ + public Action? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } + + + public IPhysicsMaterial2D Material { get; set; } = new PhysicsMaterial2DDefault(); + + public Vector2D Velocity { get; set; } = Vector2D.Zero; + public float AngularVelocity { get; set; } = 0f; + public float Mass { get; set; } = 0f; + + ITransform IAssignableTransform.Transform => Transform; + + + public bool Assign(ITransform transform) => GameObject.Assign(transform); +} diff --git a/Engine.sln b/Engine.sln index 35f3e6c..e76e3fd 100644 --- a/Engine.sln +++ b/Engine.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "Engine.Core\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Input", "Engine.Input\Engine.Input.csproj", "{12149E55-1EE8-45B4-A82E-15BA981B0C6A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Physics2D", "Engine.Physics2D\Engine.Physics2D.csproj", "{3B3C3332-07E3-4A00-9898-0A5410BCB08C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,5 +26,9 @@ Global {12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Debug|Any CPU.Build.0 = Debug|Any CPU {12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {12149E55-1EE8-45B4-A82E-15BA981B0C6A}.Release|Any CPU.Build.0 = Release|Any CPU + {3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B3C3332-07E3-4A00-9898-0A5410BCB08C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 8ccebaa8fb69ce95b7b64b1df43bcf742bc1affe Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:12:41 +0300 Subject: [PATCH 02/93] chore: Solution File for Physics2D --- Engine.Physics2D/Engine.Physics2D.sln | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Engine.Physics2D/Engine.Physics2D.sln diff --git a/Engine.Physics2D/Engine.Physics2D.sln b/Engine.Physics2D/Engine.Physics2D.sln new file mode 100644 index 0000000..fe814ce --- /dev/null +++ b/Engine.Physics2D/Engine.Physics2D.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine.Physics2D", "Engine.Physics2D.csproj", "{88874ADD-23BD-4781-A47C-71B5D4AA9C25}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "..\Engine.Core\Engine.Core.csproj", "{A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Release|Any CPU.Build.0 = Release|Any CPU + {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9C16F1F0-F5F8-4ED9-A3B4-7A0BF8003AA1} + EndGlobalSection +EndGlobal From 5c185a664c61b35759143ad9921958f6d080c134 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:13:51 +0300 Subject: [PATCH 03/93] refactor: AABB --- Engine.Physics2D/Primitives/AABB.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Engine.Physics2D/Primitives/AABB.cs b/Engine.Physics2D/Primitives/AABB.cs index 96e1560..5ffef2b 100644 --- a/Engine.Physics2D/Primitives/AABB.cs +++ b/Engine.Physics2D/Primitives/AABB.cs @@ -4,14 +4,11 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) { - public bool Overlaps(Vector2D point) - => point.X >= LowerBoundary.X && point.X <= UpperBoundary.X && - point.Y >= LowerBoundary.Y && point.Y <= UpperBoundary.Y; - - public bool Overlaps(AABB other) - => LowerBoundary.X <= other.UpperBoundary.X && UpperBoundary.X >= other.LowerBoundary.X && - LowerBoundary.Y <= other.UpperBoundary.Y && UpperBoundary.Y >= other.LowerBoundary.Y; - - public bool ApproximatelyEquals(AABB other) - => LowerBoundary.ApproximatelyEquals(other.LowerBoundary) && UpperBoundary.ApproximatelyEquals(other.UpperBoundary); + public static bool ApproximatelyEquals(AABB left, AABB right) + => left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary); +} + +public static class AABBExtensions +{ + public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right); } From 5170dd0aeabdf97c256fbc653a46034279d36d89 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:14:23 +0300 Subject: [PATCH 04/93] refactor: Circle --- Engine.Physics2D/Primitives/Circle.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index 1d30cf2..4c31175 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -4,15 +4,14 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Circle(Vector2D Position, float Radius) { - public bool Intersects(Circle other) - { - float distanceSquared = (Position - other.Position).LengthSquared(); - float radiusSumSquared = Radius * Radius + other.Radius * other.Radius; + public float RadiusSquared => Radius * Radius; + public float Diameter => 2f * Radius; - return distanceSquared < radiusSumSquared; - } - - public bool Overlaps(Vector2D point) => (Position - point).LengthSquared() <= Radius * Radius; - public bool ApproximatelyEquals(Circle other) - => Position.ApproximatelyEquals(other.Position) && Radius.ApproximatelyEquals(other.Radius); + public static bool ApproximatelyEquals(Circle left, Circle right) + => left.Position.ApproximatelyEquals(right.Position) && left.Radius.ApproximatelyEquals(right.Radius); +} + +public static class CircleExtensions +{ + public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right); } From dcda78b01015255aabc874a3bae005c0156d2481 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:15:09 +0300 Subject: [PATCH 05/93] refactor: Line --- Engine.Physics2D/Primitives/Line.cs | 103 ++++++++++++++-------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/Engine.Physics2D/Primitives/Line.cs b/Engine.Physics2D/Primitives/Line.cs index bd3162c..c23bd8a 100644 --- a/Engine.Physics2D/Primitives/Line.cs +++ b/Engine.Physics2D/Primitives/Line.cs @@ -9,30 +9,27 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Line(Vector2D From, Vector2D To) { public Line Reversed => new(To, From); - public Vector2D Direction => Vector2D.Normalize(To - From); - public float Length => (From - To).Length(); - public float LengthSquared => (From - To).LengthSquared(); + public Vector2D Direction => From.FromTo(To).Normalize(); + public float Length => From.FromTo(To).Length(); + public float LengthSquared => From.FromTo(To).LengthSquared(); - public LineEquation LineEquation + public static LineEquation GetLineEquation(Line line) { - get - { - Vector2D slopeVector = To - From; - float slope = slopeVector.Y / slopeVector.X; + Vector2D slopeVector = line.From.FromTo(line.To); + float slope = slopeVector.Y / slopeVector.X; - float yOffset = From.Y - (slope * From.X); + float yOffset = line.From.Y - (slope * line.From.X); - return new LineEquation(slope, yOffset); - } + return new LineEquation(slope, yOffset); } - public bool Intersects(Vector2D point) - => Resolve(point.X).ApproximatelyEquals(point); + public static bool Intersects(Line line, Vector2D point) + => LineEquation.Resolve(GetLineEquation(line), point.X).ApproximatelyEquals(point.Y); - public float GetT(Vector2D point) + public static float GetT(Line line, Vector2D point) { - float fromX = MathF.Abs(From.X); - float toX = MathF.Abs(To.X); + float fromX = MathF.Abs(line.From.X); + float toX = MathF.Abs(line.To.X); float pointX = MathF.Abs(point.X); float min = MathF.Min(fromX, toX); @@ -46,32 +43,32 @@ public record Line(Vector2D From, Vector2D To) // I don't even know, apparently whatever I wrote up there doesn't take into account of the direction of the line // Which... I can see how, but I am also not sure how I can make it take into account. Or actually I'm for some reason // too unmotivated to find a solution. Future me, find a better way if possible, please. - if (!Lerp(t).ApproximatelyEquals(point)) + if (!Lerp(line, t).ApproximatelyEquals(point)) return 1f - t; return t; } - public bool Exist(List vertices) + public static bool Exist(Line line, List vertices) { for (int i = 0; i < vertices.Count - 1; i++) { Vector2D vertexCurrent = vertices[i]; Vector2D vertexNext = vertices[i]; - if (From == vertexCurrent && To == vertexNext) return true; - if (From == vertexNext && To == vertexCurrent) return true; + if (line.From == vertexCurrent && line.To == vertexNext) return true; + if (line.From == vertexNext && line.To == vertexCurrent) return true; } Vector2D vertexFirst = vertices[0]; Vector2D vertexLast = vertices[^1]; - if (From == vertexFirst && To == vertexLast) return true; - if (From == vertexLast && To == vertexFirst) return true; + if (line.From == vertexFirst && line.To == vertexLast) return true; + if (line.From == vertexLast && line.To == vertexFirst) return true; return false; } - public float IntersectionParameterT(Line other) + public static float IntersectionParameterT(Line left, Line right) { - float numerator = (From.X - other.From.X) * (other.From.Y - other.To.Y) - (From.Y - other.From.Y) * (other.From.X - other.To.X); - float denominator = (From.X - To.X) * (other.From.Y - other.To.Y) - (From.Y - To.Y) * (other.From.X - other.To.X); + float numerator = (left.From.X - right.From.X) * (right.From.Y - right.To.Y) - (left.From.Y - right.From.Y) * (right.From.X - right.To.X); + float denominator = (left.From.X - left.To.X) * (right.From.Y - right.To.Y) - (left.From.Y - left.To.Y) * (right.From.X - right.To.X); // Lines are parallel if (denominator == 0) @@ -80,20 +77,17 @@ public record Line(Vector2D From, Vector2D To) return numerator / denominator; } - public Vector2D Lerp(float t) + public static Vector2D Lerp(Line line, float t) => new Vector2D( - From.X + (To.X - From.X) * t, - From.Y + (To.Y - From.Y) * t + line.From.X + (line.To.X - line.From.X) * t, + line.From.Y + (line.To.Y - line.From.Y) * t ); - public Vector2D Resolve(float x) - => new Vector2D(x, LineEquation.Resolve(x)); - - public Vector2D ClosestPointTo(Vector2D point) + public static Vector2D ClosestPointTo(Line line, Vector2D point) { // Convert edge points to vectors - var edgeVector = new Vector2D(To.X - From.X, To.Y - From.Y); - var pointVector = new Vector2D(point.X - From.X, point.Y - From.Y); + var edgeVector = new Vector2D(line.To.X - line.From.X, line.To.Y - line.From.Y); + var pointVector = new Vector2D(point.X - line.From.X, point.Y - line.From.Y); // Calculate the projection of pointVector onto edgeVector float t = (pointVector.X * edgeVector.X + pointVector.Y * edgeVector.Y) / (edgeVector.X * edgeVector.X + edgeVector.Y * edgeVector.Y); @@ -102,45 +96,50 @@ public record Line(Vector2D From, Vector2D To) t = MathF.Max(0, MathF.Min(1, t)); // Calculate the closest point on the edge - float closestX = From.X + t * edgeVector.X; - float closestY = From.Y + t * edgeVector.Y; + float closestX = line.From.X + t * edgeVector.X; + float closestY = line.From.Y + t * edgeVector.Y; return new Vector2D((float)closestX, (float)closestY); } - public Vector2D IntersectionPoint(Line other) - => Vector2D.Lerp(From, To, IntersectionParameterT(other)); + public static Vector2D IntersectionPoint(Line left, Line right) + => Vector2D.Lerp(left.From, left.To, IntersectionParameterT(left, right)); - public bool Intersects(Line other) + public static bool Intersects(Line left, Line right) { - int o1 = PhysicsMath.Orientation(From, To, other.From); - int o2 = PhysicsMath.Orientation(From, To, other.To); - int o3 = PhysicsMath.Orientation(other.From, other.To, From); - int o4 = PhysicsMath.Orientation(other.From, other.To, To); + int o1 = PhysicsMath.Orientation(left.From, left.To, right.From); + int o2 = PhysicsMath.Orientation(left.From, left.To, right.To); + int o3 = PhysicsMath.Orientation(right.From, right.To, left.From); + int o4 = PhysicsMath.Orientation(right.From, right.To, left.To); if (o1 != o2 && o3 != o4) return true; - if (o1 == 0 && PhysicsMath.OnSegment(From, other.From, To)) return true; - if (o2 == 0 && PhysicsMath.OnSegment(From, other.To, To)) return true; - if (o3 == 0 && PhysicsMath.OnSegment(other.From, From, other.To)) return true; - if (o4 == 0 && PhysicsMath.OnSegment(other.From, To, other.To)) return true; + if (o1 == 0 && PhysicsMath.OnSegment(left.From, right.From, left.To)) return true; + if (o2 == 0 && PhysicsMath.OnSegment(left.From, right.To, left.To)) return true; + if (o3 == 0 && PhysicsMath.OnSegment(right.From, left.From, right.To)) return true; + if (o4 == 0 && PhysicsMath.OnSegment(right.From, left.To, right.To)) return true; return false; } - public bool Intersects(Line other, [NotNullWhen(returnValue: true)] out Vector2D? point) + public static bool Intersects(Line left, Line right, [NotNullWhen(returnValue: true)] out Vector2D? point) { point = null; - bool result = Intersects(other); + bool result = Intersects(left, right); if (result) - point = IntersectionPoint(other); + point = IntersectionPoint(left, right); return result; } - public bool ApproximatelyEquals(Line other) - => From.ApproximatelyEquals(other.From) && To.ApproximatelyEquals(other.To); + public static bool ApproximatelyEquals(Line left, Line right) + => left.From.ApproximatelyEquals(right.From) && left.To.ApproximatelyEquals(right.To); +} + +public static class LineExtensions +{ + public static bool ApproximatelyEquals(this Line left, Line right) => Line.ApproximatelyEquals(left, right); } From 6a5d10980ae37983c7bd65f32d253c3c4212ee5c Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:15:43 +0300 Subject: [PATCH 06/93] refactor: Triangle --- Engine.Physics2D/Primitives/Triangle.cs | 68 +++++++++++-------------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/Engine.Physics2D/Primitives/Triangle.cs b/Engine.Physics2D/Primitives/Triangle.cs index 5ffcd6a..8ab76ec 100644 --- a/Engine.Physics2D/Primitives/Triangle.cs +++ b/Engine.Physics2D/Primitives/Triangle.cs @@ -6,49 +6,39 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Triangle(Vector2D A, Vector2D B, Vector2D C) { - public float Area => MathF.Abs(( - A.X * (B.Y - C.Y) + - B.X * (C.Y - A.Y) + - C.X * (A.Y - B.Y) - ) * .5f); + public float Area + => .5f * MathF.Abs( + A.X * (B.Y - C.Y) + + B.X * (C.Y - A.Y) + + C.X * (A.Y - B.Y) + ); - public Circle CircumCircle + public static Circle GetCircumCircle(Triangle triangle) { - get + Vector2D midAB = (triangle.A + triangle.B) / 2f; + Vector2D midBC = (triangle.B + triangle.C) / 2f; + + float slopeAB = (triangle.B.Y - triangle.A.Y) / (triangle.B.X - triangle.A.X); + float slopeBC = (triangle.C.Y - triangle.B.Y) / (triangle.C.X - triangle.B.X); + + Vector2D center; + if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon) { - Vector2D midAB = (A + B) / 2; - Vector2D midBC = (B + C) / 2; - - float slopeAB = (B.Y - A.Y) / (B.X - A.X); - float slopeBC = (C.Y - B.Y) / (C.X - B.X); - - Vector2D center; - if (MathF.Abs(slopeAB - slopeBC) > float.Epsilon) - { - float x = (slopeAB * slopeBC * (A.Y - C.Y) + slopeBC * (A.X + B.X) - slopeAB * (B.X + C.X)) / (2 * (slopeBC - slopeAB)); - float y = -(x - (A.X + B.X) / 2) / slopeAB + (A.Y + B.Y) / 2; - center = new Vector2D(x, y); - } - else - center = (midAB + midBC) * .5f; - - return new(center, Vector2D.Distance(center, A)); + float x = (slopeAB * slopeBC * (triangle.A.Y - triangle.C.Y) + slopeBC * (triangle.A.X + triangle.B.X) - slopeAB * (triangle.B.X + triangle.C.X)) / (2f * (slopeBC - slopeAB)); + float y = -(x - (triangle.A.X + triangle.B.X) / 2f) / slopeAB + (triangle.A.Y + triangle.B.Y) / 2f; + center = new Vector2D(x, y); } + else + center = (midAB + midBC) * .5f; + + return new(center, Vector2D.Distance(center, triangle.A)); } - public bool Overlaps(Vector2D point) - { - float originalTriangleArea = Area; - - float pointTriangleArea1 = new Triangle(point, B, C).Area; - float pointTriangleArea2 = new Triangle(A, point, C).Area; - float pointTriangleArea3 = new Triangle(A, B, point).Area; - - float pointTriangleAreasSum = pointTriangleArea1 + pointTriangleArea2 + pointTriangleArea3; - - return originalTriangleArea.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f); - } - - public bool ApproximatelyEquals(Triangle other) - => A.ApproximatelyEquals(other.A) && B.ApproximatelyEquals(other.B) && C.ApproximatelyEquals(other.C); + public static bool ApproximatelyEquals(Triangle left, Triangle right) + => left.A.ApproximatelyEquals(right.A) && left.B.ApproximatelyEquals(right.B) && left.C.ApproximatelyEquals(right.C); +} + +public static class TriangleExtensions +{ + public static bool ApproximatelyEquals(this Triangle left, Triangle right) => Triangle.ApproximatelyEquals(left, right); } From 3b299c947c1d082e960c380802255d2fcdee35ff Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:16:31 +0300 Subject: [PATCH 07/93] refactor: LineEquation --- Engine.Physics2D/Primitives/LineEquation.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Engine.Physics2D/Primitives/LineEquation.cs b/Engine.Physics2D/Primitives/LineEquation.cs index a60977b..8c80782 100644 --- a/Engine.Physics2D/Primitives/LineEquation.cs +++ b/Engine.Physics2D/Primitives/LineEquation.cs @@ -2,7 +2,14 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record LineEquation(float Slope, float OffsetY) { - public float Resolve(float x) => Slope * x + OffsetY; // y = mx + b - public bool ApproximatelyEquals(LineEquation other) - => Slope.ApproximatelyEquals(other.Slope) && OffsetY.ApproximatelyEquals(other.OffsetY); + public static float Resolve(LineEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b + + public static bool ApproximatelyEquals(LineEquation left, LineEquation right) + => left.Slope.ApproximatelyEquals(right.Slope) && left.OffsetY.ApproximatelyEquals(right.OffsetY); +} + +public static class LineEquationExtensions +{ + public static float Resolve(this LineEquation lineEquation, float x) => LineEquation.Resolve(lineEquation, x); + public static bool ApproximatelyEquals(this LineEquation left, LineEquation right) => LineEquation.ApproximatelyEquals(left, right); } From bd03d036aab5fd290922a019dcb192c272974bd5 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:17:08 +0300 Subject: [PATCH 08/93] refactor: Shape --- Engine.Physics2D/Primitives/Shape.cs | 87 ++++++++++++++-------------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 8ba8dbf..1091856 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -7,62 +7,65 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Shape(IList Vertices) { - public Triangle SuperTriangle + public static Triangle GetSuperTriangle(Shape shape) { - get + float minX = float.MaxValue, minY = float.MaxValue; + float maxX = float.MinValue, maxY = float.MinValue; + + foreach (Vector2D point in shape.Vertices) { - float minX = float.MaxValue, minY = float.MaxValue; - float maxX = float.MinValue, maxY = float.MinValue; - - foreach (Vector2D point in Vertices) - { - minX = MathF.Min(minX, point.X); - minY = MathF.Min(minY, point.Y); - maxX = MathF.Max(maxX, point.X); - maxY = MathF.Max(maxY, point.Y); - } - - float dx = maxX - minX; - float dy = maxY - minY; - float deltaMax = MathF.Max(dx, dy); - float midX = (minX + maxX) / 2; - float midY = (minY + maxY) / 2; - - Vector2D p1 = new Vector2D((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax); - Vector2D p2 = new Vector2D((float)midX, (float)midY + 20 * (float)deltaMax); - Vector2D p3 = new Vector2D((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax); - - return new Triangle(p1, p2, p3); + minX = MathF.Min(minX, point.X); + minY = MathF.Min(minY, point.Y); + maxX = MathF.Max(maxX, point.X); + maxY = MathF.Max(maxY, point.Y); } + + float dx = maxX - minX; + float dy = maxY - minY; + float deltaMax = MathF.Max(dx, dy); + float midX = (minX + maxX) / 2; + float midY = (minY + maxY) / 2; + + Vector2D p1 = new((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax); + Vector2D p2 = new((float)midX, (float)midY + 20 * (float)deltaMax); + Vector2D p3 = new((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax); + + return new Triangle(p1, p2, p3); } - public List Lines - { - get - { - List lines = new List(Vertices.Count - 1); - GetLinesNonAlloc(lines); - return lines; - } - } - - public void GetLinesNonAlloc(IList lines) + public static void GetLines(Shape shape, IList lines) { lines.Clear(); - for (int i = 0; i < Vertices.Count - 1; i++) - lines.Add(new(Vertices[i], Vertices[i + 1])); - lines.Add(new(Vertices[^1], Vertices[0])); + for (int i = 0; i < shape.Vertices.Count - 1; i++) + lines.Add(new(shape.Vertices[i], shape.Vertices[i + 1])); + lines.Add(new(shape.Vertices[^1], shape.Vertices[0])); } - public bool ApproximatelyEquals(Shape other) + public static List GetLines(Shape shape) { - if (Vertices.Count != other.Vertices.Count) + List lines = new(shape.Vertices.Count - 1); + GetLines(shape, lines); + return lines; + } + + public static bool ApproximatelyEquals(Shape left, Shape right) + { + if (left.Vertices.Count != right.Vertices.Count) return false; - for (int i = 0; i < Vertices.Count; i++) - if (!Vertices[i].ApproximatelyEquals(other.Vertices[i])) + for (int i = 0; i < left.Vertices.Count; i++) + if (!left.Vertices[i].ApproximatelyEquals(right.Vertices[i])) return false; return true; } } + +public static class ShapeExtensions +{ + public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape); + public static void ToLines(this Shape shape, IList lines) => Shape.GetLines(shape, lines); + public static List ToLines(this Shape shape) => Shape.GetLines(shape); + + public static bool ApproximatelyEquals(this Shape left, Shape right) => Shape.ApproximatelyEquals(left, right); +} From 0d29ab066fed4310cbd97aa846bf6f500fae3b32 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:18:31 +0300 Subject: [PATCH 09/93] refactor: Added Static Methods Back --- Engine.Physics2D/Physics2D.cs | 77 +++++++++++++++++++++++++++++ Engine.Physics2D/PhysicsMath.cs | 51 ------------------- Engine.Physics2D/Primitives/Math.cs | 9 ---- 3 files changed, 77 insertions(+), 60 deletions(-) create mode 100644 Engine.Physics2D/Physics2D.cs delete mode 100644 Engine.Physics2D/Primitives/Math.cs diff --git a/Engine.Physics2D/Physics2D.cs b/Engine.Physics2D/Physics2D.cs new file mode 100644 index 0000000..2c994d7 --- /dev/null +++ b/Engine.Physics2D/Physics2D.cs @@ -0,0 +1,77 @@ +using System; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Physics2D; +using Syntriax.Engine.Physics2D.Primitives; + +namespace Engine.Physics2D; + +public static partial class Physics2D +{ + public const float RadianToDegree = 57.29577866666166f; + public const float DegreeToRadian = 0.01745329277777778f; + + public static bool Overlaps(this Circle left, Circle right) + { + float distanceSquared = left.Position.FromTo(right.Position).LengthSquared(); + float radiusSumSquared = left.RadiusSquared + right.RadiusSquared; + + return distanceSquared < radiusSumSquared; + } + + public static bool Overlaps(this Circle left, Circle right, out Vector2D normal, out float depth) + { + Vector2D distanceVector = left.Position.FromTo(right.Position); + float distanceSquared = distanceVector.LengthSquared(); + float radiusSumSquared = left.RadiusSquared + right.RadiusSquared; + bool isOverlapping = distanceSquared < radiusSumSquared; + + depth = 0f; + normal = distanceVector.Normalized; + + if (isOverlapping) + depth = MathF.Sqrt(radiusSumSquared - distanceSquared); + + return isOverlapping; + } + + public static bool Overlaps(this Circle circle, Vector2D point) => circle.Position.FromTo(point).LengthSquared() <= circle.RadiusSquared; + public static bool Overlaps(this Circle circle, Vector2D point, out Vector2D normal, out float depth) + { + Vector2D distanceVector = circle.Position.FromTo(point); + float distanceSquared = distanceVector.LengthSquared(); + float radiusSquared = circle.RadiusSquared; + bool isOverlapping = distanceSquared < radiusSquared; + + depth = 0f; + normal = distanceVector.Normalized; + + if (isOverlapping) + depth = MathF.Sqrt(radiusSquared - distanceSquared); + + return isOverlapping; + } + + public static bool Overlaps(this AABB aabb, Vector2D point) + => point.X >= aabb.LowerBoundary.X && point.X <= aabb.UpperBoundary.X && + point.Y >= aabb.LowerBoundary.Y && point.Y <= aabb.UpperBoundary.Y; + + public static bool Overlaps(this AABB left, AABB right) + => left.LowerBoundary.X <= right.UpperBoundary.X && left.UpperBoundary.X >= right.LowerBoundary.X && + left.LowerBoundary.Y <= right.UpperBoundary.Y && left.UpperBoundary.Y >= right.LowerBoundary.Y; + + public static bool Overlaps(Triangle triangle, Vector2D point) + { + float originalTriangleArea = triangle.Area; + + float pointTriangleArea1 = new Triangle(point, triangle.B, triangle.C).Area; + float pointTriangleArea2 = new Triangle(triangle.A, point, triangle.C).Area; + float pointTriangleArea3 = new Triangle(triangle.A, triangle.B, point).Area; + + float pointTriangleAreasSum = pointTriangleArea1 + pointTriangleArea2 + pointTriangleArea3; + + return originalTriangleArea.ApproximatelyEquals(pointTriangleAreasSum, float.Epsilon * 3f); + } + + public static bool LaysOn(this Vector2D point, Line line) => Line.Intersects(line, point); +} diff --git a/Engine.Physics2D/PhysicsMath.cs b/Engine.Physics2D/PhysicsMath.cs index 7ccd28c..bef5107 100644 --- a/Engine.Physics2D/PhysicsMath.cs +++ b/Engine.Physics2D/PhysicsMath.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; -using Microsoft.Xna.Framework; using Syntriax.Engine.Core; using Syntriax.Engine.Physics2D.Primitives; @@ -9,52 +8,6 @@ namespace Syntriax.Engine.Physics2D; public static class PhysicsMath { - public static Vector2D Scale(this Vector2D original, Vector2D scale) - => new Vector2D(original.X * scale.X, original.Y * scale.Y); - - public static Triangle ToSuperTriangle(this IList vertices) - { - float minX = float.MaxValue, minY = float.MaxValue; - float maxX = float.MinValue, maxY = float.MinValue; - - foreach (Vector2D point in vertices) - { - minX = MathF.Min(minX, point.X); - minY = MathF.Min(minY, point.Y); - maxX = MathF.Max(maxX, point.X); - maxY = MathF.Max(maxY, point.Y); - } - - float dx = maxX - minX; - float dy = maxY - minY; - float deltaMax = MathF.Max(dx, dy); - float midX = (minX + maxX) / 2; - float midY = (minY + maxY) / 2; - - Vector2D p1 = new Vector2D((float)midX - 20f * (float)deltaMax, (float)midY - (float)deltaMax); - Vector2D p2 = new Vector2D((float)midX, (float)midY + 20 * (float)deltaMax); - Vector2D p3 = new Vector2D((float)midX + 20 * (float)deltaMax, (float)midY - (float)deltaMax); - - return new Triangle(p1, p2, p3); - } - - public static IList ToLines(this IList vertices) - { - List lines = new List(vertices.Count - 1); - ToLines(vertices, lines); - return lines; - } - - public static void ToLines(this IList vertices, IList lines) - { - lines.Clear(); - for (int i = 0; i < vertices.Count - 1; i++) - lines.Add(new(vertices[i], vertices[i + 1])); - lines.Add(new(vertices[^1], vertices[0])); - } - - public static bool LaysOn(this Vector2D point, Line line) - => line.Resolve(point.X).ApproximatelyEquals(point); // Given three collinear points p, q, r, the function checks if @@ -92,10 +45,6 @@ public static class PhysicsMath public static bool ApproximatelyEquals(this float a, float b) => ApproximatelyEquals(a, b, float.Epsilon); - public static bool ApproximatelyEquals(this Vector2 a, Vector2 b) - => ApproximatelyEquals(a, b, float.Epsilon); - public static bool ApproximatelyEquals(this Vector2 a, Vector2 b, float epsilon) - => ApproximatelyEquals(a.X, b.X, epsilon) && ApproximatelyEquals(a.Y, b.Y, epsilon); public static bool ApproximatelyEquals(this Vector2D a, Vector2D b) => ApproximatelyEquals(a, b, float.Epsilon); public static bool ApproximatelyEquals(this Vector2D a, Vector2D b, float epsilon) diff --git a/Engine.Physics2D/Primitives/Math.cs b/Engine.Physics2D/Primitives/Math.cs deleted file mode 100644 index 79875d4..0000000 --- a/Engine.Physics2D/Primitives/Math.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Syntriax.Engine.Physics2D.Primitives; - -public static class Math -{ - public const float RadianToDegree = 57.29577866666166f; - public const float DegreeToRadian = 0.01745329277777778f; - - public static float Clamp(float value, float min, float max) => (value < min) ? min : (value > max) ? max : value; -} From 5ed7ccdded04e075dca04ef93f3c75aefa338566 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 23 Jan 2024 19:19:23 +0300 Subject: [PATCH 10/93] fix: Build Errors --- Engine.Physics2D/Collider2DAABBBehaviour.cs | 2 +- Engine.Physics2D/CollisionInformation.cs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Engine.Physics2D/Collider2DAABBBehaviour.cs b/Engine.Physics2D/Collider2DAABBBehaviour.cs index 79fc368..8073e64 100644 --- a/Engine.Physics2D/Collider2DAABBBehaviour.cs +++ b/Engine.Physics2D/Collider2DAABBBehaviour.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; - +using Engine.Physics2D; using Syntriax.Engine.Core; using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Physics2D.Abstract; diff --git a/Engine.Physics2D/CollisionInformation.cs b/Engine.Physics2D/CollisionInformation.cs index 24d561d..7f811e0 100644 --- a/Engine.Physics2D/CollisionInformation.cs +++ b/Engine.Physics2D/CollisionInformation.cs @@ -1,9 +1,10 @@ -using Microsoft.Xna.Framework; + +using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D; public record CollisionInformation ( - Vector2 Normal, - Vector2 ContactPosition + Vector2D Normal, + Vector2D ContactPosition ); From 909b93088ce4fa8f508768858f079d5e1c434db2 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:02:04 +0300 Subject: [PATCH 11/93] feat: Shape Index Accessor --- Engine.Physics2D/Primitives/Shape.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 1091856..231e114 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -7,6 +7,8 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Shape(IList Vertices) { + public Vector2D this[int index] => Vertices[index]; + public static Triangle GetSuperTriangle(Shape shape) { float minX = float.MaxValue, minY = float.MaxValue; From 09c63b65df19c67883526746a77a4f96cfe990ec Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:03:41 +0300 Subject: [PATCH 12/93] fix: Scale Calling FromTo Instead of Scale --- Engine.Core/Extensions/Vector2DExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Core/Extensions/Vector2DExtensions.cs b/Engine.Core/Extensions/Vector2DExtensions.cs index 800510e..6207043 100644 --- a/Engine.Core/Extensions/Vector2DExtensions.cs +++ b/Engine.Core/Extensions/Vector2DExtensions.cs @@ -9,7 +9,7 @@ public static class Vector2DExtensions public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal); public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector); public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to); - public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.FromTo(vector, scale); + public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale); public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right); public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right); From 51b1f79a5d526e307782b8136a73fce90938d745 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:05:29 +0300 Subject: [PATCH 13/93] refactor: int to Index for Shape Accessor --- Engine.Physics2D/Primitives/Shape.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 231e114..3d1dc87 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -7,7 +7,7 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Shape(IList Vertices) { - public Vector2D this[int index] => Vertices[index]; + public Vector2D this[Index index] => Vertices[index]; public static Triangle GetSuperTriangle(Shape shape) { From 1acecdf3ce5be9a7d1e42f642432dfee1330c86c Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:07:54 +0300 Subject: [PATCH 14/93] feat: Vector2.Rotate --- Engine.Core/Extensions/Vector2DExtensions.cs | 1 + Engine.Core/Vector2D.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Engine.Core/Extensions/Vector2DExtensions.cs b/Engine.Core/Extensions/Vector2DExtensions.cs index 6207043..4f6769d 100644 --- a/Engine.Core/Extensions/Vector2DExtensions.cs +++ b/Engine.Core/Extensions/Vector2DExtensions.cs @@ -11,6 +11,7 @@ public static class Vector2DExtensions public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to); public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale); + public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian); public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right); public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right); public static Vector2D Clamp(this Vector2D vector, Vector2D min, Vector2D max) => Vector2D.Clamp(vector, min, max); diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index bd15af2..4a4fb11 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -33,6 +33,7 @@ public record Vector2D(float X, float Y) public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from; public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y); + public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(radianAngle) * vector.Y); public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y); public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y); public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y)); From 83d8a03be3d8db7957e866153fa4fd1c22a0dd50 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:10:28 +0300 Subject: [PATCH 15/93] feat: Basic Operation Methods Vector2D.Invert Vector2D.Add Vector2D.Subtract Vector2D.Multiply Vector2D.Subdivide --- Engine.Core/Extensions/Vector2DExtensions.cs | 7 +++++++ Engine.Core/Vector2D.cs | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/Engine.Core/Extensions/Vector2DExtensions.cs b/Engine.Core/Extensions/Vector2DExtensions.cs index 4f6769d..30d37d6 100644 --- a/Engine.Core/Extensions/Vector2DExtensions.cs +++ b/Engine.Core/Extensions/Vector2DExtensions.cs @@ -4,8 +4,15 @@ public static class Vector2DExtensions { public static float Length(this Vector2D vector) => Vector2D.Length(vector); public static float LengthSquared(this Vector2D vector) => Vector2D.LengthSquared(vector); + public static float Distance(this Vector2D from, Vector2D to) => Vector2D.Distance(from, to); + public static Vector2D Invert(this Vector2D vector) => Vector2D.Invert(vector); + public static Vector2D Add(this Vector2D vector, Vector2D vectorToAdd) => Vector2D.Add(vector, vectorToAdd); + public static Vector2D Subtract(this Vector2D vector, Vector2D vectorToSubtract) => Vector2D.Subtract(vector, vectorToSubtract); + public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value); + public static Vector2D Subdivide(this Vector2D vector, float value) => Vector2D.Subdivide(vector, value); + public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal); public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector); public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to); diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index 4a4fb11..d24f199 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -28,6 +28,12 @@ public record Vector2D(float X, float Y) public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to)); + public static Vector2D Invert(Vector2D vector) => new(0f - vector.X, 0f - vector.Y); + public static Vector2D Add(Vector2D left, Vector2D right) => left + right; + public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right; + public static Vector2D Multiply(Vector2D vector, float value) => vector * value; + public static Vector2D Subdivide(Vector2D vector, float value) => vector / value; + public static Vector2D Normalize(Vector2D vector) => vector / Length(vector); public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal; public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from; From c3bcaaee06cca1fd28c9fbc4b9b110e61e8e267c Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:11:16 +0300 Subject: [PATCH 16/93] refactor: Vector2D.Invert to Operator --- Engine.Core/Vector2D.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index d24f199..4400723 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -28,7 +28,7 @@ public record Vector2D(float X, float Y) public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to)); - public static Vector2D Invert(Vector2D vector) => new(0f - vector.X, 0f - vector.Y); + public static Vector2D Invert(Vector2D vector) => -vector; public static Vector2D Add(Vector2D left, Vector2D right) => left + right; public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right; public static Vector2D Multiply(Vector2D vector, float value) => vector * value; From e5732f0ac57886f1b127e154d52b6a9655d7bf85 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:11:58 +0300 Subject: [PATCH 17/93] fix: Build Error Caused by Parameter Name --- Engine.Core/Vector2D.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index 4400723..cc3f9e8 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -39,7 +39,7 @@ public record Vector2D(float X, float Y) public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from; public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y); - public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(radianAngle) * vector.Y); + public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y); public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y); public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y); public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y)); From 87bf47eefdc0805c86501618e9548a76aa6c6a38 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:22:04 +0300 Subject: [PATCH 18/93] feat: Math --- Engine.Core/Math.cs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Engine.Core/Math.cs diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs new file mode 100644 index 0000000..c77c5d6 --- /dev/null +++ b/Engine.Core/Math.cs @@ -0,0 +1,31 @@ +using System; + +namespace Syntriax.Engine.Core; + +public static class Math +{ + public const float RadianToDegree = 57.29577866666166f; + public const float DegreeToRadian = 0.01745329277777778f; + + public const float E = 2.718281828459045f; + public const float PI = 3.1415926535897932f; + public const float Tau = 6.283185307179586f; + + public static float Abs(float x) => MathF.Abs(x); + public static float Acos(float x) => MathF.Acos(x); + public static float Asin(float x) => MathF.Asin(x); + public static float Atan2(float y, float x) => MathF.Atan2(y, x); + public static float Atanh(float x) => MathF.Atanh(x); + public static float Ceiling(float x) => MathF.Ceiling(x); + public static float CopySign(float x, float y) => MathF.CopySign(x, y); + public static float Floor(float x) => MathF.Floor(x); + public static float IEEERemainder(float x, float y) => MathF.IEEERemainder(x, y); + public static float Log(float x, float y) => MathF.Log(x, y); + public static float Max(float x, float y) => MathF.Max(x, y); + public static float MaxMagnitude(float x, float y) => MathF.MaxMagnitude(x, y); + public static float Min(float x, float y) => MathF.Min(x, y); + public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y); + public static float Pow(float x, float y) => MathF.Pow(x, y); + public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode); + public static float Truncate(float x) => MathF.Truncate(x); +} From b3d404bb6be5757c52e1bc769982b230199ed4a9 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:23:43 +0300 Subject: [PATCH 19/93] feat: TransformExtensions.TransformVector2D --- Engine.Core/Extensions/Abstract/TransformExtensions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Engine.Core/Extensions/Abstract/TransformExtensions.cs diff --git a/Engine.Core/Extensions/Abstract/TransformExtensions.cs b/Engine.Core/Extensions/Abstract/TransformExtensions.cs new file mode 100644 index 0000000..24244a3 --- /dev/null +++ b/Engine.Core/Extensions/Abstract/TransformExtensions.cs @@ -0,0 +1,9 @@ +namespace Syntriax.Engine.Core.Abstract; + +public static class TransformExtensions +{ + public static Vector2D TransformVector2D(this ITransform transform, Vector2D vector) + => vector.Scale(transform.Scale) + .Rotate(transform.Rotation * Math.DegreeToRadian) + .Add(transform.Position); +} From 4bf618251fffdfadac24f8165902b2acc94132f6 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:25:32 +0300 Subject: [PATCH 20/93] feat: Math.Clamp --- Engine.Core/Math.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs index c77c5d6..6e1ebea 100644 --- a/Engine.Core/Math.cs +++ b/Engine.Core/Math.cs @@ -16,6 +16,7 @@ public static class Math public static float Asin(float x) => MathF.Asin(x); public static float Atan2(float y, float x) => MathF.Atan2(y, x); public static float Atanh(float x) => MathF.Atanh(x); + public static float Clamp(float x, float min, float max) => (x < min) ? min : (x > max) ? max : x; public static float Ceiling(float x) => MathF.Ceiling(x); public static float CopySign(float x, float y) => MathF.CopySign(x, y); public static float Floor(float x) => MathF.Floor(x); From a60f79f12b4606a7aae406bcbdaf541b140eaeab Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:25:56 +0300 Subject: [PATCH 21/93] chore: Removed Physics2D Degree Constants --- Engine.Physics2D/Physics2D.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Engine.Physics2D/Physics2D.cs b/Engine.Physics2D/Physics2D.cs index 2c994d7..acf0fc3 100644 --- a/Engine.Physics2D/Physics2D.cs +++ b/Engine.Physics2D/Physics2D.cs @@ -8,9 +8,6 @@ namespace Engine.Physics2D; public static partial class Physics2D { - public const float RadianToDegree = 57.29577866666166f; - public const float DegreeToRadian = 0.01745329277777778f; - public static bool Overlaps(this Circle left, Circle right) { float distanceSquared = left.Position.FromTo(right.Position).LengthSquared(); From d40183db65da1f135306ac4c7353300b8e466d0a Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:29:11 +0300 Subject: [PATCH 22/93] feat: float & Vector2D.ApproximatelyEquals --- Engine.Core/Extensions/FloatExtensions.cs | 22 ++++++++++++++++++++ Engine.Core/Extensions/Vector2DExtensions.cs | 3 +++ Engine.Core/Vector2D.cs | 3 +++ 3 files changed, 28 insertions(+) create mode 100644 Engine.Core/Extensions/FloatExtensions.cs diff --git a/Engine.Core/Extensions/FloatExtensions.cs b/Engine.Core/Extensions/FloatExtensions.cs new file mode 100644 index 0000000..019bc39 --- /dev/null +++ b/Engine.Core/Extensions/FloatExtensions.cs @@ -0,0 +1,22 @@ +namespace Syntriax.Engine.Core; + +public static class FloatExtensions +{ + public static bool ApproximatelyEquals(this float a, float b) + => ApproximatelyEquals(a, b, float.Epsilon); + public static bool ApproximatelyEquals(this float a, float b, float epsilon) + { + if (a == b) + return true; + + const float floatNormal = (1 << 23) * float.Epsilon; + float absA = Math.Abs(a); + float absB = Math.Abs(b); + float diff = Math.Abs(a - b); + + if (a == 0.0f || b == 0.0f || diff < floatNormal) + return diff < (epsilon * floatNormal); + + return diff / Math.Min(absA + absB, float.MaxValue) < epsilon; + } +} diff --git a/Engine.Core/Extensions/Vector2DExtensions.cs b/Engine.Core/Extensions/Vector2DExtensions.cs index 30d37d6..f2a4c41 100644 --- a/Engine.Core/Extensions/Vector2DExtensions.cs +++ b/Engine.Core/Extensions/Vector2DExtensions.cs @@ -27,4 +27,7 @@ public static class Vector2DExtensions public static float Cross(this Vector2D left, Vector2D right) => Vector2D.Cross(left, right); public static float AngleBetween(this Vector2D left, Vector2D right) => Vector2D.Angle(left, right); public static float Dot(this Vector2D left, Vector2D right) => Vector2D.Dot(left, right); + + + public static bool ApproximatelyEquals(this Vector2D left, Vector2D right, float epsilon = float.Epsilon) => Vector2D.ApproximatelyEquals(left, right, epsilon); } diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index cc3f9e8..336fd4e 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -49,5 +49,8 @@ public record Vector2D(float X, float Y) public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right))); public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y; + public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon) + => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon); + public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})"; } From fbf9bd5832523c779b3a72cca2eaffd0f4ea7fcd Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:27:28 +0300 Subject: [PATCH 23/93] refactor: Removed Old PhysicsMath Class --- Engine.Physics2D/PhysicsMath.cs | 67 --------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 Engine.Physics2D/PhysicsMath.cs diff --git a/Engine.Physics2D/PhysicsMath.cs b/Engine.Physics2D/PhysicsMath.cs deleted file mode 100644 index bef5107..0000000 --- a/Engine.Physics2D/PhysicsMath.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; - -using Syntriax.Engine.Core; -using Syntriax.Engine.Physics2D.Primitives; - -namespace Syntriax.Engine.Physics2D; - -public static class PhysicsMath -{ - - - // Given three collinear points p, q, r, the function checks if - // point q lies on line segment 'pr' - public static bool OnSegment(Vector2D p, Vector2D q, Vector2D r) - { - if (q.X <= MathF.Max(p.X, r.X) && q.X >= MathF.Min(p.X, r.X) && - q.Y <= MathF.Max(p.Y, r.Y) && q.Y >= MathF.Min(p.Y, r.Y)) - return true; - - return false; - } - - // To find orientation of ordered triplet (p, q, r). - // The function returns following values - // 0 --> p, q and r are collinear - // 1 --> Clockwise - // 2 --> Counterclockwise - public static int Orientation(Vector2D p, Vector2D q, Vector2D r) - { - // See https://www.geeksforgeeks.org/orientation-3-ordered-points/ - // for details of below formula. - float val = (q.Y - p.Y) * (r.X - q.X) - - (q.X - p.X) * (r.Y - q.Y); - - if (val == 0) return 0; // collinear - - return (val > 0) ? 1 : 2; // clock or counterclock wise - } - - public static float IntersectionParameterT(Vector2D p0, Vector2D p1, Vector2D q0, Vector2D q1) - => ((q0.X - p0.X) * (p1.Y - p0.Y) - (q0.Y - p0.Y) * (p1.X - p0.X)) / - ((q1.Y - q0.Y) * (p1.X - p0.X) - (q1.X - q0.X) * (p1.Y - p0.Y)); - - - public static bool ApproximatelyEquals(this float a, float b) - => ApproximatelyEquals(a, b, float.Epsilon); - public static bool ApproximatelyEquals(this Vector2D a, Vector2D b) - => ApproximatelyEquals(a, b, float.Epsilon); - public static bool ApproximatelyEquals(this Vector2D a, Vector2D b, float epsilon) - => ApproximatelyEquals(a.X, b.X, epsilon) && ApproximatelyEquals(a.Y, b.Y, epsilon); - public static bool ApproximatelyEquals(this float a, float b, float epsilon) - { - if (a == b) - return true; - - const float floatNormal = (1 << 23) * float.Epsilon; - float absA = MathF.Abs(a); - float absB = MathF.Abs(b); - float diff = MathF.Abs(a - b); - - if (a == 0.0f || b == 0.0f || diff < floatNormal) - return diff < (epsilon * floatNormal); - - return diff / MathF.Min(absA + absB, float.MaxValue) < epsilon; - } -} From 77e1949f59c4515f6aea66de525d02f056569f69 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:28:01 +0300 Subject: [PATCH 24/93] refactor: Math Constants Now Use Each Other as References --- Engine.Core/Math.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs index 6e1ebea..ea6ba8d 100644 --- a/Engine.Core/Math.cs +++ b/Engine.Core/Math.cs @@ -4,12 +4,12 @@ namespace Syntriax.Engine.Core; public static class Math { - public const float RadianToDegree = 57.29577866666166f; - public const float DegreeToRadian = 0.01745329277777778f; + public const float RadianToDegree = 180f / PI; + public const float DegreeToRadian = PI / 180f; public const float E = 2.718281828459045f; public const float PI = 3.1415926535897932f; - public const float Tau = 6.283185307179586f; + public const float Tau = 2f * PI; public static float Abs(float x) => MathF.Abs(x); public static float Acos(float x) => MathF.Acos(x); From 0c3bf48d2c0b5fadef88f7af4d452ff160747d9e Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:31:53 +0300 Subject: [PATCH 25/93] feat: Vector2D.Orientation --- Engine.Core/Vector2D.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index 336fd4e..3f8292f 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -49,6 +49,23 @@ public record Vector2D(float X, float Y) public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right))); public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y; + /// + /// Finds the Orientation of 3 s + /// + /// 0 -> Collinear, 1 -> Clockwise, 2 -> Counterclockwise + public static int Orientation(Vector2D left, Vector2D middle, Vector2D right) + { + Vector2D leftToMiddle = left.FromTo(middle); + Vector2D middleToRight = middle.FromTo(right); + + float value = leftToMiddle.Y * middleToRight.X - + leftToMiddle.X * middleToRight.Y; + + if (value > 0) return 1; + if (value < 0) return 2; + return 0; + } + public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon) => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon); From 56b46b93eb4d9fd25d5506c993369f3706224578 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:42:18 +0300 Subject: [PATCH 26/93] fix: Build Errors --- Engine.Physics2D/Primitives/Line.cs | 25 ++++++++++++++------- Engine.Physics2D/Primitives/LineEquation.cs | 2 ++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Engine.Physics2D/Primitives/Line.cs b/Engine.Physics2D/Primitives/Line.cs index c23bd8a..85a981e 100644 --- a/Engine.Physics2D/Primitives/Line.cs +++ b/Engine.Physics2D/Primitives/Line.cs @@ -107,18 +107,27 @@ public record Line(Vector2D From, Vector2D To) public static bool Intersects(Line left, Line right) { - int o1 = PhysicsMath.Orientation(left.From, left.To, right.From); - int o2 = PhysicsMath.Orientation(left.From, left.To, right.To); - int o3 = PhysicsMath.Orientation(right.From, right.To, left.From); - int o4 = PhysicsMath.Orientation(right.From, right.To, left.To); + int o1 = Vector2D.Orientation(left.From, left.To, right.From); + int o2 = Vector2D.Orientation(left.From, left.To, right.To); + int o3 = Vector2D.Orientation(right.From, right.To, left.From); + int o4 = Vector2D.Orientation(right.From, right.To, left.To); if (o1 != o2 && o3 != o4) return true; - if (o1 == 0 && PhysicsMath.OnSegment(left.From, right.From, left.To)) return true; - if (o2 == 0 && PhysicsMath.OnSegment(left.From, right.To, left.To)) return true; - if (o3 == 0 && PhysicsMath.OnSegment(right.From, left.From, right.To)) return true; - if (o4 == 0 && PhysicsMath.OnSegment(right.From, left.To, right.To)) return true; + if (o1 == 0 && OnSegment(left, right.From)) return true; + if (o2 == 0 && OnSegment(left, right.To)) return true; + if (o3 == 0 && OnSegment(right, left.From)) return true; + if (o4 == 0 && OnSegment(right, left.To)) return true; + + return false; + } + + public static bool OnSegment(Line line, Vector2D point) + { + if (point.X <= MathF.Max(line.From.X, line.To.X) && point.X >= MathF.Min(line.From.X, line.To.X) && + point.Y <= MathF.Max(line.From.Y, line.To.Y) && point.Y >= MathF.Min(line.From.Y, line.To.Y)) + return true; return false; } diff --git a/Engine.Physics2D/Primitives/LineEquation.cs b/Engine.Physics2D/Primitives/LineEquation.cs index 8c80782..06ff657 100644 --- a/Engine.Physics2D/Primitives/LineEquation.cs +++ b/Engine.Physics2D/Primitives/LineEquation.cs @@ -1,3 +1,5 @@ +using Syntriax.Engine.Core; + namespace Syntriax.Engine.Physics2D.Primitives; public record LineEquation(float Slope, float OffsetY) From 468615e4cbeb93b63cd274ef5433eb3bf1a3c530 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:46:31 +0300 Subject: [PATCH 27/93] feat: AABB.FromVectors --- Engine.Physics2D/Primitives/AABB.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Engine.Physics2D/Primitives/AABB.cs b/Engine.Physics2D/Primitives/AABB.cs index 5ffef2b..3d54e17 100644 --- a/Engine.Physics2D/Primitives/AABB.cs +++ b/Engine.Physics2D/Primitives/AABB.cs @@ -1,14 +1,36 @@ +using System.Collections.Generic; + using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) { + public static AABB FromVector2Ds(IList vectors) + { + if (vectors.Count < 2) + throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items."); + + Vector2D lowerBoundary = vectors[0]; + Vector2D upperBoundary = vectors[0]; + + for (int i = 1; i < vectors.Count; i++) + { + Vector2D vector = vectors[i]; + lowerBoundary = Vector2D.Min(lowerBoundary, vector); + upperBoundary = Vector2D.Max(upperBoundary, vector); + } + + return new(lowerBoundary, upperBoundary); + } + public static bool ApproximatelyEquals(AABB left, AABB right) => left.LowerBoundary.ApproximatelyEquals(right.LowerBoundary) && left.UpperBoundary.ApproximatelyEquals(right.UpperBoundary); } public static class AABBExtensions { + public static AABB ToAABB(this IList vectors) => AABB.FromVector2Ds(vectors); + public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right); } From bfab35c27ec25c265d3ee63efb5ed90109e79cc1 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:50:26 +0300 Subject: [PATCH 28/93] feat: Shape IEnumerable --- Engine.Physics2D/Primitives/Shape.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 3d1dc87..dbd3987 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -1,11 +1,12 @@ using System; +using System.Collections; using System.Collections.Generic; using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; -public record Shape(IList Vertices) +public record Shape(IList Vertices) : IEnumerable { public Vector2D this[Index index] => Vertices[index]; @@ -61,6 +62,9 @@ public record Shape(IList Vertices) return true; } + + public IEnumerator GetEnumerator() => Vertices.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Vertices.GetEnumerator(); } public static class ShapeExtensions From 326bcfca61f57937b67472bc12c10036429082d4 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 12:58:51 +0300 Subject: [PATCH 29/93] fix: AABB.FromVectors --- Engine.Physics2D/Primitives/AABB.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Engine.Physics2D/Primitives/AABB.cs b/Engine.Physics2D/Primitives/AABB.cs index 3d54e17..6c48d36 100644 --- a/Engine.Physics2D/Primitives/AABB.cs +++ b/Engine.Physics2D/Primitives/AABB.cs @@ -6,21 +6,23 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) { - public static AABB FromVector2Ds(IList vectors) + public static AABB FromVectors(IEnumerable vectors) { - if (vectors.Count < 2) - throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items."); + int counter = 0; - Vector2D lowerBoundary = vectors[0]; - Vector2D upperBoundary = vectors[0]; + Vector2D lowerBoundary = new(float.MaxValue, float.MaxValue); + Vector2D upperBoundary = new(float.MinValue, float.MinValue); - for (int i = 1; i < vectors.Count; i++) + foreach (Vector2D vector in vectors) { - Vector2D vector = vectors[i]; lowerBoundary = Vector2D.Min(lowerBoundary, vector); upperBoundary = Vector2D.Max(upperBoundary, vector); + counter++; } + if (counter < 2) + throw new System.ArgumentException($"Parameter {nameof(vectors)} must have at least 2 items."); + return new(lowerBoundary, upperBoundary); } @@ -30,7 +32,7 @@ public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) public static class AABBExtensions { - public static AABB ToAABB(this IList vectors) => AABB.FromVector2Ds(vectors); + public static AABB ToAABB(this IEnumerable vectors) => AABB.FromVectors(vectors); public static bool ApproximatelyEquals(this AABB left, AABB right) => AABB.ApproximatelyEquals(left, right); } From 08b31d9db1d339b53584cd73af5bbd54b51cf54e Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 18:34:12 +0300 Subject: [PATCH 30/93] feat: Vector2D.Abs --- Engine.Core/Extensions/Vector2DExtensions.cs | 1 + Engine.Core/Vector2D.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Engine.Core/Extensions/Vector2DExtensions.cs b/Engine.Core/Extensions/Vector2DExtensions.cs index f2a4c41..2980a76 100644 --- a/Engine.Core/Extensions/Vector2DExtensions.cs +++ b/Engine.Core/Extensions/Vector2DExtensions.cs @@ -13,6 +13,7 @@ public static class Vector2DExtensions public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value); public static Vector2D Subdivide(this Vector2D vector, float value) => Vector2D.Subdivide(vector, value); + public static Vector2D Abs(Vector2D vector) => Vector2D.Abs(vector); public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal); public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector); public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to); diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index 3f8292f..967ba4d 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -34,6 +34,7 @@ public record Vector2D(float X, float Y) public static Vector2D Multiply(Vector2D vector, float value) => vector * value; public static Vector2D Subdivide(Vector2D vector, float value) => vector / value; + public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y)); public static Vector2D Normalize(Vector2D vector) => vector / Length(vector); public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal; public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from; From da67f4559b2597223236b03f13d7c74812f50a6d Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 18:35:25 +0300 Subject: [PATCH 31/93] fix: Forgotten this keyword on Vector2DExtensions.Abs --- Engine.Core/Extensions/Vector2DExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Core/Extensions/Vector2DExtensions.cs b/Engine.Core/Extensions/Vector2DExtensions.cs index 2980a76..a8ddaa3 100644 --- a/Engine.Core/Extensions/Vector2DExtensions.cs +++ b/Engine.Core/Extensions/Vector2DExtensions.cs @@ -13,7 +13,7 @@ public static class Vector2DExtensions public static Vector2D Multiply(this Vector2D vector, float value) => Vector2D.Multiply(vector, value); public static Vector2D Subdivide(this Vector2D vector, float value) => Vector2D.Subdivide(vector, value); - public static Vector2D Abs(Vector2D vector) => Vector2D.Abs(vector); + public static Vector2D Abs(this Vector2D vector) => Vector2D.Abs(vector); public static Vector2D Reflect(this Vector2D vector, Vector2D normal) => Vector2D.Reflect(vector, normal); public static Vector2D Normalize(this Vector2D vector) => Vector2D.Normalize(vector); public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to); From 528649659d044609a81b6cd73aba196c65a3514d Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 18:37:43 +0300 Subject: [PATCH 32/93] feat: AABB Center, Size & HalfSize --- Engine.Physics2D/Primitives/AABB.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Engine.Physics2D/Primitives/AABB.cs b/Engine.Physics2D/Primitives/AABB.cs index 6c48d36..5da1cc1 100644 --- a/Engine.Physics2D/Primitives/AABB.cs +++ b/Engine.Physics2D/Primitives/AABB.cs @@ -6,6 +6,10 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) { + public Vector2D Center => (LowerBoundary + UpperBoundary) * .5f; + public Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs(); + public Vector2D SizeHalf => Size * .5f; + public static AABB FromVectors(IEnumerable vectors) { int counter = 0; From 3428fcc6ca759805c26f742f9b2ca820953c3ba3 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 18:40:12 +0300 Subject: [PATCH 33/93] perf: BehaviourController.GetBehaviours now uses Linq.Enumerable.Empty if None Found --- Engine.Core/BehaviourController.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Engine.Core/BehaviourController.cs b/Engine.Core/BehaviourController.cs index 98d4585..582276b 100644 --- a/Engine.Core/BehaviourController.cs +++ b/Engine.Core/BehaviourController.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Syntriax.Engine.Core.Abstract; @@ -55,17 +56,17 @@ public class BehaviourController : IBehaviourController public IList GetBehaviours() { - IList behaviours = new List(); + List? behaviours = null; foreach (var behaviourItem in this.behaviours) { if (behaviourItem is not T behaviour) continue; - behaviours ??= new List(); + behaviours ??= []; behaviours.Add(behaviour); } - return behaviours; + return behaviours ?? Enumerable.Empty().ToList(); } public void RemoveBehaviour(bool removeAll = false) where T : class, IBehaviour From 350ef030ac9da5e82167897b59dcff418a2521ae Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 18:44:26 +0300 Subject: [PATCH 34/93] refactor: Circle.Position to Circle.Center --- Engine.Physics2D/Primitives/Circle.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index 4c31175..9cd8073 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -2,13 +2,13 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; -public record Circle(Vector2D Position, float Radius) +public record Circle(Vector2D Center, float Radius) { public float RadiusSquared => Radius * Radius; public float Diameter => 2f * Radius; public static bool ApproximatelyEquals(Circle left, Circle right) - => left.Position.ApproximatelyEquals(right.Position) && left.Radius.ApproximatelyEquals(right.Radius); + => left.Center.ApproximatelyEquals(right.Center) && left.Radius.ApproximatelyEquals(right.Radius); } public static class CircleExtensions From ed15238dcdc094453c697a8f83d9308f6702dc21 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Wed, 24 Jan 2024 19:21:53 +0300 Subject: [PATCH 35/93] BREAKING CHANGE: New ICollider --- .../Abstract/ICircleCollider2D.cs | 9 +++ Engine.Physics2D/Abstract/ICollider2D.cs | 6 +- Engine.Physics2D/Abstract/IRigidBody2D.cs | 1 + Engine.Physics2D/Abstract/IShapeCollider2D.cs | 9 +++ Engine.Physics2D/Collider2DAABBBehaviour.cs | 67 ------------------- Engine.Physics2D/Collider2DCircleBehaviour.cs | 28 ++++++++ Engine.Physics2D/Collider2DShapeBehaviour.cs | 29 ++++++++ Engine.Physics2D/Physics2D.cs | 8 +-- Engine.Physics2D/Primitives/Circle.cs | 16 +++++ Engine.Physics2D/Primitives/Shape.cs | 24 +++++++ Engine.Physics2D/RigidBody2D.cs | 3 +- 11 files changed, 123 insertions(+), 77 deletions(-) create mode 100644 Engine.Physics2D/Abstract/ICircleCollider2D.cs create mode 100644 Engine.Physics2D/Abstract/IShapeCollider2D.cs delete mode 100644 Engine.Physics2D/Collider2DAABBBehaviour.cs create mode 100644 Engine.Physics2D/Collider2DCircleBehaviour.cs create mode 100644 Engine.Physics2D/Collider2DShapeBehaviour.cs diff --git a/Engine.Physics2D/Abstract/ICircleCollider2D.cs b/Engine.Physics2D/Abstract/ICircleCollider2D.cs new file mode 100644 index 0000000..2391e53 --- /dev/null +++ b/Engine.Physics2D/Abstract/ICircleCollider2D.cs @@ -0,0 +1,9 @@ +using Syntriax.Engine.Physics2D.Primitives; + +namespace Syntriax.Engine.Physics2D.Abstract; + +public interface ICircleCollider2D : ICollider2D +{ + Circle CircleLocal { get; set; } + Circle CircleWorld { get; } +} diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs index a88a19f..964180c 100644 --- a/Engine.Physics2D/Abstract/ICollider2D.cs +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; - using Syntriax.Engine.Core; using Syntriax.Engine.Core.Abstract; @@ -9,11 +7,9 @@ namespace Syntriax.Engine.Physics2D.Abstract; public interface ICollider2D : IBehaviour, IAssignableTransform { Action? OnCollisionPreResolve { get; set; } + Action? OnCollisionResolved { get; set; } IRigidBody2D? RigidBody2D { get; } - IList Vertices { get; } - - bool CheckCollision(Vector2D point); void Recalculate(); } diff --git a/Engine.Physics2D/Abstract/IRigidBody2D.cs b/Engine.Physics2D/Abstract/IRigidBody2D.cs index 820caa6..ef8a5c2 100644 --- a/Engine.Physics2D/Abstract/IRigidBody2D.cs +++ b/Engine.Physics2D/Abstract/IRigidBody2D.cs @@ -11,4 +11,5 @@ public interface IRigidBody2D : IBehaviour, IAssignableTransform float AngularVelocity { get; set; } float Mass { get; set; } + bool IsStatic { get; set; } } diff --git a/Engine.Physics2D/Abstract/IShapeCollider2D.cs b/Engine.Physics2D/Abstract/IShapeCollider2D.cs new file mode 100644 index 0000000..2cc5218 --- /dev/null +++ b/Engine.Physics2D/Abstract/IShapeCollider2D.cs @@ -0,0 +1,9 @@ +using Syntriax.Engine.Physics2D.Primitives; + +namespace Syntriax.Engine.Physics2D.Abstract; + +public interface IShapeCollider2D : ICollider2D +{ + Shape ShapeLocal { get; set; } + Shape ShapeWorld { get; } +} diff --git a/Engine.Physics2D/Collider2DAABBBehaviour.cs b/Engine.Physics2D/Collider2DAABBBehaviour.cs deleted file mode 100644 index 8073e64..0000000 --- a/Engine.Physics2D/Collider2DAABBBehaviour.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using Engine.Physics2D; -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Abstract; -using Syntriax.Engine.Physics2D.Abstract; -using Syntriax.Engine.Physics2D.Primitives; - -namespace Syntriax.Engine.Physics2D; - -public class Collider2DAABBBehaviour : BehaviourOverride, ICollider2D -{ - public AABB AABBLocal { get; set; } = null!; - public AABB AABBWorld { get; private set; } = null!; - - private IRigidBody2D? _rigidBody2D = null; - private List _vertices = new List(4); - - public IRigidBody2D? RigidBody2D - { - get - { - if (_rigidBody2D is null) - BehaviourController.TryGetBehaviour(out _rigidBody2D); - - return _rigidBody2D; - } - } - - public Action? OnCollisionPreResolve { get; set; } = null; - - public Action? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } - ITransform IAssignableTransform.Transform => Transform; - public bool Assign(ITransform transform) => GameObject.Assign(transform); - - public IList Vertices => _vertices; - - public bool CheckCollision(Vector2D point) - { - return AABBWorld.Overlaps(point); - } - - public void Recalculate() - { - AABBWorld = new AABB( - AABBLocal.LowerBoundary.Scale(Transform.Scale) + Transform.Position, - AABBLocal.UpperBoundary.Scale(Transform.Scale) + Transform.Position - ); - - Vertices.Clear(); - Vertices.Add(AABBWorld.LowerBoundary); - Vertices.Add(new Vector2D(AABBWorld.LowerBoundary.X, AABBWorld.UpperBoundary.Y)); - Vertices.Add(AABBWorld.UpperBoundary); - Vertices.Add(new Vector2D(AABBWorld.UpperBoundary.X, AABBWorld.LowerBoundary.Y)); - } - public Collider2DAABBBehaviour(Vector2D lowerBoundary, Vector2D upperBoundary) - { - AABBLocal = new AABB(lowerBoundary, upperBoundary); - AABBWorld = new AABB(lowerBoundary, upperBoundary); - } - - public Collider2DAABBBehaviour() - { - AABBLocal = new(Vector2D.Zero, Vector2D.Zero); - AABBWorld = new(Vector2D.Zero, Vector2D.Zero); - } -} diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs new file mode 100644 index 0000000..7a68e68 --- /dev/null +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -0,0 +1,28 @@ +using System; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Physics2D.Abstract; +using Syntriax.Engine.Physics2D.Primitives; + +namespace Syntriax.Engine.Physics2D; + +public class Collider2DCircleBehaviour : BehaviourOverride, ICircleCollider2D +{ + public Action? OnCollisionPreResolve { get; set; } = null; + public Action? OnCollisionResolved { get; set; } = null; + + public Action? OnTransformAssigned { get; set; } = null; + + + public Circle CircleWorld { get; protected set; } = new(Vector2D.Zero, 1f); + public Circle CircleLocal { get; set; } = new(Vector2D.Zero, 1f); + public IRigidBody2D? RigidBody2D { get; set; } = null; + + + ITransform IAssignableTransform.Transform => GameObject.Transform; + + public bool Assign(ITransform transform) => GameObject.Assign(transform); + + public virtual void Recalculate() => CircleWorld = Transform.TransformCircle(CircleLocal).Displace(Transform.Position); +} diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs new file mode 100644 index 0000000..159fa02 --- /dev/null +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -0,0 +1,29 @@ +using System; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Physics2D.Abstract; +using Syntriax.Engine.Physics2D.Primitives; + +namespace Syntriax.Engine.Physics2D; + +public class Collider2DShapeBehaviour : BehaviourOverride, IShapeCollider2D +{ + public Action? OnCollisionPreResolve { get; set; } = null; + public Action? OnCollisionResolved { get; set; } = null; + + public Action? OnTransformAssigned { get; set; } = null; + + + public Shape ShapeWorld => _shapeWorld; + public Shape ShapeLocal { get; set; } = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); + public IRigidBody2D? RigidBody2D { get; set; } = null; + + protected Shape _shapeWorld = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); + + ITransform IAssignableTransform.Transform => GameObject.Transform; + + public bool Assign(ITransform transform) => GameObject.Assign(transform); + + public virtual void Recalculate() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); +} diff --git a/Engine.Physics2D/Physics2D.cs b/Engine.Physics2D/Physics2D.cs index acf0fc3..a9f722b 100644 --- a/Engine.Physics2D/Physics2D.cs +++ b/Engine.Physics2D/Physics2D.cs @@ -10,7 +10,7 @@ public static partial class Physics2D { public static bool Overlaps(this Circle left, Circle right) { - float distanceSquared = left.Position.FromTo(right.Position).LengthSquared(); + float distanceSquared = left.Center.FromTo(right.Center).LengthSquared(); float radiusSumSquared = left.RadiusSquared + right.RadiusSquared; return distanceSquared < radiusSumSquared; @@ -18,7 +18,7 @@ public static partial class Physics2D public static bool Overlaps(this Circle left, Circle right, out Vector2D normal, out float depth) { - Vector2D distanceVector = left.Position.FromTo(right.Position); + Vector2D distanceVector = left.Center.FromTo(right.Center); float distanceSquared = distanceVector.LengthSquared(); float radiusSumSquared = left.RadiusSquared + right.RadiusSquared; bool isOverlapping = distanceSquared < radiusSumSquared; @@ -32,10 +32,10 @@ public static partial class Physics2D return isOverlapping; } - public static bool Overlaps(this Circle circle, Vector2D point) => circle.Position.FromTo(point).LengthSquared() <= circle.RadiusSquared; + public static bool Overlaps(this Circle circle, Vector2D point) => circle.Center.FromTo(point).LengthSquared() <= circle.RadiusSquared; public static bool Overlaps(this Circle circle, Vector2D point, out Vector2D normal, out float depth) { - Vector2D distanceVector = circle.Position.FromTo(point); + Vector2D distanceVector = circle.Center.FromTo(point); float distanceSquared = distanceVector.LengthSquared(); float radiusSquared = circle.RadiusSquared; bool isOverlapping = distanceSquared < radiusSquared; diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index 9cd8073..360fdf5 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -1,4 +1,5 @@ using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; @@ -7,11 +8,26 @@ public record Circle(Vector2D Center, float Radius) public float RadiusSquared => Radius * Radius; public float Diameter => 2f * Radius; + public static Circle SetCenter(Circle circle, Vector2D center) => new(center, circle.Radius); + public static Circle SetRadius(Circle circle, float radius) => new(circle.Center, radius); + + public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius); + + public static Circle TransformCircle(ITransform transform, Circle circle) + => new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.Magnitude); + public static bool ApproximatelyEquals(Circle left, Circle right) => left.Center.ApproximatelyEquals(right.Center) && left.Radius.ApproximatelyEquals(right.Radius); } public static class CircleExtensions { + public static Circle SetCenter(this Circle circle, Vector2D center) => Circle.SetCenter(circle, center); + public static Circle SetRadius(this Circle circle, float radius) => Circle.SetRadius(circle, radius); + + public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector); + + public static Circle TransformCircle(this ITransform transform, Circle circle) => TransformCircle(transform, circle); + public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right); } diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index dbd3987..08570d2 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; @@ -51,6 +52,26 @@ public record Shape(IList Vertices) : IEnumerable return lines; } + public static Shape TransformShape(Shape shape, ITransform transform) + { + List vertices = new(shape.Vertices.Count); + + int count = shape.Vertices.Count; + for (int i = 0; i < count; i++) + vertices.Add(transform.TransformVector2D(shape[i])); + + return new Shape(vertices); + } + + public static void TransformShape(Shape from, ITransform transform, ref Shape to) + { + to.Vertices.Clear(); + + int count = from.Vertices.Count; + for (int i = 0; i < count; i++) + to.Vertices.Add(transform.TransformVector2D(from[i])); + } + public static bool ApproximatelyEquals(Shape left, Shape right) { if (left.Vertices.Count != right.Vertices.Count) @@ -73,5 +94,8 @@ public static class ShapeExtensions public static void ToLines(this Shape shape, IList lines) => Shape.GetLines(shape, lines); public static List ToLines(this Shape shape) => Shape.GetLines(shape); + public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform); + public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to); + public static bool ApproximatelyEquals(this Shape left, Shape right) => Shape.ApproximatelyEquals(left, right); } diff --git a/Engine.Physics2D/RigidBody2D.cs b/Engine.Physics2D/RigidBody2D.cs index ed4fb4a..e2fe1da 100644 --- a/Engine.Physics2D/RigidBody2D.cs +++ b/Engine.Physics2D/RigidBody2D.cs @@ -15,7 +15,8 @@ public class RigidBody2D : BehaviourOverride, IRigidBody2D public Vector2D Velocity { get; set; } = Vector2D.Zero; public float AngularVelocity { get; set; } = 0f; - public float Mass { get; set; } = 0f; + public float Mass { get; set; } = 1f; + public bool IsStatic { get; set; } = false; ITransform IAssignableTransform.Transform => Transform; From 385defd8e61128532213b29bc602bfcee2313b45 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 12:42:16 +0300 Subject: [PATCH 36/93] refactor: Possible Bugs --- Engine.Physics2D/Collider2DCircleBehaviour.cs | 8 +++----- Engine.Physics2D/Collider2DShapeBehaviour.cs | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs index 7a68e68..8607e44 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -12,17 +12,15 @@ public class Collider2DCircleBehaviour : BehaviourOverride, ICircleCollider2D public Action? OnCollisionPreResolve { get; set; } = null; public Action? OnCollisionResolved { get; set; } = null; - public Action? OnTransformAssigned { get; set; } = null; - public Circle CircleWorld { get; protected set; } = new(Vector2D.Zero, 1f); public Circle CircleLocal { get; set; } = new(Vector2D.Zero, 1f); public IRigidBody2D? RigidBody2D { get; set; } = null; - ITransform IAssignableTransform.Transform => GameObject.Transform; - - public bool Assign(ITransform transform) => GameObject.Assign(transform); + ITransform IAssignableTransform.Transform => Transform; + Action? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } + bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); public virtual void Recalculate() => CircleWorld = Transform.TransformCircle(CircleLocal).Displace(Transform.Position); } diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index 159fa02..13c0cff 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -12,8 +12,6 @@ public class Collider2DShapeBehaviour : BehaviourOverride, IShapeCollider2D public Action? OnCollisionPreResolve { get; set; } = null; public Action? OnCollisionResolved { get; set; } = null; - public Action? OnTransformAssigned { get; set; } = null; - public Shape ShapeWorld => _shapeWorld; public Shape ShapeLocal { get; set; } = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); @@ -21,9 +19,9 @@ public class Collider2DShapeBehaviour : BehaviourOverride, IShapeCollider2D protected Shape _shapeWorld = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); - ITransform IAssignableTransform.Transform => GameObject.Transform; - - public bool Assign(ITransform transform) => GameObject.Assign(transform); + ITransform IAssignableTransform.Transform => Transform; + Action? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } + bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); public virtual void Recalculate() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); } From 24cbc8a267824dcb3d79f6e344e144b9fc4a549d Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 12:46:43 +0300 Subject: [PATCH 37/93] feat: New CollisionDetectionInformation --- Engine.Physics2D/CollisionDetectionInformation.cs | 12 ++++++++++++ Engine.Physics2D/CollisionInformation.cs | 10 ---------- 2 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 Engine.Physics2D/CollisionDetectionInformation.cs delete mode 100644 Engine.Physics2D/CollisionInformation.cs diff --git a/Engine.Physics2D/CollisionDetectionInformation.cs b/Engine.Physics2D/CollisionDetectionInformation.cs new file mode 100644 index 0000000..3274bf9 --- /dev/null +++ b/Engine.Physics2D/CollisionDetectionInformation.cs @@ -0,0 +1,12 @@ +using Syntriax.Engine.Core; +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public record CollisionDetectionInformation +( + ICollider2D Left, + ICollider2D Right, + Vector2D Normal, + float Penetration +); diff --git a/Engine.Physics2D/CollisionInformation.cs b/Engine.Physics2D/CollisionInformation.cs deleted file mode 100644 index 7f811e0..0000000 --- a/Engine.Physics2D/CollisionInformation.cs +++ /dev/null @@ -1,10 +0,0 @@ - -using Syntriax.Engine.Core; - -namespace Syntriax.Engine.Physics2D; - -public record CollisionInformation -( - Vector2D Normal, - Vector2D ContactPosition -); From 816f09fffe25bd133bc04e8a1df3de104485dab6 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 12:49:21 +0300 Subject: [PATCH 38/93] feat: Math.Sqrt --- Engine.Core/Math.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs index ea6ba8d..6762545 100644 --- a/Engine.Core/Math.cs +++ b/Engine.Core/Math.cs @@ -28,5 +28,6 @@ public static class Math public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y); public static float Pow(float x, float y) => MathF.Pow(x, y); public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode); + public static float Sqrt(float x) => MathF.Sqrt(x); public static float Truncate(float x) => MathF.Truncate(x); } From f7467a62ee56e7ad4d4f268cba0c0e133bb1ea9c Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 18:21:52 +0300 Subject: [PATCH 39/93] feat: Collision Detector Circles --- Engine.Physics2D/CollisionDetector.cs | 58 ++++++++++++++++++++++++++ Engine.Physics2D/ICollisionDetector.cs | 8 ++++ 2 files changed, 66 insertions(+) create mode 100644 Engine.Physics2D/CollisionDetector.cs create mode 100644 Engine.Physics2D/ICollisionDetector.cs diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs new file mode 100644 index 0000000..78cae49 --- /dev/null +++ b/Engine.Physics2D/CollisionDetector.cs @@ -0,0 +1,58 @@ +using Syntriax.Engine.Core; +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public class CollisionDetector : ICollisionDetector +{ + public bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation? collisionInformation) + where T1 : ICollider2D + where T2 : ICollider2D + { + collisionInformation = default; + if (left is IShapeCollider2D shapeColliderLeft) + { + if (right is IShapeCollider2D shapeColliderRight) + return DetectShapeShape(shapeColliderLeft, shapeColliderRight, out collisionInformation); + else if (right is ICircleCollider2D circleColliderRight) + return DetectShapeCircle(shapeColliderLeft, circleColliderRight, out collisionInformation); + } + else if (left is ICircleCollider2D circleColliderLeft) + { + if (right is IShapeCollider2D shapeColliderRight) + return DetectShapeCircle(shapeColliderRight, circleColliderLeft, out collisionInformation); + else if (right is ICircleCollider2D circleColliderRight) + return DetectCircleCircle(circleColliderLeft, circleColliderRight, out collisionInformation); + } + + return false; + } + + private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation) + { + throw new System.NotImplementedException(); + } + + private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation? collisionInformation) + { + throw new System.NotImplementedException(); + } + + private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation) + { + collisionInformation = default; + + Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center); + float distanceCircleCenterSquared = leftToRightCenter.MagnitudeSquared; + float radiusSumSquared = left.CircleWorld.RadiusSquared + right.CircleWorld.RadiusSquared; + + float circleSurfaceDistanceSquared = distanceCircleCenterSquared - radiusSumSquared; + + bool collision = circleSurfaceDistanceSquared <= 0f; + + if (collision) + collisionInformation = new(left, right, leftToRightCenter.Normalized, Math.Sqrt(circleSurfaceDistanceSquared)); + + return collision; + } +} diff --git a/Engine.Physics2D/ICollisionDetector.cs b/Engine.Physics2D/ICollisionDetector.cs new file mode 100644 index 0000000..3180126 --- /dev/null +++ b/Engine.Physics2D/ICollisionDetector.cs @@ -0,0 +1,8 @@ +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public interface ICollisionDetector +{ + bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation? collisionInformation) where T1 : ICollider2D where T2 : ICollider2D; +} From f5be49609bbd6b1407c7ec7c94b229b7af6c1be4 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 18:32:13 +0300 Subject: [PATCH 40/93] feat: NotNullWhen to TryDetect --- Engine.Physics2D/CollisionDetector.cs | 11 +++++++++-- Engine.Physics2D/ICollisionDetector.cs | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index 78cae49..648f9e9 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + using Syntriax.Engine.Core; using Syntriax.Engine.Physics2D.Abstract; @@ -5,7 +7,7 @@ namespace Syntriax.Engine.Physics2D; public class CollisionDetector : ICollisionDetector { - public bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation? collisionInformation) + public bool TryDetect(T1 left, T2 right, [NotNullWhen(true)] out CollisionDetectionInformation? collisionInformation) where T1 : ICollider2D where T2 : ICollider2D { @@ -20,7 +22,7 @@ public class CollisionDetector : ICollisionDetector else if (left is ICircleCollider2D circleColliderLeft) { if (right is IShapeCollider2D shapeColliderRight) - return DetectShapeCircle(shapeColliderRight, circleColliderLeft, out collisionInformation); + return DetectCircleShape(circleColliderLeft, shapeColliderRight, out collisionInformation); else if (right is ICircleCollider2D circleColliderRight) return DetectCircleCircle(circleColliderLeft, circleColliderRight, out collisionInformation); } @@ -28,6 +30,11 @@ public class CollisionDetector : ICollisionDetector return false; } + private bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation) + { + throw new System.NotImplementedException(); + } + private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation) { throw new System.NotImplementedException(); diff --git a/Engine.Physics2D/ICollisionDetector.cs b/Engine.Physics2D/ICollisionDetector.cs index 3180126..460e5bb 100644 --- a/Engine.Physics2D/ICollisionDetector.cs +++ b/Engine.Physics2D/ICollisionDetector.cs @@ -1,8 +1,10 @@ +using System.Diagnostics.CodeAnalysis; + using Syntriax.Engine.Physics2D.Abstract; namespace Syntriax.Engine.Physics2D; public interface ICollisionDetector { - bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation? collisionInformation) where T1 : ICollider2D where T2 : ICollider2D; + bool TryDetect(T1 left, T2 right, [NotNullWhen(returnValue: true)] out CollisionDetectionInformation? collisionInformation) where T1 : ICollider2D where T2 : ICollider2D; } From 0af1b11396e61dfa4c7c2d080a10d760a7786db2 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 18:35:24 +0300 Subject: [PATCH 41/93] feat: Test Collision Detection & Move --- Engine.Physics2D/PhysicsEngine2D.cs | 46 +++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 684bdeb..0d4c476 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -4,17 +4,16 @@ using System.Collections.Generic; using Syntriax.Engine.Core; using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Physics2D.Abstract; -using Syntriax.Engine.Physics2D.Primitives; namespace Syntriax.Engine.Physics2D; public class PhysicsEngine2D : IPhysicsEngine2D { - private List rigidBodies = new List(32); - private List colliders = new List(64); + private readonly List rigidBodies = new(32); + private readonly List colliders = new(64); private int _iterationCount = 1; - + private ICollisionDetector collisionDetector = new CollisionDetector(); public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } @@ -43,11 +42,32 @@ public class PhysicsEngine2D : IPhysicsEngine2D for (int iterationIndex = 0; iterationIndex < IterationCount; iterationIndex++) { - foreach (var rigidBody in rigidBodies) - StepRigidBody(rigidBody, intervalDeltaTime); + // Can Parallel + for (int i = 0; i < rigidBodies.Count; i++) + StepRigidBody(rigidBodies[i], intervalDeltaTime); + // Can Parallel foreach (var collider in colliders) collider.Recalculate(); + + // Can Parallel + for (int x = 0; x < colliders.Count; x++) + { + ICollider2D? colliderX = colliders[x]; + for (int y = 0; y < colliders.Count; y++) + { + ICollider2D? colliderY = colliders[y]; + + if (colliderX.RigidBody2D == colliderY.RigidBody2D && colliderY.RigidBody2D is null) + continue; + + if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation? information)) + { + information.Left.Transform.Position -= .5f * information.Normal * information.Penetration; + information.Right.Transform.Position += .5f * information.Normal * information.Penetration; + } + } + } } } @@ -57,6 +77,20 @@ public class PhysicsEngine2D : IPhysicsEngine2D rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime; } + private static void CacheRotations(List rigidBodies, List rotations) + { + rotations.Clear(); + for (int i = 0; i < rigidBodies.Count; i++) + rotations.Add(rigidBodies[i].Transform.Rotation); + } + + private static void CachePositions(List rigidBodies, List positions) + { + positions.Clear(); + for (int i = 0; i < rigidBodies.Count; i++) + positions.Add(rigidBodies[i].Transform.Position); + } + private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour) { if (behaviour is not ICollider2D collider2D) From 9e1f38897fe9fbb229631dc5886bb0fe79e56427 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 18:37:51 +0300 Subject: [PATCH 42/93] fix: Self Referencing Call --- Engine.Physics2D/Primitives/Circle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index 360fdf5..dd39b9f 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -27,7 +27,7 @@ public static class CircleExtensions public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector); - public static Circle TransformCircle(this ITransform transform, Circle circle) => TransformCircle(transform, circle); + public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle); public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right); } From 00b80f1a0108d2c51009723e7d845b2ba402aece Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 20:42:49 +0300 Subject: [PATCH 43/93] feat: Collider2DBehaviourBase --- Engine.Physics2D/Collider2DBehaviourBase.cs | 27 +++++++++++++++++++ Engine.Physics2D/Collider2DCircleBehaviour.cs | 16 ++--------- Engine.Physics2D/Collider2DShapeBehaviour.cs | 17 ++---------- 3 files changed, 31 insertions(+), 29 deletions(-) create mode 100644 Engine.Physics2D/Collider2DBehaviourBase.cs diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBehaviourBase.cs new file mode 100644 index 0000000..c796770 --- /dev/null +++ b/Engine.Physics2D/Collider2DBehaviourBase.cs @@ -0,0 +1,27 @@ +using System; + +using Syntriax.Engine.Core; +using Syntriax.Engine.Core.Abstract; +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D +{ + public Action? OnCollisionPreResolve { get; set; } = null; + public Action? OnCollisionResolved { get; set; } = null; + + + protected IRigidBody2D? _rigidBody2D = null; + + + public IRigidBody2D? RigidBody2D => _rigidBody2D; + + ITransform IAssignableTransform.Transform => Transform; + Action? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } + bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); + + public abstract void Recalculate(); + + protected override void OnInitialize() => BehaviourController.TryGetBehaviour(out _rigidBody2D); +} diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs index 8607e44..1995f49 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -1,26 +1,14 @@ -using System; - using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Physics2D.Abstract; using Syntriax.Engine.Physics2D.Primitives; namespace Syntriax.Engine.Physics2D; -public class Collider2DCircleBehaviour : BehaviourOverride, ICircleCollider2D +public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollider2D { - public Action? OnCollisionPreResolve { get; set; } = null; - public Action? OnCollisionResolved { get; set; } = null; - - public Circle CircleWorld { get; protected set; } = new(Vector2D.Zero, 1f); public Circle CircleLocal { get; set; } = new(Vector2D.Zero, 1f); - public IRigidBody2D? RigidBody2D { get; set; } = null; - ITransform IAssignableTransform.Transform => Transform; - Action? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } - bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); - - public virtual void Recalculate() => CircleWorld = Transform.TransformCircle(CircleLocal).Displace(Transform.Position); + public override void Recalculate() => CircleWorld = Transform.TransformCircle(CircleLocal).Displace(Transform.Position); } diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index 13c0cff..d16950b 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -1,27 +1,14 @@ -using System; - -using Syntriax.Engine.Core; -using Syntriax.Engine.Core.Abstract; using Syntriax.Engine.Physics2D.Abstract; using Syntriax.Engine.Physics2D.Primitives; namespace Syntriax.Engine.Physics2D; -public class Collider2DShapeBehaviour : BehaviourOverride, IShapeCollider2D +public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D { - public Action? OnCollisionPreResolve { get; set; } = null; - public Action? OnCollisionResolved { get; set; } = null; - - public Shape ShapeWorld => _shapeWorld; public Shape ShapeLocal { get; set; } = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); - public IRigidBody2D? RigidBody2D { get; set; } = null; protected Shape _shapeWorld = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); - ITransform IAssignableTransform.Transform => Transform; - Action? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } - bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); - - public virtual void Recalculate() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); + public override void Recalculate() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); } From 601d15fa45228cebd8401629e09cce31968900cb Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 21:52:03 +0300 Subject: [PATCH 44/93] feat: Math.Sqr --- Engine.Core/Math.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs index 6762545..7cec55b 100644 --- a/Engine.Core/Math.cs +++ b/Engine.Core/Math.cs @@ -28,6 +28,7 @@ public static class Math public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y); public static float Pow(float x, float y) => MathF.Pow(x, y); public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode); + public static float Sqr(float x) => x * x; public static float Sqrt(float x) => MathF.Sqrt(x); public static float Truncate(float x) => MathF.Truncate(x); } From 3b83be695c31703eab2941ad5483b5c4685b8ef5 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 21:52:40 +0300 Subject: [PATCH 45/93] fix: Circle Collider2D Recalculate Wrong Calculation --- Engine.Physics2D/Collider2DCircleBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs index 1995f49..9d79fad 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -10,5 +10,5 @@ public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollide public Circle CircleLocal { get; set; } = new(Vector2D.Zero, 1f); - public override void Recalculate() => CircleWorld = Transform.TransformCircle(CircleLocal).Displace(Transform.Position); + public override void Recalculate() => CircleWorld = Transform.TransformCircle(CircleLocal); } From e7ca96e2e2a3cd2c0673efdf6357c2eba5b18a53 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 21:52:56 +0300 Subject: [PATCH 46/93] fix: Collider2D Not Registering Rigidbody2D Attached --- Engine.Physics2D/Collider2DBehaviourBase.cs | 26 ++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBehaviourBase.cs index c796770..4dea627 100644 --- a/Engine.Physics2D/Collider2DBehaviourBase.cs +++ b/Engine.Physics2D/Collider2DBehaviourBase.cs @@ -23,5 +23,29 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D public abstract void Recalculate(); - protected override void OnInitialize() => BehaviourController.TryGetBehaviour(out _rigidBody2D); + protected override void OnInitialize() + { + BehaviourController.TryGetBehaviour(out _rigidBody2D); + + BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController; + BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController; + } + + private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour) + { + if (behaviour is IRigidBody2D rigidbody) + _rigidBody2D = rigidbody; + } + + private void OnBehaviourRemovedFromController(IBehaviourController _, IBehaviour behaviour) + { + if (behaviour is IRigidBody2D _) + _rigidBody2D = null; + } + + protected override void OnFinalize() + { + BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController; + BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController; ; + } } From 3c39e6709db6fa2da55c0ac79f4babb9affd7d3d Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 21:53:28 +0300 Subject: [PATCH 47/93] fix: Collision Detector CircleCircle Depth Calculation Fixed --- Engine.Physics2D/CollisionDetector.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index 648f9e9..8829d57 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -50,15 +50,15 @@ public class CollisionDetector : ICollisionDetector collisionInformation = default; Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center); - float distanceCircleCenterSquared = leftToRightCenter.MagnitudeSquared; - float radiusSumSquared = left.CircleWorld.RadiusSquared + right.CircleWorld.RadiusSquared; + float distanceCircleCenter = leftToRightCenter.Magnitude; + float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius; - float circleSurfaceDistanceSquared = distanceCircleCenterSquared - radiusSumSquared; + float circleSurfaceDistance = distanceCircleCenter - radiusSum; - bool collision = circleSurfaceDistanceSquared <= 0f; + bool collision = circleSurfaceDistance <= 0f; if (collision) - collisionInformation = new(left, right, leftToRightCenter.Normalized, Math.Sqrt(circleSurfaceDistanceSquared)); + collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance); return collision; } From 266443504f7ce6197bd615cbf83bfe185360b45c Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 21:54:23 +0300 Subject: [PATCH 48/93] feat: Improved Test Collision Resolving --- Engine.Physics2D/PhysicsEngine2D.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 0d4c476..72c3162 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using Syntriax.Engine.Core; @@ -54,17 +53,20 @@ public class PhysicsEngine2D : IPhysicsEngine2D for (int x = 0; x < colliders.Count; x++) { ICollider2D? colliderX = colliders[x]; - for (int y = 0; y < colliders.Count; y++) + for (int y = x + 1; y < colliders.Count; y++) { ICollider2D? colliderY = colliders[y]; - if (colliderX.RigidBody2D == colliderY.RigidBody2D && colliderY.RigidBody2D is null) + if (colliderX.RigidBody2D == colliderY.RigidBody2D) continue; if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation? information)) { - information.Left.Transform.Position -= .5f * information.Normal * information.Penetration; - information.Right.Transform.Position += .5f * information.Normal * information.Penetration; + Vector2D displacementVector = .5f * information.Normal * information.Penetration; + information.Left.Transform.Position -= displacementVector; + information.Right.Transform.Position += displacementVector; + information.Left.Recalculate(); + information.Right.Recalculate(); } } } From ab9181fe3fd8c0fd2d7171ca259c7b3cb7da930f Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 21:54:39 +0300 Subject: [PATCH 49/93] refactor: Removed Unused Using --- Engine.Physics2D/Abstract/ICollider2D.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs index 964180c..7d652dd 100644 --- a/Engine.Physics2D/Abstract/ICollider2D.cs +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -1,5 +1,5 @@ using System; -using Syntriax.Engine.Core; + using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Abstract; From 0ba8927858a4f43ce59ddd5917fb65d469786e5d Mon Sep 17 00:00:00 2001 From: Syntriax Date: Thu, 25 Jan 2024 22:00:49 +0300 Subject: [PATCH 50/93] perf: Collider2DBase NeedsRecalculation Field --- Engine.Physics2D/Collider2DBehaviourBase.cs | 21 ++++++++++++++++--- Engine.Physics2D/Collider2DCircleBehaviour.cs | 2 +- Engine.Physics2D/Collider2DShapeBehaviour.cs | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBehaviourBase.cs index 4dea627..93becf7 100644 --- a/Engine.Physics2D/Collider2DBehaviourBase.cs +++ b/Engine.Physics2D/Collider2DBehaviourBase.cs @@ -12,16 +12,25 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D public Action? OnCollisionResolved { get; set; } = null; + protected bool NeedsRecalculation { get; private set; } = true; protected IRigidBody2D? _rigidBody2D = null; - public IRigidBody2D? RigidBody2D => _rigidBody2D; ITransform IAssignableTransform.Transform => Transform; Action? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); - public abstract void Recalculate(); + public void Recalculate() + { + if (!NeedsRecalculation) + return; + + CalculateCollider(); + NeedsRecalculation = false; + } + + public abstract void CalculateCollider(); protected override void OnInitialize() { @@ -29,6 +38,8 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController; BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController; + + Transform.OnPositionChanged += OnPositionChanged; } private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour) @@ -43,9 +54,13 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D _rigidBody2D = null; } + private void OnPositionChanged(ITransform transform) => NeedsRecalculation = true; + protected override void OnFinalize() { BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController; - BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController; ; + BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController; + + Transform.OnPositionChanged -= OnPositionChanged; } } diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs index 9d79fad..38991c5 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -10,5 +10,5 @@ public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollide public Circle CircleLocal { get; set; } = new(Vector2D.Zero, 1f); - public override void Recalculate() => CircleWorld = Transform.TransformCircle(CircleLocal); + public override void CalculateCollider() => CircleWorld = Transform.TransformCircle(CircleLocal); } diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index d16950b..9a0794c 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -10,5 +10,5 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2 protected Shape _shapeWorld = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); - public override void Recalculate() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); + public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); } From ceebe210417e52abf6d904ae603d82263b658599 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 12:21:32 +0300 Subject: [PATCH 51/93] chore: Removed Unnecessary .sln Files --- Engine.Core/Engine.Core.sln | 25 --------------------- Engine.Input/Engine.Input.sln | 25 --------------------- Engine.Physics2D/Engine.Physics2D.sln | 31 --------------------------- 3 files changed, 81 deletions(-) delete mode 100644 Engine.Core/Engine.Core.sln delete mode 100644 Engine.Input/Engine.Input.sln delete mode 100644 Engine.Physics2D/Engine.Physics2D.sln diff --git a/Engine.Core/Engine.Core.sln b/Engine.Core/Engine.Core.sln deleted file mode 100644 index 5133cda..0000000 --- a/Engine.Core/Engine.Core.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine.Core", "Engine.Core.csproj", "{6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6C1AF4B1-60B0-4225-9A96-F597BC04E9D0}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CC449885-B99C-4C71-842D-3C19A35E786F} - EndGlobalSection -EndGlobal diff --git a/Engine.Input/Engine.Input.sln b/Engine.Input/Engine.Input.sln deleted file mode 100644 index c53c614..0000000 --- a/Engine.Input/Engine.Input.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine.Input", "Engine.Input.csproj", "{637B86E7-3699-4248-9A9F-C5CB09779B53}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {637B86E7-3699-4248-9A9F-C5CB09779B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {637B86E7-3699-4248-9A9F-C5CB09779B53}.Debug|Any CPU.Build.0 = Debug|Any CPU - {637B86E7-3699-4248-9A9F-C5CB09779B53}.Release|Any CPU.ActiveCfg = Release|Any CPU - {637B86E7-3699-4248-9A9F-C5CB09779B53}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {FB9A6AA6-FAEF-4DE6-BDE0-C9CC4F02B29A} - EndGlobalSection -EndGlobal diff --git a/Engine.Physics2D/Engine.Physics2D.sln b/Engine.Physics2D/Engine.Physics2D.sln deleted file mode 100644 index fe814ce..0000000 --- a/Engine.Physics2D/Engine.Physics2D.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.002.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Engine.Physics2D", "Engine.Physics2D.csproj", "{88874ADD-23BD-4781-A47C-71B5D4AA9C25}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Engine.Core", "..\Engine.Core\Engine.Core.csproj", "{A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Release|Any CPU.ActiveCfg = Release|Any CPU - {88874ADD-23BD-4781-A47C-71B5D4AA9C25}.Release|Any CPU.Build.0 = Release|Any CPU - {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A9D6D7DC-4B5A-4D1E-878D-08BA3A871D3A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9C16F1F0-F5F8-4ED9-A3B4-7A0BF8003AA1} - EndGlobalSection -EndGlobal From 238bf2d57412c3307e665c5f882f51edb5567f18 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 12:34:01 +0300 Subject: [PATCH 52/93] feat: Shape.CreateCopy --- Engine.Physics2D/Primitives/Shape.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 08570d2..dc9e2aa 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -11,6 +11,8 @@ public record Shape(IList Vertices) : IEnumerable { public Vector2D this[Index index] => Vertices[index]; + public static Shape CreateCopy(Shape shape) => new(new List(shape.Vertices)); + public static Triangle GetSuperTriangle(Shape shape) { float minX = float.MaxValue, minY = float.MaxValue; @@ -90,6 +92,7 @@ public record Shape(IList Vertices) : IEnumerable public static class ShapeExtensions { + public static Shape CreateCopy(Shape shape) => Shape.CreateCopy(shape); public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape); public static void ToLines(this Shape shape, IList lines) => Shape.GetLines(shape, lines); public static List ToLines(this Shape shape) => Shape.GetLines(shape); From 8ebde9dedf34e892afa61e6d0720685d9353ad32 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 12:39:37 +0300 Subject: [PATCH 53/93] feat: Projection Data Record --- Engine.Physics2D/Primitives/Projection.cs | 30 +++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Engine.Physics2D/Primitives/Projection.cs diff --git a/Engine.Physics2D/Primitives/Projection.cs b/Engine.Physics2D/Primitives/Projection.cs new file mode 100644 index 0000000..acbecc8 --- /dev/null +++ b/Engine.Physics2D/Primitives/Projection.cs @@ -0,0 +1,30 @@ +namespace Syntriax.Engine.Physics2D.Primitives; + +public record Projection(float Min, float Max) +{ + public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _); + public static bool Overlaps(Projection left, Projection right, out float depth) + { + bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max; + if (rightMinInLeft) + { + depth = left.Max - right.Min; + return true; + } + + bool rightMaxInLeft = right.Max < left.Max && right.Max > left.Min; + if (rightMaxInLeft) + { + depth = left.Min - right.Max; + return true; + } + + depth = default; + return false; + } +} +public static class ProjectionExtensions +{ + public static bool Overlaps(this Projection left, Projection right) => Projection.Overlaps(left, right); + public static bool Overlaps(this Projection left, Projection right, out float depth) => Projection.Overlaps(left, right, out depth); +} From dfcc877e58d8be932990f747b938838c7dc651e7 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 12:40:12 +0300 Subject: [PATCH 54/93] chore: Removed System Using --- Engine.Physics2D/Primitives/Shape.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index dc9e2aa..6b74d49 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using System.Collections.Generic; @@ -9,7 +8,7 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Shape(IList Vertices) : IEnumerable { - public Vector2D this[Index index] => Vertices[index]; + public Vector2D this[System.Index index] => Vertices[index]; public static Shape CreateCopy(Shape shape) => new(new List(shape.Vertices)); @@ -20,15 +19,15 @@ public record Shape(IList Vertices) : IEnumerable foreach (Vector2D point in shape.Vertices) { - minX = MathF.Min(minX, point.X); - minY = MathF.Min(minY, point.Y); - maxX = MathF.Max(maxX, point.X); - maxY = MathF.Max(maxY, point.Y); + minX = Math.Min(minX, point.X); + minY = Math.Min(minY, point.Y); + maxX = Math.Max(maxX, point.X); + maxY = Math.Max(maxY, point.Y); } float dx = maxX - minX; float dy = maxY - minY; - float deltaMax = MathF.Max(dx, dy); + float deltaMax = Math.Max(dx, dy); float midX = (minX + maxX) / 2; float midY = (minY + maxY) / 2; From f980546d37337dcc15f7da7d96dec468a16ebb0e Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 12:41:51 +0300 Subject: [PATCH 55/93] feat: Circle & Shape Projections --- Engine.Physics2D/Primitives/Circle.cs | 8 ++++++++ Engine.Physics2D/Primitives/Shape.cs | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index dd39b9f..071dce2 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -13,6 +13,12 @@ public record Circle(Vector2D Center, float Radius) public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius); + public static Projection Project(Circle circle, Vector2D projectionVector) + { + float projectedCenter = circle.Center.Dot(projectionVector); + return new(projectedCenter - circle.Radius, projectedCenter + circle.Radius); + } + public static Circle TransformCircle(ITransform transform, Circle circle) => new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.Magnitude); @@ -27,6 +33,8 @@ public static class CircleExtensions public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector); + public static Projection ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector); + public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle); public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right); diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 6b74d49..5dbc804 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -53,6 +53,30 @@ public record Shape(IList Vertices) : IEnumerable return lines; } + public static void Project(Shape shape, Vector2D projectionVector, IList list) + { + list.Clear(); + + int count = shape.Vertices.Count; + for (int i = 0; i < count; i++) + list.Add(projectionVector.Dot(shape[i])); + } + + public static Projection Project(Shape shape, Vector2D projectionVector) + { + float min = float.MaxValue; + float max = float.MinValue; + + foreach (var vertex in shape) + { + float projectedLength = projectionVector.Dot(vertex); + min = Math.Min(projectedLength, min); + max = Math.Max(projectedLength, max); + } + + return new(min, max); + } + public static Shape TransformShape(Shape shape, ITransform transform) { List vertices = new(shape.Vertices.Count); @@ -96,6 +120,9 @@ public static class ShapeExtensions public static void ToLines(this Shape shape, IList lines) => Shape.GetLines(shape, lines); public static List ToLines(this Shape shape) => Shape.GetLines(shape); + public static void ToProjection(this Shape shape, Vector2D projectionVector, IList list) => Shape.Project(shape, projectionVector, list); + public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector); + public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform); public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to); From 271a9a244b1b2fc0bbb2e3bb722a3244b0bd262d Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 12:45:26 +0300 Subject: [PATCH 56/93] feat: CollisionDetector CircleCircle Projection --- Engine.Physics2D/CollisionDetector.cs | 29 +++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index 8829d57..4845f64 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -49,17 +49,34 @@ public class CollisionDetector : ICollisionDetector { collisionInformation = default; - Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center); - float distanceCircleCenter = leftToRightCenter.Magnitude; - float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius; + Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized; - float circleSurfaceDistance = distanceCircleCenter - radiusSum; + Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector); + Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector); - bool collision = circleSurfaceDistance <= 0f; + bool collision = leftProjection.Overlaps(rightProjection, out float depth); if (collision) - collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance); + collisionInformation = new(left, right, leftToRightCenterProjectionVector, depth); return collision; } + + // private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation) + // { + // collisionInformation = default; + + // Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center); + // float distanceCircleCenter = leftToRightCenter.Magnitude; + // float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius; + + // float circleSurfaceDistance = distanceCircleCenter - radiusSum; + + // bool collision = circleSurfaceDistance <= 0f; + + // if (collision) + // collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance); + + // return collision; + // } } From 4607955d55dd534c40d8bffc698a51b78d656016 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 18:31:15 +0300 Subject: [PATCH 57/93] feat: Vector2D.Perpendicular --- Engine.Core/Extensions/Vector2DExtensions.cs | 1 + Engine.Core/Vector2D.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/Engine.Core/Extensions/Vector2DExtensions.cs b/Engine.Core/Extensions/Vector2DExtensions.cs index a8ddaa3..29995da 100644 --- a/Engine.Core/Extensions/Vector2DExtensions.cs +++ b/Engine.Core/Extensions/Vector2DExtensions.cs @@ -19,6 +19,7 @@ public static class Vector2DExtensions public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to); public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale); + public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector); public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian); public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right); public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right); diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index 967ba4d..4051c54 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -40,6 +40,7 @@ public record Vector2D(float X, float Y) public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from; public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y); + public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X); public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y); public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y); public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y); From ac09b78edd4ca657820d684a47f2f009ac645244 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 18:49:26 +0300 Subject: [PATCH 58/93] feat: Transform Recalculation Conditions Updated --- Engine.Physics2D/Collider2DBehaviourBase.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBehaviourBase.cs index 93becf7..061d557 100644 --- a/Engine.Physics2D/Collider2DBehaviourBase.cs +++ b/Engine.Physics2D/Collider2DBehaviourBase.cs @@ -39,13 +39,15 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController; BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController; - Transform.OnPositionChanged += OnPositionChanged; + Transform.OnPositionChanged += SetNeedsRecalculation; + Transform.OnRotationChanged += SetNeedsRecalculation; + Transform.OnScaleChanged += SetNeedsRecalculation; } private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour) { - if (behaviour is IRigidBody2D rigidbody) - _rigidBody2D = rigidbody; + if (behaviour is IRigidBody2D rigidBody) + _rigidBody2D = rigidBody; } private void OnBehaviourRemovedFromController(IBehaviourController _, IBehaviour behaviour) @@ -54,13 +56,15 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D _rigidBody2D = null; } - private void OnPositionChanged(ITransform transform) => NeedsRecalculation = true; + private void SetNeedsRecalculation(ITransform transform) => NeedsRecalculation = true; protected override void OnFinalize() { BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController; BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController; + Transform.OnScaleChanged -= SetNeedsRecalculation; - Transform.OnPositionChanged -= OnPositionChanged; + Transform.OnPositionChanged -= SetNeedsRecalculation; + Transform.OnRotationChanged -= SetNeedsRecalculation; } } From 2bfd391286bd327359074d0700c346926191bbcc Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 18:54:53 +0300 Subject: [PATCH 59/93] feat: Basic Shape to Circle Collision Detection --- Engine.Physics2D/CollisionDetector.cs | 39 ++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index 4845f64..9a8f109 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Syntriax.Engine.Core; using Syntriax.Engine.Physics2D.Abstract; +using Syntriax.Engine.Physics2D.Primitives; namespace Syntriax.Engine.Physics2D; @@ -30,9 +31,9 @@ public class CollisionDetector : ICollisionDetector return false; } - private bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation) + private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation) { - throw new System.NotImplementedException(); + return DetectShapeCircle(shapeCollider, circleCollider, out collisionInformation); } private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation) @@ -42,7 +43,39 @@ public class CollisionDetector : ICollisionDetector private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation? collisionInformation) { - throw new System.NotImplementedException(); + collisionInformation = default; + + { + Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized; + + Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector); + Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector); + + if (!shapeProjection.Overlaps(circleProjection, out float depth)) + return false; + + if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) + collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth); + } + + var vertices = shapeCollider.ShapeWorld.Vertices; + int count = vertices.Count; + + for (int indexProjection = 0; indexProjection < count; indexProjection++) + { + Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized; + + Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector); + Projection circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector); + + if (!shapeProjection.Overlaps(circleProjection, out float depth)) + return false; + + if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) + collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth); + } + + return true; } private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation) From c6d2bad23ecf8ffbaa47c48078c72cc8def74d90 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 18:56:31 +0300 Subject: [PATCH 60/93] feat: Basic Shape to Shape Collision Detection --- Engine.Physics2D/CollisionDetector.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index 9a8f109..b8a4953 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -38,7 +38,26 @@ public class CollisionDetector : ICollisionDetector private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation) { - throw new System.NotImplementedException(); + collisionInformation = default; + + var vertices = left.ShapeWorld.Vertices; + int count = vertices.Count; + + for (int indexProjection = 0; indexProjection < count; indexProjection++) + { + Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized; + + Projection leftProjection = left.ShapeWorld.ToProjection(projectionVector); + Projection rightProjection = right.ShapeWorld.ToProjection(projectionVector); + + if (!leftProjection.Overlaps(rightProjection, out float depth)) + return false; + + if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) + collisionInformation = new(left, right, projectionVector, depth); + } + + return true; } private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation? collisionInformation) From ceb29cc42fb126cac294d3d8be32c123c1d03248 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 19:01:53 +0300 Subject: [PATCH 61/93] feat: Shape.CreateNgon --- Engine.Physics2D/Primitives/Shape.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 5dbc804..5aa4c10 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -12,6 +12,22 @@ public record Shape(IList Vertices) : IEnumerable public static Shape CreateCopy(Shape shape) => new(new List(shape.Vertices)); + public static Shape CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up); + public static Shape CreateNgon(int vertexCount, Vector2D positionToRotate) + { + if (vertexCount < 3) + throw new System.ArgumentException($"{nameof(vertexCount)} must have a value of more than 2."); + + List vertices = new(vertexCount); + + float radiansPerVertex = 360f / vertexCount * Math.DegreeToRadian; + + for (int i = 0; i < vertexCount; i++) + vertices.Add(positionToRotate.Rotate(i * radiansPerVertex)); + + return new(vertices); + } + public static Triangle GetSuperTriangle(Shape shape) { float minX = float.MaxValue, minY = float.MaxValue; From 6a84c3ec1a38e181183965a66cdcef3f0be40389 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 19:05:17 +0300 Subject: [PATCH 62/93] feat: Useful Readonly Shapes --- Engine.Physics2D/Primitives/Shape.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 5aa4c10..74b6142 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -8,6 +8,12 @@ namespace Syntriax.Engine.Physics2D.Primitives; public record Shape(IList Vertices) : IEnumerable { + public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up); + public static readonly Shape Box = CreateNgon(4, Vector2D.One); + public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up); + public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right); + + public Vector2D this[System.Index index] => Vertices[index]; public static Shape CreateCopy(Shape shape) => new(new List(shape.Vertices)); From 85bad951ff9802d1b8c0a00e23f11914b5acd20d Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 19:13:53 +0300 Subject: [PATCH 63/93] fix: Shape Collision on Larger Shapes --- Engine.Physics2D/Primitives/Projection.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Engine.Physics2D/Primitives/Projection.cs b/Engine.Physics2D/Primitives/Projection.cs index acbecc8..153c4c4 100644 --- a/Engine.Physics2D/Primitives/Projection.cs +++ b/Engine.Physics2D/Primitives/Projection.cs @@ -5,6 +5,7 @@ public record Projection(float Min, float Max) public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _); public static bool Overlaps(Projection left, Projection right, out float depth) { + // TODO Try to improve this bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max; if (rightMinInLeft) { @@ -19,7 +20,21 @@ public record Projection(float Min, float Max) return true; } - depth = default; + bool leftMinInRight = left.Min > right.Min && left.Min < right.Max; + if (leftMinInRight) + { + depth = right.Max - left.Min; + return true; + } + + bool leftMaxInRight = left.Max < right.Max && left.Max > right.Min; + if (leftMaxInRight) + { + depth = right.Min - left.Max; + return true; + } + + depth = 0f; return false; } } From 058c6dafe3ba572d1d14c139e8d468e87931a1a6 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 19:28:32 +0300 Subject: [PATCH 64/93] refactor: Removed Unused Methods --- Engine.Physics2D/PhysicsEngine2D.cs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 72c3162..0624fac 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -79,20 +79,6 @@ public class PhysicsEngine2D : IPhysicsEngine2D rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime; } - private static void CacheRotations(List rigidBodies, List rotations) - { - rotations.Clear(); - for (int i = 0; i < rigidBodies.Count; i++) - rotations.Add(rigidBodies[i].Transform.Rotation); - } - - private static void CachePositions(List rigidBodies, List positions) - { - positions.Clear(); - for (int i = 0; i < rigidBodies.Count; i++) - positions.Add(rigidBodies[i].Transform.Position); - } - private void OnBehaviourAdded(IBehaviourController controller, IBehaviour behaviour) { if (behaviour is not ICollider2D collider2D) From c32add40ff07beadeaa5464353ee156b3028afd9 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 20:35:05 +0300 Subject: [PATCH 65/93] fix: Shape to Shape Detection --- Engine.Physics2D/CollisionDetector.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index b8a4953..74da3bb 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -39,7 +39,11 @@ public class CollisionDetector : ICollisionDetector private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation) { collisionInformation = default; + return DetectShapeShapeOneWay(left, right, ref collisionInformation) && DetectShapeShapeOneWay(right, left, ref collisionInformation); + } + private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation? collisionInformation) + { var vertices = left.ShapeWorld.Vertices; int count = vertices.Count; From b14d10db0c399ea1adfd2fc2a815c779e848f276 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Fri, 26 Jan 2024 23:40:02 +0300 Subject: [PATCH 66/93] perf: Drastically Improved Memory Usage TIL, records are not value types and are actually just reference types. So I was pretty much allocating from heap every time I used any of my data types (Like Vector2D). Needless to say, they are all now readonly structs as I originally intended them to be. --- Engine.Core/EngineTime.cs | 10 ++++----- Engine.Core/Vector2D.cs | 16 ++++++++++---- .../CollisionDetectionInformation.cs | 10 +++++++-- Engine.Physics2D/CollisionDetector.cs | 22 +++++++++---------- Engine.Physics2D/ICollisionDetector.cs | 4 +--- Engine.Physics2D/PhysicsEngine2D.cs | 2 +- Engine.Physics2D/PhysicsMaterial2D.cs | 6 ++++- Engine.Physics2D/PhysicsMaterial2DDefault.cs | 8 +++++-- Engine.Physics2D/Primitives/AABB.cs | 11 ++++++---- Engine.Physics2D/Primitives/Circle.cs | 9 +++++--- Engine.Physics2D/Primitives/Line.cs | 13 ++++++----- Engine.Physics2D/Primitives/LineEquation.cs | 5 ++++- Engine.Physics2D/Primitives/Projection.cs | 5 ++++- Engine.Physics2D/Primitives/Shape.cs | 8 ++++--- Engine.Physics2D/Primitives/Triangle.cs | 8 +++++-- 15 files changed, 88 insertions(+), 49 deletions(-) diff --git a/Engine.Core/EngineTime.cs b/Engine.Core/EngineTime.cs index 107748c..c9d5f87 100644 --- a/Engine.Core/EngineTime.cs +++ b/Engine.Core/EngineTime.cs @@ -2,8 +2,8 @@ using System; namespace Syntriax.Engine.Core; -public record EngineTime -( - TimeSpan Total, - TimeSpan Elapsed -); +public readonly struct EngineTime(TimeSpan Total, TimeSpan Elapsed) +{ + public readonly TimeSpan Total { get; init; } = Total; + public readonly TimeSpan Elapsed { get; init; } = Elapsed; +} diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index 4051c54..1f23e9a 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -3,11 +3,14 @@ using System; namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized}")] -public record Vector2D(float X, float Y) +public readonly struct Vector2D(float X, float Y) { - public float Magnitude => Length(this); - public float MagnitudeSquared => LengthSquared(this); - public Vector2D Normalized => Normalize(this); + public readonly float X { get; init; } = X; + public readonly float Y { get; init; } = Y; + + public readonly float Magnitude => Length(this); + public readonly float MagnitudeSquared => LengthSquared(this); + public readonly Vector2D Normalized => Normalize(this); public readonly static Vector2D Up = new(0f, 1f); public readonly static Vector2D Down = new(0f, -1f); @@ -22,6 +25,8 @@ public record Vector2D(float X, float Y) public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value); public static Vector2D operator *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value); public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value); + public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y; + public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y; public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector)); public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y; @@ -72,4 +77,7 @@ public record Vector2D(float X, float Y) => left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon); public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})"; + + public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y); + public override int GetHashCode() => HashCode.Combine(X, Y); } diff --git a/Engine.Physics2D/CollisionDetectionInformation.cs b/Engine.Physics2D/CollisionDetectionInformation.cs index 3274bf9..ccf4ce2 100644 --- a/Engine.Physics2D/CollisionDetectionInformation.cs +++ b/Engine.Physics2D/CollisionDetectionInformation.cs @@ -3,10 +3,16 @@ using Syntriax.Engine.Physics2D.Abstract; namespace Syntriax.Engine.Physics2D; -public record CollisionDetectionInformation +public readonly struct CollisionDetectionInformation ( ICollider2D Left, ICollider2D Right, Vector2D Normal, float Penetration -); +) +{ + public ICollider2D Left { get; init; } = Left; + public ICollider2D Right { get; init; } = Right; + public Vector2D Normal { get; init; } = Normal; + public float Penetration { get; init; } = Penetration; +} diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index 74da3bb..32b0e4b 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -1,5 +1,3 @@ -using System.Diagnostics.CodeAnalysis; - using Syntriax.Engine.Core; using Syntriax.Engine.Physics2D.Abstract; using Syntriax.Engine.Physics2D.Primitives; @@ -8,7 +6,7 @@ namespace Syntriax.Engine.Physics2D; public class CollisionDetector : ICollisionDetector { - public bool TryDetect(T1 left, T2 right, [NotNullWhen(true)] out CollisionDetectionInformation? collisionInformation) + public bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D { @@ -31,18 +29,18 @@ public class CollisionDetector : ICollisionDetector return false; } - private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation) + private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation collisionInformation) { return DetectShapeCircle(shapeCollider, circleCollider, out collisionInformation); } - private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation) + private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation collisionInformation) { collisionInformation = default; return DetectShapeShapeOneWay(left, right, ref collisionInformation) && DetectShapeShapeOneWay(right, left, ref collisionInformation); } - private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation? collisionInformation) + private static bool DetectShapeShapeOneWay(IShapeCollider2D left, IShapeCollider2D right, ref CollisionDetectionInformation collisionInformation) { var vertices = left.ShapeWorld.Vertices; int count = vertices.Count; @@ -57,14 +55,14 @@ public class CollisionDetector : ICollisionDetector if (!leftProjection.Overlaps(rightProjection, out float depth)) return false; - if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) + if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) collisionInformation = new(left, right, projectionVector, depth); } return true; } - private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation? collisionInformation) + private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation collisionInformation) { collisionInformation = default; @@ -77,7 +75,7 @@ public class CollisionDetector : ICollisionDetector if (!shapeProjection.Overlaps(circleProjection, out float depth)) return false; - if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) + if (Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth); } @@ -94,14 +92,14 @@ public class CollisionDetector : ICollisionDetector if (!shapeProjection.Overlaps(circleProjection, out float depth)) return false; - if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) + if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth); } return true; } - private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation) + private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation collisionInformation) { collisionInformation = default; @@ -118,7 +116,7 @@ public class CollisionDetector : ICollisionDetector return collision; } - // private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation) + // private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation collisionInformation) // { // collisionInformation = default; diff --git a/Engine.Physics2D/ICollisionDetector.cs b/Engine.Physics2D/ICollisionDetector.cs index 460e5bb..11b9db1 100644 --- a/Engine.Physics2D/ICollisionDetector.cs +++ b/Engine.Physics2D/ICollisionDetector.cs @@ -1,10 +1,8 @@ -using System.Diagnostics.CodeAnalysis; - using Syntriax.Engine.Physics2D.Abstract; namespace Syntriax.Engine.Physics2D; public interface ICollisionDetector { - bool TryDetect(T1 left, T2 right, [NotNullWhen(returnValue: true)] out CollisionDetectionInformation? collisionInformation) where T1 : ICollider2D where T2 : ICollider2D; + bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D; } diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 0624fac..a471e1d 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -60,7 +60,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D if (colliderX.RigidBody2D == colliderY.RigidBody2D) continue; - if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation? information)) + if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information)) { Vector2D displacementVector = .5f * information.Normal * information.Penetration; information.Left.Transform.Position -= displacementVector; diff --git a/Engine.Physics2D/PhysicsMaterial2D.cs b/Engine.Physics2D/PhysicsMaterial2D.cs index 878691a..23e462e 100644 --- a/Engine.Physics2D/PhysicsMaterial2D.cs +++ b/Engine.Physics2D/PhysicsMaterial2D.cs @@ -2,4 +2,8 @@ using Syntriax.Engine.Physics2D.Abstract; namespace Syntriax.Engine.Physics2D; -public record PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D { } +public readonly struct PhysicsMaterial2D(float Friction, float Restitution) : IPhysicsMaterial2D +{ + public readonly float Friction { get; init; } = Friction; + public readonly float Restitution { get; init; } = Restitution; +} diff --git a/Engine.Physics2D/PhysicsMaterial2DDefault.cs b/Engine.Physics2D/PhysicsMaterial2DDefault.cs index 61b4367..41d1709 100644 --- a/Engine.Physics2D/PhysicsMaterial2DDefault.cs +++ b/Engine.Physics2D/PhysicsMaterial2DDefault.cs @@ -1,6 +1,10 @@ +using Syntriax.Engine.Physics2D.Abstract; + namespace Syntriax.Engine.Physics2D; -public record PhysicsMaterial2DDefault : PhysicsMaterial2D +public readonly struct PhysicsMaterial2DDefault : IPhysicsMaterial2D { - public PhysicsMaterial2DDefault() : base(.1f, .1f) { } + public readonly float Friction => .1f; + + public readonly float Restitution => .1f; } diff --git a/Engine.Physics2D/Primitives/AABB.cs b/Engine.Physics2D/Primitives/AABB.cs index 5da1cc1..b102087 100644 --- a/Engine.Physics2D/Primitives/AABB.cs +++ b/Engine.Physics2D/Primitives/AABB.cs @@ -4,11 +4,14 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; -public record AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) +public readonly struct AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) { - public Vector2D Center => (LowerBoundary + UpperBoundary) * .5f; - public Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs(); - public Vector2D SizeHalf => Size * .5f; + public readonly Vector2D LowerBoundary { get; init; } = LowerBoundary; + public readonly Vector2D UpperBoundary { get; init; } = UpperBoundary; + + public readonly Vector2D Center => (LowerBoundary + UpperBoundary) * .5f; + public readonly Vector2D Size => LowerBoundary.FromTo(UpperBoundary).Abs(); + public readonly Vector2D SizeHalf => Size * .5f; public static AABB FromVectors(IEnumerable vectors) { diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index 071dce2..4b8e20b 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -3,10 +3,13 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; -public record Circle(Vector2D Center, float Radius) +public readonly struct Circle(Vector2D Center, float Radius) { - public float RadiusSquared => Radius * Radius; - public float Diameter => 2f * Radius; + public readonly Vector2D Center { get; init; } = Center; + public readonly float Radius { get; init; } = Radius; + + public readonly float RadiusSquared => Radius * Radius; + public readonly float Diameter => 2f * Radius; public static Circle SetCenter(Circle circle, Vector2D center) => new(center, circle.Radius); public static Circle SetRadius(Circle circle, float radius) => new(circle.Center, radius); diff --git a/Engine.Physics2D/Primitives/Line.cs b/Engine.Physics2D/Primitives/Line.cs index 85a981e..bcb8e0d 100644 --- a/Engine.Physics2D/Primitives/Line.cs +++ b/Engine.Physics2D/Primitives/Line.cs @@ -6,12 +6,15 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; -public record Line(Vector2D From, Vector2D To) +public readonly struct Line(Vector2D From, Vector2D To) { - public Line Reversed => new(To, From); - public Vector2D Direction => From.FromTo(To).Normalize(); - public float Length => From.FromTo(To).Length(); - public float LengthSquared => From.FromTo(To).LengthSquared(); + public readonly Vector2D From { get; init; } = From; + public readonly Vector2D To { get; init; } = To; + + public readonly Line Reversed => new(To, From); + public readonly Vector2D Direction => From.FromTo(To).Normalize(); + public readonly float Length => From.FromTo(To).Length(); + public readonly float LengthSquared => From.FromTo(To).LengthSquared(); public static LineEquation GetLineEquation(Line line) { diff --git a/Engine.Physics2D/Primitives/LineEquation.cs b/Engine.Physics2D/Primitives/LineEquation.cs index 06ff657..a36b25e 100644 --- a/Engine.Physics2D/Primitives/LineEquation.cs +++ b/Engine.Physics2D/Primitives/LineEquation.cs @@ -2,8 +2,11 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; -public record LineEquation(float Slope, float OffsetY) +public readonly struct LineEquation(float Slope, float OffsetY) { + public readonly float Slope { get; init; } = Slope; + public readonly float OffsetY { get; init; } = OffsetY; + public static float Resolve(LineEquation lineEquation, float x) => lineEquation.Slope * x + lineEquation.OffsetY; // y = mx + b public static bool ApproximatelyEquals(LineEquation left, LineEquation right) diff --git a/Engine.Physics2D/Primitives/Projection.cs b/Engine.Physics2D/Primitives/Projection.cs index 153c4c4..9f76f4a 100644 --- a/Engine.Physics2D/Primitives/Projection.cs +++ b/Engine.Physics2D/Primitives/Projection.cs @@ -1,7 +1,10 @@ namespace Syntriax.Engine.Physics2D.Primitives; -public record Projection(float Min, float Max) +public readonly struct Projection(float Min, float Max) { + public readonly float Min { get; init; } = Min; + public readonly float Max { get; init; } = Max; + public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _); public static bool Overlaps(Projection left, Projection right, out float depth) { diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 74b6142..240b25e 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -6,13 +6,15 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; -public record Shape(IList Vertices) : IEnumerable +public readonly struct Shape(IList Vertices) : IEnumerable { public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up); public static readonly Shape Box = CreateNgon(4, Vector2D.One); public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up); public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right); + public readonly IList Vertices { get; init; } = Vertices; + public Vector2D this[System.Index index] => Vertices[index]; @@ -89,9 +91,9 @@ public record Shape(IList Vertices) : IEnumerable float min = float.MaxValue; float max = float.MinValue; - foreach (var vertex in shape) + for (int i = 0; i < shape.Vertices.Count; i++) { - float projectedLength = projectionVector.Dot(vertex); + float projectedLength = projectionVector.Dot(shape.Vertices[i]); min = Math.Min(projectedLength, min); max = Math.Max(projectedLength, max); } diff --git a/Engine.Physics2D/Primitives/Triangle.cs b/Engine.Physics2D/Primitives/Triangle.cs index 8ab76ec..06099f4 100644 --- a/Engine.Physics2D/Primitives/Triangle.cs +++ b/Engine.Physics2D/Primitives/Triangle.cs @@ -4,9 +4,13 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; -public record Triangle(Vector2D A, Vector2D B, Vector2D C) +public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) { - public float Area + public readonly Vector2D A { get; init; } = A; + public readonly Vector2D B { get; init; } = B; + public readonly Vector2D C { get; init; } = C; + + public readonly float Area => .5f * MathF.Abs( A.X * (B.Y - C.Y) + B.X * (C.Y - A.Y) + From 7b47703ba0ed6303ad9c6d7557832935eb486466 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 00:51:34 +0300 Subject: [PATCH 67/93] refactor: DebuggerDisplays For Basic Types --- Engine.Core/Behaviour.cs | 1 + Engine.Core/BehaviourController.cs | 1 + Engine.Core/GameManager.cs | 1 + Engine.Core/GameObject.cs | 1 + Engine.Core/Transform.cs | 1 + Engine.Physics2D/CollisionDetectionInformation.cs | 1 + Engine.Physics2D/Primitives/AABB.cs | 1 + Engine.Physics2D/Primitives/Circle.cs | 1 + Engine.Physics2D/Primitives/Line.cs | 1 + Engine.Physics2D/Primitives/LineEquation.cs | 1 + Engine.Physics2D/Primitives/Projection.cs | 1 + Engine.Physics2D/Primitives/Shape.cs | 1 + Engine.Physics2D/Primitives/Triangle.cs | 1 + 13 files changed, 13 insertions(+) diff --git a/Engine.Core/Behaviour.cs b/Engine.Core/Behaviour.cs index 943d366..f782872 100644 --- a/Engine.Core/Behaviour.cs +++ b/Engine.Core/Behaviour.cs @@ -5,6 +5,7 @@ using Syntriax.Engine.Core.Exceptions; namespace Syntriax.Engine.Core; +[System.Diagnostics.DebuggerDisplay("Priority: {Priority}, Initialized: {Initialized}")] public abstract class Behaviour : IBehaviour { public Action? OnUnassigned { get; set; } = null; diff --git a/Engine.Core/BehaviourController.cs b/Engine.Core/BehaviourController.cs index 582276b..22367b8 100644 --- a/Engine.Core/BehaviourController.cs +++ b/Engine.Core/BehaviourController.cs @@ -7,6 +7,7 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core; +[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count()}")] public class BehaviourController : IBehaviourController { public Action? OnPreUpdate { get; set; } diff --git a/Engine.Core/GameManager.cs b/Engine.Core/GameManager.cs index 8410bce..6ac44cb 100644 --- a/Engine.Core/GameManager.cs +++ b/Engine.Core/GameManager.cs @@ -8,6 +8,7 @@ using Syntriax.Engine.Core.Factory; namespace Syntriax.Engine.Core; +[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count()}")] public class GameManager : IEntity, IEnumerable { public Action? OnCameraChanged { get; set; } = null; diff --git a/Engine.Core/GameObject.cs b/Engine.Core/GameObject.cs index 7423977..0e50c25 100644 --- a/Engine.Core/GameObject.cs +++ b/Engine.Core/GameObject.cs @@ -5,6 +5,7 @@ using Syntriax.Engine.Core.Exceptions; namespace Syntriax.Engine.Core; +[System.Diagnostics.DebuggerDisplay("Name: {Name}, Initialized: {Initialized}")] public class GameObject : IGameObject { public Action? OnStateEnableAssigned { get; set; } = null; diff --git a/Engine.Core/Transform.cs b/Engine.Core/Transform.cs index f4b28dd..cc0c32e 100644 --- a/Engine.Core/Transform.cs +++ b/Engine.Core/Transform.cs @@ -4,6 +4,7 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core; +[System.Diagnostics.DebuggerDisplay("Position: {Position.ToString(), nq}, Scale: {Scale.ToString(), nq}, Rotation: {Rotation}")] public class Transform : ITransform { public Action? OnPositionChanged { get; set; } = null; diff --git a/Engine.Physics2D/CollisionDetectionInformation.cs b/Engine.Physics2D/CollisionDetectionInformation.cs index ccf4ce2..453932c 100644 --- a/Engine.Physics2D/CollisionDetectionInformation.cs +++ b/Engine.Physics2D/CollisionDetectionInformation.cs @@ -3,6 +3,7 @@ using Syntriax.Engine.Physics2D.Abstract; namespace Syntriax.Engine.Physics2D; +[System.Diagnostics.DebuggerDisplay("Normal: {Normal.ToString(), nq}, Penetration: {Penetration}")] public readonly struct CollisionDetectionInformation ( ICollider2D Left, diff --git a/Engine.Physics2D/Primitives/AABB.cs b/Engine.Physics2D/Primitives/AABB.cs index b102087..e165fd8 100644 --- a/Engine.Physics2D/Primitives/AABB.cs +++ b/Engine.Physics2D/Primitives/AABB.cs @@ -4,6 +4,7 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; +[System.Diagnostics.DebuggerDisplay("LowerBoundary: {LowerBoundary.ToString(), nq}, UpperBoundary: {UpperBoundary.ToString(), nq}")] public readonly struct AABB(Vector2D LowerBoundary, Vector2D UpperBoundary) { public readonly Vector2D LowerBoundary { get; init; } = LowerBoundary; diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index 4b8e20b..4a6e180 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -3,6 +3,7 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; +[System.Diagnostics.DebuggerDisplay("Center: {Center.ToString(), nq}, Radius: {Radius}")] public readonly struct Circle(Vector2D Center, float Radius) { public readonly Vector2D Center { get; init; } = Center; diff --git a/Engine.Physics2D/Primitives/Line.cs b/Engine.Physics2D/Primitives/Line.cs index bcb8e0d..7d0f1ec 100644 --- a/Engine.Physics2D/Primitives/Line.cs +++ b/Engine.Physics2D/Primitives/Line.cs @@ -6,6 +6,7 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; +[System.Diagnostics.DebuggerDisplay("From: {From.ToString(), nq}, To: {To.ToString(), nq}, Direction: {Direction.ToString(), nq}, Length: {Length}")] public readonly struct Line(Vector2D From, Vector2D To) { public readonly Vector2D From { get; init; } = From; diff --git a/Engine.Physics2D/Primitives/LineEquation.cs b/Engine.Physics2D/Primitives/LineEquation.cs index a36b25e..3a3e417 100644 --- a/Engine.Physics2D/Primitives/LineEquation.cs +++ b/Engine.Physics2D/Primitives/LineEquation.cs @@ -2,6 +2,7 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; +[System.Diagnostics.DebuggerDisplay("y = {Slope}x + {OffsetY}")] public readonly struct LineEquation(float Slope, float OffsetY) { public readonly float Slope { get; init; } = Slope; diff --git a/Engine.Physics2D/Primitives/Projection.cs b/Engine.Physics2D/Primitives/Projection.cs index 9f76f4a..2f0af58 100644 --- a/Engine.Physics2D/Primitives/Projection.cs +++ b/Engine.Physics2D/Primitives/Projection.cs @@ -1,5 +1,6 @@ namespace Syntriax.Engine.Physics2D.Primitives; +[System.Diagnostics.DebuggerDisplay("Min: {Min}, Max: {Max}")] public readonly struct Projection(float Min, float Max) { public readonly float Min { get; init; } = Min; diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 240b25e..abc49ed 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -6,6 +6,7 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; +[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count()}")] public readonly struct Shape(IList Vertices) : IEnumerable { public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up); diff --git a/Engine.Physics2D/Primitives/Triangle.cs b/Engine.Physics2D/Primitives/Triangle.cs index 06099f4..9695460 100644 --- a/Engine.Physics2D/Primitives/Triangle.cs +++ b/Engine.Physics2D/Primitives/Triangle.cs @@ -4,6 +4,7 @@ using Syntriax.Engine.Core; namespace Syntriax.Engine.Physics2D.Primitives; +[System.Diagnostics.DebuggerDisplay("A: {A.ToString(), nq}, B: {B.ToString(), nq}, B: {C.ToString(), nq}")] public readonly struct Triangle(Vector2D A, Vector2D B, Vector2D C) { public readonly Vector2D A { get; init; } = A; From 32e2a6e7d391a51d864906fe58b08ae33f3d6109 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 14:58:50 +0300 Subject: [PATCH 68/93] feat: Rigidbody Mass Restriction --- Engine.Physics2D/RigidBody2D.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Engine.Physics2D/RigidBody2D.cs b/Engine.Physics2D/RigidBody2D.cs index e2fe1da..3820170 100644 --- a/Engine.Physics2D/RigidBody2D.cs +++ b/Engine.Physics2D/RigidBody2D.cs @@ -10,14 +10,18 @@ public class RigidBody2D : BehaviourOverride, IRigidBody2D { public Action? OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } + private const float LOWEST_ALLOWED_MASS = 0.00001f; + private float _mass = 1f; + public IPhysicsMaterial2D Material { get; set; } = new PhysicsMaterial2DDefault(); public Vector2D Velocity { get; set; } = Vector2D.Zero; public float AngularVelocity { get; set; } = 0f; - public float Mass { get; set; } = 1f; public bool IsStatic { get; set; } = false; + public float Mass { get => _mass; set => _mass = Core.Math.Max(value, LOWEST_ALLOWED_MASS); } + ITransform IAssignableTransform.Transform => Transform; From bd43d39367ad5ec12b60b95d92cacf2157950488 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 15:07:07 +0300 Subject: [PATCH 69/93] fix: Circles Not Colliding Accurately Fixed --- Engine.Physics2D/CollisionDetector.cs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector.cs index 32b0e4b..93efd8e 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector.cs @@ -66,19 +66,6 @@ public class CollisionDetector : ICollisionDetector { collisionInformation = default; - { - Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized; - - Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector); - Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector); - - if (!shapeProjection.Overlaps(circleProjection, out float depth)) - return false; - - if (Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) - collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth); - } - var vertices = shapeCollider.ShapeWorld.Vertices; int count = vertices.Count; @@ -96,6 +83,19 @@ public class CollisionDetector : ICollisionDetector collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth); } + { + Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized; + + Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector); + Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector); + + if (!shapeProjection.Overlaps(circleProjection, out float depth)) + return false; + + if (collisionInformation.Left is null || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth)) + collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth); + } + return true; } From 9556be6f1728080435ff9cdc42bc6c64d47a7f3e Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 15:07:40 +0300 Subject: [PATCH 70/93] feat: Static & Mass Consideration For Rigidbodies --- Engine.Physics2D/PhysicsEngine2D.cs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index a471e1d..5bdec77 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -62,9 +62,31 @@ public class PhysicsEngine2D : IPhysicsEngine2D if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information)) { - Vector2D displacementVector = .5f * information.Normal * information.Penetration; - information.Left.Transform.Position -= displacementVector; - information.Right.Transform.Position += displacementVector; + Vector2D displacementVector = information.Normal * information.Penetration; + + + bool isStaticLeft = information.Left.RigidBody2D?.IsStatic ?? true; + bool isStaticRight = information.Right.RigidBody2D?.IsStatic ?? true; + + if (isStaticLeft && isStaticRight) + continue; + else if (isStaticLeft) + information.Right.Transform.Position += displacementVector; + else if (isStaticRight) + information.Left.Transform.Position -= displacementVector; + else + { + float leftMass = information.Left.RigidBody2D?.Mass ?? float.Epsilon; + float rightMass = information.Right.RigidBody2D?.Mass ?? float.Epsilon; + float massSum = leftMass + rightMass; + + float leftMomentumPercentage = leftMass / massSum; + float rightMomentumPercentage = rightMass / massSum; + + information.Right.Transform.Position += leftMomentumPercentage * displacementVector; + information.Left.Transform.Position -= rightMomentumPercentage * displacementVector; + } + information.Left.Recalculate(); information.Right.Recalculate(); } From 0ba6913a616ce3d546b0513553bd81a173681112 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 19:21:18 +0300 Subject: [PATCH 71/93] fix: Static Rigidbodies Moving When Velocity or AngularVelocity Assigned --- Engine.Physics2D/PhysicsEngine2D.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 5bdec77..3be4953 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -97,6 +97,9 @@ public class PhysicsEngine2D : IPhysicsEngine2D private static void StepRigidBody(IRigidBody2D rigidBody, float intervalDeltaTime) { + if (rigidBody.IsStatic) + return; + rigidBody.Transform.Position += rigidBody.Velocity * intervalDeltaTime; rigidBody.Transform.Rotation += rigidBody.AngularVelocity * intervalDeltaTime; } From 309c8db6e10fd5d99f6902c476536c027df4dc2e Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 19:22:59 +0300 Subject: [PATCH 72/93] feat: ICollider2D Action Calls --- Engine.Physics2D/Abstract/ICollider2D.cs | 4 ++-- Engine.Physics2D/PhysicsEngine2D.cs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs index 7d652dd..5d03006 100644 --- a/Engine.Physics2D/Abstract/ICollider2D.cs +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -6,8 +6,8 @@ namespace Syntriax.Engine.Physics2D.Abstract; public interface ICollider2D : IBehaviour, IAssignableTransform { - Action? OnCollisionPreResolve { get; set; } - Action? OnCollisionResolved { get; set; } + Action? OnCollisionPreResolve { get; set; } + Action? OnCollisionResolved { get; set; } IRigidBody2D? RigidBody2D { get; } diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 3be4953..d7fc89d 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -62,6 +62,9 @@ public class PhysicsEngine2D : IPhysicsEngine2D if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information)) { + colliderX.OnCollisionPreResolve?.Invoke(colliderX, information); + colliderY.OnCollisionPreResolve?.Invoke(colliderY, information); + Vector2D displacementVector = information.Normal * information.Penetration; @@ -89,6 +92,9 @@ public class PhysicsEngine2D : IPhysicsEngine2D information.Left.Recalculate(); information.Right.Recalculate(); + + colliderX.OnCollisionResolved?.Invoke(colliderX, information); + colliderY.OnCollisionResolved?.Invoke(colliderY, information); } } } From 05d88f7ca2bf906694f9cfa17f9daa71806978b1 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 19:58:31 +0300 Subject: [PATCH 73/93] refactor: Renamed ICollisionDetector to ICollisionDetector2D --- .../{ICollisionDetector.cs => Abstract/ICollisionDetector2D.cs} | 2 +- .../{CollisionDetector.cs => CollisionDetector2D.cs} | 2 +- Engine.Physics2D/PhysicsEngine2D.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename Engine.Physics2D/{ICollisionDetector.cs => Abstract/ICollisionDetector2D.cs} (86%) rename Engine.Physics2D/{CollisionDetector.cs => CollisionDetector2D.cs} (99%) diff --git a/Engine.Physics2D/ICollisionDetector.cs b/Engine.Physics2D/Abstract/ICollisionDetector2D.cs similarity index 86% rename from Engine.Physics2D/ICollisionDetector.cs rename to Engine.Physics2D/Abstract/ICollisionDetector2D.cs index 11b9db1..d76da2a 100644 --- a/Engine.Physics2D/ICollisionDetector.cs +++ b/Engine.Physics2D/Abstract/ICollisionDetector2D.cs @@ -2,7 +2,7 @@ using Syntriax.Engine.Physics2D.Abstract; namespace Syntriax.Engine.Physics2D; -public interface ICollisionDetector +public interface ICollisionDetector2D { bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D where T2 : ICollider2D; } diff --git a/Engine.Physics2D/CollisionDetector.cs b/Engine.Physics2D/CollisionDetector2D.cs similarity index 99% rename from Engine.Physics2D/CollisionDetector.cs rename to Engine.Physics2D/CollisionDetector2D.cs index 93efd8e..1f9c332 100644 --- a/Engine.Physics2D/CollisionDetector.cs +++ b/Engine.Physics2D/CollisionDetector2D.cs @@ -4,7 +4,7 @@ using Syntriax.Engine.Physics2D.Primitives; namespace Syntriax.Engine.Physics2D; -public class CollisionDetector : ICollisionDetector +public class CollisionDetector2D : ICollisionDetector2D { public bool TryDetect(T1 left, T2 right, out CollisionDetectionInformation collisionInformation) where T1 : ICollider2D diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index d7fc89d..a308e18 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -12,7 +12,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D private readonly List colliders = new(64); private int _iterationCount = 1; - private ICollisionDetector collisionDetector = new CollisionDetector(); + private ICollisionDetector2D collisionDetector = new CollisionDetector2D(); public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } From 4d9121118df11a3267e4af930566c4d769038636 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 19:59:27 +0300 Subject: [PATCH 74/93] refactor: Renamed OnCollisionPreResolve to OnCollisionDetected --- Engine.Physics2D/Abstract/ICollider2D.cs | 2 +- Engine.Physics2D/PhysicsEngine2D.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs index 5d03006..f69ab9b 100644 --- a/Engine.Physics2D/Abstract/ICollider2D.cs +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -6,7 +6,7 @@ namespace Syntriax.Engine.Physics2D.Abstract; public interface ICollider2D : IBehaviour, IAssignableTransform { - Action? OnCollisionPreResolve { get; set; } + Action? OnCollisionDetected { get; set; } Action? OnCollisionResolved { get; set; } IRigidBody2D? RigidBody2D { get; } diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index a308e18..c04c8da 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -62,8 +62,8 @@ public class PhysicsEngine2D : IPhysicsEngine2D if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information)) { - colliderX.OnCollisionPreResolve?.Invoke(colliderX, information); - colliderY.OnCollisionPreResolve?.Invoke(colliderY, information); + colliderX.OnCollisionDetected?.Invoke(colliderX, information); + colliderY.OnCollisionDetected?.Invoke(colliderY, information); Vector2D displacementVector = information.Normal * information.Penetration; From a3c4afb22356f0bebe8a87d29b8393066af23387 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:08:16 +0300 Subject: [PATCH 75/93] refactor: Basic Collision Resolver --- .../Abstract/ICollisionResolver2D.cs | 2 +- Engine.Physics2D/CollisionResolver2D.cs | 44 +++++++++++++++++++ Engine.Physics2D/PhysicsEngine2D.cs | 32 +------------- 3 files changed, 47 insertions(+), 31 deletions(-) create mode 100644 Engine.Physics2D/CollisionResolver2D.cs diff --git a/Engine.Physics2D/Abstract/ICollisionResolver2D.cs b/Engine.Physics2D/Abstract/ICollisionResolver2D.cs index 83d54b4..05672a0 100644 --- a/Engine.Physics2D/Abstract/ICollisionResolver2D.cs +++ b/Engine.Physics2D/Abstract/ICollisionResolver2D.cs @@ -2,5 +2,5 @@ namespace Syntriax.Engine.Physics2D.Abstract; public interface ICollisionResolver2D { - void ResolveCollision(ICollider2D colliderA, ICollider2D colliderB); + void Resolve(CollisionDetectionInformation collisionInformation); } diff --git a/Engine.Physics2D/CollisionResolver2D.cs b/Engine.Physics2D/CollisionResolver2D.cs new file mode 100644 index 0000000..4cb6c04 --- /dev/null +++ b/Engine.Physics2D/CollisionResolver2D.cs @@ -0,0 +1,44 @@ +using Syntriax.Engine.Core; +using Syntriax.Engine.Physics2D.Abstract; + +namespace Syntriax.Engine.Physics2D; + +public class CollisionResolver2D : ICollisionResolver2D +{ + public void Resolve(CollisionDetectionInformation collisionInformation) + { + Vector2D displacementVector = collisionInformation.Normal * collisionInformation.Penetration; + + ICollider2D left = collisionInformation.Left; + ICollider2D right = collisionInformation.Right; + + bool isLeftStatic = left.RigidBody2D?.IsStatic ?? true; + bool isRightStatic = right.RigidBody2D?.IsStatic ?? true; + + if (isLeftStatic && isRightStatic) + return; + + if (isLeftStatic) + right.Transform.Position += displacementVector; + else if (isRightStatic) + left.Transform.Position -= displacementVector; + else + { + float leftMass = left.RigidBody2D?.Mass ?? float.Epsilon; + float rightMass = right.RigidBody2D?.Mass ?? float.Epsilon; + float sumMass = leftMass + rightMass; + + float leftMomentumPercentage = leftMass / sumMass; + float rightMomentumPercentage = rightMass / sumMass; + + right.Transform.Position += leftMomentumPercentage * displacementVector; + left.Transform.Position -= rightMomentumPercentage * displacementVector; + } + + left.Recalculate(); + right.Recalculate(); + + left.OnCollisionResolved?.Invoke(collisionInformation.Left, collisionInformation); + right.OnCollisionResolved?.Invoke(right, collisionInformation); + } +} diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index c04c8da..f0d9406 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -13,6 +13,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D private int _iterationCount = 1; private ICollisionDetector2D collisionDetector = new CollisionDetector2D(); + private ICollisionResolver2D collisionResolver = new CollisionResolver2D(); public int IterationCount { get => _iterationCount; set => _iterationCount = value < 1 ? 1 : value; } @@ -65,36 +66,7 @@ public class PhysicsEngine2D : IPhysicsEngine2D colliderX.OnCollisionDetected?.Invoke(colliderX, information); colliderY.OnCollisionDetected?.Invoke(colliderY, information); - Vector2D displacementVector = information.Normal * information.Penetration; - - - bool isStaticLeft = information.Left.RigidBody2D?.IsStatic ?? true; - bool isStaticRight = information.Right.RigidBody2D?.IsStatic ?? true; - - if (isStaticLeft && isStaticRight) - continue; - else if (isStaticLeft) - information.Right.Transform.Position += displacementVector; - else if (isStaticRight) - information.Left.Transform.Position -= displacementVector; - else - { - float leftMass = information.Left.RigidBody2D?.Mass ?? float.Epsilon; - float rightMass = information.Right.RigidBody2D?.Mass ?? float.Epsilon; - float massSum = leftMass + rightMass; - - float leftMomentumPercentage = leftMass / massSum; - float rightMomentumPercentage = rightMass / massSum; - - information.Right.Transform.Position += leftMomentumPercentage * displacementVector; - information.Left.Transform.Position -= rightMomentumPercentage * displacementVector; - } - - information.Left.Recalculate(); - information.Right.Recalculate(); - - colliderX.OnCollisionResolved?.Invoke(colliderX, information); - colliderY.OnCollisionResolved?.Invoke(colliderY, information); + collisionResolver?.Resolve(information); } } } From 5620f2f1ebb3d616438626c36c3be2274c4aa7ad Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:14:55 +0300 Subject: [PATCH 76/93] feat: Circle.UnitCircle --- Engine.Physics2D/Primitives/Circle.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Engine.Physics2D/Primitives/Circle.cs b/Engine.Physics2D/Primitives/Circle.cs index 4a6e180..2be2b56 100644 --- a/Engine.Physics2D/Primitives/Circle.cs +++ b/Engine.Physics2D/Primitives/Circle.cs @@ -6,6 +6,8 @@ namespace Syntriax.Engine.Physics2D.Primitives; [System.Diagnostics.DebuggerDisplay("Center: {Center.ToString(), nq}, Radius: {Radius}")] public readonly struct Circle(Vector2D Center, float Radius) { + public static readonly Circle UnitCircle = new(Vector2D.Zero, 1f); + public readonly Vector2D Center { get; init; } = Center; public readonly float Radius { get; init; } = Radius; From 0d4c96a2fc1f7afb15dd7b54b56447b201245a03 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:17:46 +0300 Subject: [PATCH 77/93] fix: Forgotten Build Errors on Collider2DBehaviourBase Actions --- Engine.Physics2D/Collider2DBehaviourBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBehaviourBase.cs index 061d557..9008387 100644 --- a/Engine.Physics2D/Collider2DBehaviourBase.cs +++ b/Engine.Physics2D/Collider2DBehaviourBase.cs @@ -8,8 +8,8 @@ namespace Syntriax.Engine.Physics2D; public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D { - public Action? OnCollisionPreResolve { get; set; } = null; - public Action? OnCollisionResolved { get; set; } = null; + public Action? OnCollisionDetected { get; set; } = null; + public Action? OnCollisionResolved { get; set; } = null; protected bool NeedsRecalculation { get; private set; } = true; From 574104c224ede2e8ca94fb825f6305de703aa91c Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:19:00 +0300 Subject: [PATCH 78/93] feat: ICollider IsTrigger --- Engine.Physics2D/Abstract/ICollider2D.cs | 1 + Engine.Physics2D/Collider2DBehaviourBase.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs index f69ab9b..9dfb806 100644 --- a/Engine.Physics2D/Abstract/ICollider2D.cs +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -10,6 +10,7 @@ public interface ICollider2D : IBehaviour, IAssignableTransform Action? OnCollisionResolved { get; set; } IRigidBody2D? RigidBody2D { get; } + bool IsTrigger { get; set; } void Recalculate(); } diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBehaviourBase.cs index 9008387..0d42623 100644 --- a/Engine.Physics2D/Collider2DBehaviourBase.cs +++ b/Engine.Physics2D/Collider2DBehaviourBase.cs @@ -16,9 +16,11 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D protected IRigidBody2D? _rigidBody2D = null; public IRigidBody2D? RigidBody2D => _rigidBody2D; + public bool IsTrigger { get; set; } = false; ITransform IAssignableTransform.Transform => Transform; Action? IAssignableTransform.OnTransformAssigned { get => GameObject.OnTransformAssigned; set => GameObject.OnTransformAssigned = value; } + bool IAssignableTransform.Assign(ITransform transform) => GameObject.Assign(transform); public void Recalculate() From ab2489f6cfa4f30572fd5d138bdbb1258505e53a Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:19:55 +0300 Subject: [PATCH 79/93] feat: Shape & Circle Collider Constructors --- Engine.Physics2D/Collider2DCircleBehaviour.cs | 8 ++++++++ Engine.Physics2D/Collider2DShapeBehaviour.cs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs index 38991c5..a11be89 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -11,4 +11,12 @@ public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollide public override void CalculateCollider() => CircleWorld = Transform.TransformCircle(CircleLocal); + + + public Collider2DCircleBehaviour() { } + public Collider2DCircleBehaviour(Circle circle) + { + CircleLocal = circle; + Recalculate(); + } } diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index 9a0794c..ca2b076 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -11,4 +11,12 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2 protected Shape _shapeWorld = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); + + + public Collider2DShapeBehaviour() { } + public Collider2DShapeBehaviour(Shape shape) + { + ShapeLocal = shape; + Recalculate(); + } } From 15788f2eca29647fbce98c9650f03f11d62b6976 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:20:28 +0300 Subject: [PATCH 80/93] refactor: Improved Shape & Circle Code Readability --- Engine.Physics2D/Collider2DCircleBehaviour.cs | 5 ++--- Engine.Physics2D/Collider2DShapeBehaviour.cs | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs index a11be89..d652636 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -1,4 +1,3 @@ -using Syntriax.Engine.Core; using Syntriax.Engine.Physics2D.Abstract; using Syntriax.Engine.Physics2D.Primitives; @@ -6,8 +5,8 @@ namespace Syntriax.Engine.Physics2D; public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollider2D { - public Circle CircleWorld { get; protected set; } = new(Vector2D.Zero, 1f); - public Circle CircleLocal { get; set; } = new(Vector2D.Zero, 1f); + public Circle CircleWorld { get; protected set; } = Circle.UnitCircle; + public Circle CircleLocal { get; set; } = Circle.UnitCircle; public override void CalculateCollider() => CircleWorld = Transform.TransformCircle(CircleLocal); diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index ca2b076..f225ad0 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -5,10 +5,9 @@ namespace Syntriax.Engine.Physics2D; public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D { - public Shape ShapeWorld => _shapeWorld; - public Shape ShapeLocal { get; set; } = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); + public Shape ShapeWorld { get; protected set; } = Shape.Box; + public Shape ShapeLocal { get; set; } = Shape.Box; - protected Shape _shapeWorld = new([new(1f, 1f), new(-1f, 1f), new(-1f, -1f), new(1f, -1f)]); public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); From affd2bb8c4a7c9e8d23a66cb1cb0e277701201e5 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:31:51 +0300 Subject: [PATCH 81/93] feat: ICollider.OnTriggered --- Engine.Physics2D/Abstract/ICollider2D.cs | 2 ++ Engine.Physics2D/Collider2DBehaviourBase.cs | 1 + Engine.Physics2D/PhysicsEngine2D.cs | 15 +++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/Engine.Physics2D/Abstract/ICollider2D.cs b/Engine.Physics2D/Abstract/ICollider2D.cs index 9dfb806..5d2a5ff 100644 --- a/Engine.Physics2D/Abstract/ICollider2D.cs +++ b/Engine.Physics2D/Abstract/ICollider2D.cs @@ -9,6 +9,8 @@ public interface ICollider2D : IBehaviour, IAssignableTransform Action? OnCollisionDetected { get; set; } Action? OnCollisionResolved { get; set; } + Action? OnTriggered { get; set; } + IRigidBody2D? RigidBody2D { get; } bool IsTrigger { get; set; } diff --git a/Engine.Physics2D/Collider2DBehaviourBase.cs b/Engine.Physics2D/Collider2DBehaviourBase.cs index 0d42623..e69096f 100644 --- a/Engine.Physics2D/Collider2DBehaviourBase.cs +++ b/Engine.Physics2D/Collider2DBehaviourBase.cs @@ -10,6 +10,7 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D { public Action? OnCollisionDetected { get; set; } = null; public Action? OnCollisionResolved { get; set; } = null; + public Action? OnTriggered { get; set; } = null; protected bool NeedsRecalculation { get; private set; } = true; diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index f0d9406..af56e22 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -61,8 +61,23 @@ public class PhysicsEngine2D : IPhysicsEngine2D if (colliderX.RigidBody2D == colliderY.RigidBody2D) continue; + bool bothCollidersAreTriggers = colliderX.IsTrigger && colliderX.IsTrigger == colliderY.IsTrigger; + if (bothCollidersAreTriggers) + continue; + if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information)) { + if (colliderX.IsTrigger) + { + colliderX.OnTriggered?.Invoke(colliderX, colliderY); + continue; + } + else if (colliderY.IsTrigger) + { + colliderY.OnTriggered?.Invoke(colliderY, colliderY); + continue; + } + colliderX.OnCollisionDetected?.Invoke(colliderX, information); colliderY.OnCollisionDetected?.Invoke(colliderY, information); From 6a104d8abd013334ed2787c4b4c6fa6481e4c6a4 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 20:34:58 +0300 Subject: [PATCH 82/93] fix: Collider2DShapeBehaviour TransformShape ref Requirement --- Engine.Physics2D/Collider2DShapeBehaviour.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index f225ad0..28b9f51 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -5,9 +5,10 @@ namespace Syntriax.Engine.Physics2D; public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2D { - public Shape ShapeWorld { get; protected set; } = Shape.Box; + public Shape ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; } public Shape ShapeLocal { get; set; } = Shape.Box; + private Shape _shapeWorld = Shape.Box; public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); From 69eca44dd8ca8c13bb0bdd0ef3b6599c23fd5de5 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 21:05:56 +0300 Subject: [PATCH 83/93] fix: DebuggerDisplay Wrongly Typed Parameters --- Engine.Core/BehaviourController.cs | 2 +- Engine.Core/GameManager.cs | 2 +- Engine.Physics2D/Primitives/Shape.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine.Core/BehaviourController.cs b/Engine.Core/BehaviourController.cs index 22367b8..0a96572 100644 --- a/Engine.Core/BehaviourController.cs +++ b/Engine.Core/BehaviourController.cs @@ -7,7 +7,7 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Core; -[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count()}")] +[System.Diagnostics.DebuggerDisplay("Behaviour Count: {behaviours.Count}")] public class BehaviourController : IBehaviourController { public Action? OnPreUpdate { get; set; } diff --git a/Engine.Core/GameManager.cs b/Engine.Core/GameManager.cs index 6ac44cb..5d24d65 100644 --- a/Engine.Core/GameManager.cs +++ b/Engine.Core/GameManager.cs @@ -8,7 +8,7 @@ using Syntriax.Engine.Core.Factory; namespace Syntriax.Engine.Core; -[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count()}")] +[System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")] public class GameManager : IEntity, IEnumerable { public Action? OnCameraChanged { get; set; } = null; diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index abc49ed..432063b 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -6,7 +6,7 @@ using Syntriax.Engine.Core.Abstract; namespace Syntriax.Engine.Physics2D.Primitives; -[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count()}")] +[System.Diagnostics.DebuggerDisplay("Vertices Count: {Vertices.Count}")] public readonly struct Shape(IList Vertices) : IEnumerable { public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up); From 2ca243d79cba874db3dfe54bc872b43dde6c720b Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 21:15:17 +0300 Subject: [PATCH 84/93] fix: Shape & Circle Colliders Parametered Constructs Null Transform Error --- Engine.Physics2D/Collider2DCircleBehaviour.cs | 6 +----- Engine.Physics2D/Collider2DShapeBehaviour.cs | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Engine.Physics2D/Collider2DCircleBehaviour.cs b/Engine.Physics2D/Collider2DCircleBehaviour.cs index d652636..7314c02 100644 --- a/Engine.Physics2D/Collider2DCircleBehaviour.cs +++ b/Engine.Physics2D/Collider2DCircleBehaviour.cs @@ -13,9 +13,5 @@ public class Collider2DCircleBehaviour : Collider2DBehaviourBase, ICircleCollide public Collider2DCircleBehaviour() { } - public Collider2DCircleBehaviour(Circle circle) - { - CircleLocal = circle; - Recalculate(); - } + public Collider2DCircleBehaviour(Circle circle) => CircleLocal = circle; } diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index 28b9f51..a4d993d 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -14,9 +14,5 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2 public Collider2DShapeBehaviour() { } - public Collider2DShapeBehaviour(Shape shape) - { - ShapeLocal = shape; - Recalculate(); - } + public Collider2DShapeBehaviour(Shape shape) => ShapeLocal = shape; } From 11483231a5920d480256680935459ed77ecb80bb Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 21:21:35 +0300 Subject: [PATCH 85/93] fix: ShapeExtensions.CreateCopy not having this Keyword --- Engine.Physics2D/Primitives/Shape.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Physics2D/Primitives/Shape.cs b/Engine.Physics2D/Primitives/Shape.cs index 432063b..d332ab0 100644 --- a/Engine.Physics2D/Primitives/Shape.cs +++ b/Engine.Physics2D/Primitives/Shape.cs @@ -140,7 +140,7 @@ public readonly struct Shape(IList Vertices) : IEnumerable public static class ShapeExtensions { - public static Shape CreateCopy(Shape shape) => Shape.CreateCopy(shape); + public static Shape CreateCopy(this Shape shape) => Shape.CreateCopy(shape); public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape); public static void ToLines(this Shape shape, IList lines) => Shape.GetLines(shape, lines); public static List ToLines(this Shape shape) => Shape.GetLines(shape); From c1c1676b9a4c822b0e8f8adf23262127d7a11fba Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 21:21:58 +0300 Subject: [PATCH 86/93] feat: Behaviour DebuggerDisplay Displays Behaviour Type Name Now --- Engine.Core/Behaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Core/Behaviour.cs b/Engine.Core/Behaviour.cs index f782872..fb7ae2c 100644 --- a/Engine.Core/Behaviour.cs +++ b/Engine.Core/Behaviour.cs @@ -5,7 +5,7 @@ using Syntriax.Engine.Core.Exceptions; namespace Syntriax.Engine.Core; -[System.Diagnostics.DebuggerDisplay("Priority: {Priority}, Initialized: {Initialized}")] +[System.Diagnostics.DebuggerDisplay("{GetType().Name, nq}, Priority: {Priority}, Initialized: {Initialized}")] public abstract class Behaviour : IBehaviour { public Action? OnUnassigned { get; set; } = null; From 1ffddab2c1b0f831e563678485e9e55421ee30d1 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sat, 27 Jan 2024 21:50:55 +0300 Subject: [PATCH 87/93] fix: Collider2DShapeBehaviour.ShapeWorld Create Copy of Shape.Box --- Engine.Physics2D/Collider2DShapeBehaviour.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Physics2D/Collider2DShapeBehaviour.cs b/Engine.Physics2D/Collider2DShapeBehaviour.cs index a4d993d..a658b14 100644 --- a/Engine.Physics2D/Collider2DShapeBehaviour.cs +++ b/Engine.Physics2D/Collider2DShapeBehaviour.cs @@ -8,7 +8,7 @@ public class Collider2DShapeBehaviour : Collider2DBehaviourBase, IShapeCollider2 public Shape ShapeWorld { get => _shapeWorld; protected set => _shapeWorld = value; } public Shape ShapeLocal { get; set; } = Shape.Box; - private Shape _shapeWorld = Shape.Box; + private Shape _shapeWorld = Shape.Box.CreateCopy(); public override void CalculateCollider() => Transform.TransformShape(ShapeLocal, ref _shapeWorld); From dc96b9302472eb6824204ebeecac893383997dfa Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sun, 28 Jan 2024 14:56:13 +0300 Subject: [PATCH 88/93] feat(physics): Engine Rigidbody2D Static Check --- Engine.Physics2D/PhysicsEngine2D.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index af56e22..8b22240 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -65,6 +65,10 @@ public class PhysicsEngine2D : IPhysicsEngine2D if (bothCollidersAreTriggers) continue; + bool bothCollidersAreStatic = colliderX.RigidBody2D?.IsStatic ?? true && colliderX.RigidBody2D?.IsStatic == colliderY.RigidBody2D?.IsStatic; + if (bothCollidersAreStatic) + continue; + if (collisionDetector.TryDetect(colliderX, colliderY, out CollisionDetectionInformation information)) { if (colliderX.IsTrigger) From 1c7d941bc177d5c3fe24b428c1b89d5c2025642f Mon Sep 17 00:00:00 2001 From: Syntriax Date: Sun, 28 Jan 2024 14:56:44 +0300 Subject: [PATCH 89/93] feat(core): IBehavior.IsActive --- Engine.Core/Abstract/IBehaviour.cs | 5 +++++ Engine.Core/Behaviour.cs | 2 ++ Engine.Core/BehaviourOverride.cs | 16 ++++++++-------- Engine.Physics2D/PhysicsEngine2D.cs | 6 ++++++ 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Engine.Core/Abstract/IBehaviour.cs b/Engine.Core/Abstract/IBehaviour.cs index 47c30bc..95d12d8 100644 --- a/Engine.Core/Abstract/IBehaviour.cs +++ b/Engine.Core/Abstract/IBehaviour.cs @@ -16,4 +16,9 @@ public interface IBehaviour : IEntity, IAssignableBehaviourController, IAssignab /// Call priority of the . /// int Priority { get; set; } + + /// + /// If the is active. + /// + bool IsActive { get; } } diff --git a/Engine.Core/Behaviour.cs b/Engine.Core/Behaviour.cs index fb7ae2c..4b9241b 100644 --- a/Engine.Core/Behaviour.cs +++ b/Engine.Core/Behaviour.cs @@ -26,6 +26,8 @@ public abstract class Behaviour : IBehaviour public IStateEnable StateEnable => _stateEnable; public IBehaviourController BehaviourController => _behaviourController; + public bool IsActive => StateEnable.Enabled && BehaviourController.GameObject.StateEnable.Enabled; + public bool Initialized { get => _initialized; diff --git a/Engine.Core/BehaviourOverride.cs b/Engine.Core/BehaviourOverride.cs index 81a8d4c..6e8dcc9 100644 --- a/Engine.Core/BehaviourOverride.cs +++ b/Engine.Core/BehaviourOverride.cs @@ -39,13 +39,13 @@ public abstract class BehaviourOverride : Behaviour OnFinalize(); } - protected virtual void OnPreUpdatePreEnabledCheck() { } + protected virtual void OnPreUpdatePreActiveCheck() { } protected virtual void OnPreUpdate() { } private void PreUpdate(IBehaviourController _) { - OnPreUpdatePreEnabledCheck(); + OnPreUpdatePreActiveCheck(); - if (!StateEnable.Enabled) + if (!IsActive) return; if (isInitializedThisFrame) @@ -61,23 +61,23 @@ public abstract class BehaviourOverride : Behaviour isInitializedThisFrame = false; } - protected virtual void OnUpdatePreEnabledCheck() { } + protected virtual void OnUpdatePreActiveCheck() { } protected virtual void OnUpdate() { } private void Update(IBehaviourController _) { - OnUpdatePreEnabledCheck(); + OnUpdatePreActiveCheck(); - if (!StateEnable.Enabled) + if (!IsActive) return; OnUpdate(); } - protected virtual void OnPreDrawPreEnabledCheck() { } + protected virtual void OnPreDrawPreActiveCheck() { } protected virtual void OnPreDraw() { } private void PreDraw(IBehaviourController _) { - OnPreDrawPreEnabledCheck(); + OnPreDrawPreActiveCheck(); if (!StateEnable.Enabled) return; diff --git a/Engine.Physics2D/PhysicsEngine2D.cs b/Engine.Physics2D/PhysicsEngine2D.cs index 8b22240..c61af05 100644 --- a/Engine.Physics2D/PhysicsEngine2D.cs +++ b/Engine.Physics2D/PhysicsEngine2D.cs @@ -54,10 +54,16 @@ public class PhysicsEngine2D : IPhysicsEngine2D for (int x = 0; x < colliders.Count; x++) { ICollider2D? colliderX = colliders[x]; + if (!colliderX.IsActive) + return; + for (int y = x + 1; y < colliders.Count; y++) { ICollider2D? colliderY = colliders[y]; + if (!colliderY.IsActive) + return; + if (colliderX.RigidBody2D == colliderY.RigidBody2D) continue; From d7d53e467a9fe118c8ec9e93ee97a3bd64b1208e Mon Sep 17 00:00:00 2001 From: Syntriax Date: Mon, 29 Jan 2024 12:43:52 +0300 Subject: [PATCH 90/93] fix: Vector2D Normal Debugger Display --- Engine.Core/Vector2D.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine.Core/Vector2D.cs b/Engine.Core/Vector2D.cs index 1f23e9a..bbf9399 100644 --- a/Engine.Core/Vector2D.cs +++ b/Engine.Core/Vector2D.cs @@ -2,7 +2,7 @@ using System; namespace Syntriax.Engine.Core; -[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized}")] +[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")] public readonly struct Vector2D(float X, float Y) { public readonly float X { get; init; } = X; From 04614547934f66df4d0b39a8bca4aa49fabec493 Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 30 Jan 2024 12:03:53 +0300 Subject: [PATCH 91/93] feat: Math's Abs, Clamp, Max, Min & Sqr Methods Converted to Generics --- Engine.Core/Math.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Engine.Core/Math.cs b/Engine.Core/Math.cs index 7cec55b..eda310b 100644 --- a/Engine.Core/Math.cs +++ b/Engine.Core/Math.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; namespace Syntriax.Engine.Core; @@ -11,24 +12,24 @@ public static class Math public const float PI = 3.1415926535897932f; public const float Tau = 2f * PI; - public static float Abs(float x) => MathF.Abs(x); + public static T Abs(T x) where T : INumber => x > T.Zero ? x : -x; public static float Acos(float x) => MathF.Acos(x); public static float Asin(float x) => MathF.Asin(x); public static float Atan2(float y, float x) => MathF.Atan2(y, x); public static float Atanh(float x) => MathF.Atanh(x); - public static float Clamp(float x, float min, float max) => (x < min) ? min : (x > max) ? max : x; + public static T Clamp(this T x, T min, T max) where T : INumber => (x < min) ? min : (x > max) ? max : x; public static float Ceiling(float x) => MathF.Ceiling(x); public static float CopySign(float x, float y) => MathF.CopySign(x, y); public static float Floor(float x) => MathF.Floor(x); public static float IEEERemainder(float x, float y) => MathF.IEEERemainder(x, y); public static float Log(float x, float y) => MathF.Log(x, y); - public static float Max(float x, float y) => MathF.Max(x, y); + public static T Max(T x, T y) where T : INumber => (x > y) ? x : y; public static float MaxMagnitude(float x, float y) => MathF.MaxMagnitude(x, y); - public static float Min(float x, float y) => MathF.Min(x, y); + public static T Min(T x, T y) where T : INumber => (x < y) ? x : y; public static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y); public static float Pow(float x, float y) => MathF.Pow(x, y); public static float Round(float x, int digits, MidpointRounding mode) => MathF.Round(x, digits, mode); - public static float Sqr(float x) => x * x; + public static T Sqr(T x) where T : INumber => x * x; public static float Sqrt(float x) => MathF.Sqrt(x); public static float Truncate(float x) => MathF.Truncate(x); } From 9853e0af365a3ba78116f7489f53f92e2256a40c Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 30 Jan 2024 12:08:21 +0300 Subject: [PATCH 92/93] feat: IGameManager --- Engine.Core/Abstract/IGameManager.cs | 21 +++++++++++++++++++++ Engine.Core/GameManager.cs | 9 ++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 Engine.Core/Abstract/IGameManager.cs diff --git a/Engine.Core/Abstract/IGameManager.cs b/Engine.Core/Abstract/IGameManager.cs new file mode 100644 index 0000000..6909fb2 --- /dev/null +++ b/Engine.Core/Abstract/IGameManager.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace Syntriax.Engine.Core.Abstract; + +public interface IGameManager : IEntity, IEnumerable +{ + Action? OnGameObjectRegistered { get; set; } + Action? OnGameObjectUnRegistered { get; set; } + + + IReadOnlyList GameObjects { get; } + + + void RegisterGameObject(IGameObject gameObject); + T InstantiateGameObject(params object?[]? args) where T : class, IGameObject; + IGameObject RemoveGameObject(IGameObject gameObject); + + void Update(EngineTime time); + void PreDraw(); +} diff --git a/Engine.Core/GameManager.cs b/Engine.Core/GameManager.cs index 5d24d65..be303a3 100644 --- a/Engine.Core/GameManager.cs +++ b/Engine.Core/GameManager.cs @@ -9,7 +9,7 @@ using Syntriax.Engine.Core.Factory; namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")] -public class GameManager : IEntity, IEnumerable +public class GameManager : IGameManager { public Action? OnCameraChanged { get; set; } = null; public Action? OnGameObjectRegistered { get; set; } = null; @@ -21,7 +21,7 @@ public class GameManager : IEntity, IEnumerable public Action? OnStateEnableAssigned { get; set; } = null; - private IList _gameObjects = new List(Constants.GAME_OBJECTS_SIZE_INITIAL); + private readonly List _gameObjects = new(Constants.GAME_OBJECTS_SIZE_INITIAL); private IStateEnable _stateEnable = null!; private GameObjectFactory _gameObjectFactory = null!; @@ -39,7 +39,8 @@ public class GameManager : IEntity, IEnumerable } public bool Initialized => _initialized; - public IList GameObjects => _gameObjects; + public IReadOnlyList GameObjects => _gameObjects; + public IStateEnable StateEnable { get @@ -102,6 +103,7 @@ public class GameManager : IEntity, IEnumerable foreach (var gameObject in GameObjects) gameObject.Initialize(); + _initialized = true; OnInitialized?.Invoke(this); return true; } @@ -115,6 +117,7 @@ public class GameManager : IEntity, IEnumerable GameObjects[i].Finalize(); OnFinalized?.Invoke(this); + _initialized = false; return true; } From 9768dbddeda50d253aec1b5ae59489c47069b29e Mon Sep 17 00:00:00 2001 From: Syntriax Date: Tue, 30 Jan 2024 12:13:47 +0300 Subject: [PATCH 93/93] refactor(core): Removed ICamera --- Engine.Core/Abstract/ICamera.cs | 12 ------------ Engine.Core/GameManager.cs | 15 --------------- 2 files changed, 27 deletions(-) delete mode 100644 Engine.Core/Abstract/ICamera.cs diff --git a/Engine.Core/Abstract/ICamera.cs b/Engine.Core/Abstract/ICamera.cs deleted file mode 100644 index bcab06c..0000000 --- a/Engine.Core/Abstract/ICamera.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Syntriax.Engine.Core.Abstract; - -public interface ICamera : IAssignableTransform -{ - Action? OnZoomChanged { get; set; } - - float Zoom { get; set; } - - void Update(); -} diff --git a/Engine.Core/GameManager.cs b/Engine.Core/GameManager.cs index be303a3..b9dd0c4 100644 --- a/Engine.Core/GameManager.cs +++ b/Engine.Core/GameManager.cs @@ -11,7 +11,6 @@ namespace Syntriax.Engine.Core; [System.Diagnostics.DebuggerDisplay("GameObject Count: {_gameObjects.Count}")] public class GameManager : IGameManager { - public Action? OnCameraChanged { get; set; } = null; public Action? OnGameObjectRegistered { get; set; } = null; public Action? OnGameObjectUnRegistered { get; set; } = null; @@ -26,7 +25,6 @@ public class GameManager : IGameManager private IStateEnable _stateEnable = null!; private GameObjectFactory _gameObjectFactory = null!; private bool _initialized = false; - private ICamera _camera = null!; private GameObjectFactory GameObjectFactory { @@ -56,19 +54,6 @@ public class GameManager : IGameManager } } - public ICamera Camera - { - get => _camera; - set - { - if (_camera == value) - return; - - _camera = value; - OnCameraChanged?.Invoke(this); - } - } - public void RegisterGameObject(IGameObject gameObject) { if (_gameObjects.Contains(gameObject))