mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-15 14:52:35 +02:00
Physics (#1602)
* Physics worlds * Paul's a good boy * Build working * Ingame and not lagging to hell * Why didn't you commit ahhhhh * Hard collisions working * Solver parity * Decent broadphase work done * BroadPhase outline done * BroadPhase working * waiting for pvs * Fix static PVS AABB * Stop static bodies from awakening * Optimise a bunch of stuff * Even more broadphase stuff * I'm fucking stupid * Optimise fixture updates * Collision solver start * Building * A is for Argumentative * Fix contact caching island flags * Circle shapes actually workeded * Damping * DS2 consumables only * Slightly more stable * Even slightlier more stablier * VV your heart out * Initial joint support * 90% of joints I just wanted to push as I'd scream if I lost progress * JOINT PURGATORY * Joints barely functional lmao * Okay these joints slightly more functional * Remove station FrictionJoint * Also that * Some Box2D ports * Cleanup mass * Edge shape * Active contacts * Fix active contacts * Optimise active contacts even more * Boxes be stacking * I would die for smug oh my fucking god * In which everything is fixed * Distance joints working LETS GO * Remove frequency on distancejoint * Fix some stuff and break joints * Crashing fixed mehbeh * ICollideSpecial and more resilience * auto-clear * showbb vera * Slap that TODO in there * Fix restartround crash * Random fixes * Fix fixture networking * Add intersection method for broadphase * Fix contacts * Licenses done * Optimisations * Fix wall clips * Config caching for island * allocations optimisations * Optimise casts * Optimise events queue for physics * Contact manager optimisations * Optimise controllers * Sloth joint or something idk * Controller graph * Remove content cvar * Random cleanup * Finally remove VirtualController * Manifold structs again * Optimise this absolute retardation * Optimise * fix license * Cleanup physics interface * AHHHHHHHHHHHHH * Fix collisions again * snivybus * Fix potential nasty manifold bug * Tests go snivy Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
co-authored by
Metal Gear Sloth
parent
30df989e8d
commit
fefcc7cba3
@@ -2,10 +2,12 @@ using System.IO;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.UnitTesting.Client.GameObjects.Components
|
||||
@@ -38,7 +40,11 @@ namespace Robust.UnitTesting.Client.GameObjects.Components
|
||||
|
||||
var mock = new Mock<IEntitySystemManager>();
|
||||
var system = new SharedTransformSystem();
|
||||
var broady = new BroadPhaseSystem();
|
||||
var physics = new PhysicsSystem();
|
||||
mock.Setup(m => m.GetEntitySystem<SharedTransformSystem>()).Returns(system);
|
||||
mock.Setup(m => m.GetEntitySystem<SharedBroadPhaseSystem>()).Returns(broady);
|
||||
mock.Setup(m => m.GetEntitySystem<SharedPhysicsSystem>()).Returns(physics);
|
||||
|
||||
IoCManager.RegisterInstance<IEntitySystemManager>(mock.Object, true);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -43,6 +46,18 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
|
||||
|
||||
private static readonly EntityCoordinates InitialPos = new(new EntityUid(1), (0, 0));
|
||||
|
||||
protected override void OverrideIoC()
|
||||
{
|
||||
base.OverrideIoC();
|
||||
var mock = new Mock<IEntitySystemManager>();
|
||||
var broady = new BroadPhaseSystem();
|
||||
var physics = new PhysicsSystem();
|
||||
mock.Setup(m => m.GetEntitySystem<SharedBroadPhaseSystem>()).Returns(broady);
|
||||
mock.Setup(m => m.GetEntitySystem<SharedPhysicsSystem>()).Returns(physics);
|
||||
|
||||
IoCManager.RegisterInstance<IEntitySystemManager>(mock.Object, true);
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Maps;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -34,9 +38,10 @@ entities:
|
||||
type: Transform
|
||||
- index: 0
|
||||
type: MapGrid
|
||||
- shapes:
|
||||
- !type:PhysShapeGrid
|
||||
grid: 0
|
||||
- fixtures:
|
||||
- shape:
|
||||
!type:PhysShapeGrid
|
||||
grid: 0
|
||||
type: Physics
|
||||
- uid: 1
|
||||
type: MapDeserializeTest
|
||||
@@ -57,6 +62,20 @@ entities:
|
||||
|
||||
";
|
||||
|
||||
protected override void OverrideIoC()
|
||||
{
|
||||
base.OverrideIoC();
|
||||
var mockFormat = new Mock<ICustomFormatManager>();
|
||||
var mock = new Mock<IEntitySystemManager>();
|
||||
var broady = new BroadPhaseSystem();
|
||||
var physics = new PhysicsSystem();
|
||||
mock.Setup(m => m.GetEntitySystem<SharedBroadPhaseSystem>()).Returns(broady);
|
||||
mock.Setup(m => m.GetEntitySystem<SharedPhysicsSystem>()).Returns(physics);
|
||||
|
||||
IoCManager.RegisterInstance<IEntitySystemManager>(mock.Object, true);
|
||||
IoCManager.RegisterInstance<ICustomFormatManager>(mockFormat.Object, true);
|
||||
}
|
||||
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
@@ -80,6 +99,7 @@ entities:
|
||||
[Test]
|
||||
public void TestDataLoadPriority()
|
||||
{
|
||||
// TODO: Fix after serv3
|
||||
var map = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using System.IO;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
@@ -20,6 +24,18 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
components:
|
||||
- type: Transform";
|
||||
|
||||
protected override void OverrideIoC()
|
||||
{
|
||||
base.OverrideIoC();
|
||||
var mock = new Mock<IEntitySystemManager>();
|
||||
var broady = new BroadPhaseSystem();
|
||||
var physics = new PhysicsSystem();
|
||||
mock.Setup(m => m.GetEntitySystem<SharedBroadPhaseSystem>()).Returns(broady);
|
||||
mock.Setup(m => m.GetEntitySystem<SharedPhysicsSystem>()).Returns(physics);
|
||||
|
||||
IoCManager.RegisterInstance<IEntitySystemManager>(mock.Object, true);
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Timing;
|
||||
using MapGrid = Robust.Shared.Map.MapGrid;
|
||||
|
||||
@@ -13,6 +16,19 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
[TestFixture, TestOf(typeof(MapGrid))]
|
||||
class MapGrid_Tests : RobustUnitTest
|
||||
{
|
||||
protected override void OverrideIoC()
|
||||
{
|
||||
base.OverrideIoC();
|
||||
|
||||
var mock = new Mock<IEntitySystemManager>();
|
||||
var broady = new BroadPhaseSystem();
|
||||
var physics = new PhysicsSystem();
|
||||
mock.Setup(m => m.GetEntitySystem<SharedBroadPhaseSystem>()).Returns(broady);
|
||||
mock.Setup(m => m.GetEntitySystem<SharedPhysicsSystem>()).Returns(physics);
|
||||
|
||||
IoCManager.RegisterInstance<IEntitySystemManager>(mock.Object, true);
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using NUnit.Framework;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Map
|
||||
{
|
||||
@@ -11,6 +15,18 @@ namespace Robust.UnitTesting.Shared.Map
|
||||
{
|
||||
public override UnitTestProject Project => UnitTestProject.Server;
|
||||
|
||||
protected override void OverrideIoC()
|
||||
{
|
||||
base.OverrideIoC();
|
||||
var mock = new Mock<IEntitySystemManager>();
|
||||
var broady = new BroadPhaseSystem();
|
||||
var physics = new PhysicsSystem();
|
||||
mock.Setup(m => m.GetEntitySystem<SharedBroadPhaseSystem>()).Returns(broady);
|
||||
mock.Setup(m => m.GetEntitySystem<SharedPhysicsSystem>()).Returns(physics);
|
||||
|
||||
IoCManager.RegisterInstance<IEntitySystemManager>(mock.Object, true);
|
||||
}
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void OneTimeSetup()
|
||||
{
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(PhysicsManager))]
|
||||
internal class CollisionManager_Test
|
||||
{
|
||||
[Test]
|
||||
public void IsCollidingFalse()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(5, -5, 10, 6);
|
||||
var testBox = new Box2(-3, -3, 4, 6);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mock = new Mock<IPhysBody>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
mock.Setup(foo => foo.MapID).Returns(new MapId(0));
|
||||
mock.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock.Setup(foo => foo.CollisionLayer).Returns(0x4);
|
||||
mock.Setup(foo => foo.CollisionMask).Returns(0x04);
|
||||
manager.AddBody(mock.Object);
|
||||
|
||||
// Act
|
||||
var result = manager.TryCollideRect(testBox, new MapId(0));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsCollidingInclusive()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(5, -5, 10, 6);
|
||||
var testBox = new Box2(-3, -3, 5, 6);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mock = new Mock<IPhysBody>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
mock.Setup(foo => foo.MapID).Returns(new MapId(0));
|
||||
mock.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock.Setup(foo => foo.CollisionLayer).Returns(0x4);
|
||||
mock.Setup(foo => foo.CollisionMask).Returns(0x04);
|
||||
manager.AddBody(mock.Object);
|
||||
|
||||
// Act
|
||||
var result = manager.TryCollideRect(testBox, new MapId(0));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsCollidingTrue()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(5, -5, 10, 6);
|
||||
var testBox = new Box2(-3, -3, 6, 6);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mock = new Mock<IPhysBody>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
mock.Setup(foo => foo.MapID).Returns(new MapId(0));
|
||||
mock.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock.Setup(foo => foo.CollisionLayer).Returns(0x4);
|
||||
mock.Setup(foo => foo.CollisionMask).Returns(0x04);
|
||||
manager.AddBody(mock.Object);
|
||||
|
||||
// Act
|
||||
var result = manager.TryCollideRect(testBox, new MapId(0));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsCollidingFalseNoLayer()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(5, -5, 10, 6);
|
||||
var testBox = new Box2(-3, -3, 6, 6);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mock = new Mock<IPhysBody>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
mock.Setup(foo => foo.MapID).Returns(new MapId(0));
|
||||
mock.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock.Setup(foo => foo.CollisionLayer).Returns(0); // Collision layer is None
|
||||
mock.Setup(foo => foo.CollisionMask).Returns(0x04);
|
||||
manager.AddBody(mock.Object);
|
||||
|
||||
// Act
|
||||
var result = manager.TryCollideRect(testBox, new MapId(0));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsCollidingWrongMap()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(5, -5, 10, 6);
|
||||
var testBox = new Box2(-3, -3, 5, 6);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mock = new Mock<IPhysBody>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
mock.Setup(foo => foo.MapID).Returns(new MapId(3));
|
||||
mock.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock.Setup(foo => foo.CollisionLayer).Returns(0x4);
|
||||
mock.Setup(foo => foo.CollisionMask).Returns(0x04);
|
||||
manager.AddBody(mock.Object);
|
||||
|
||||
// Act
|
||||
var result = manager.TryCollideRect(testBox, new MapId(0));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RayCast()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(5, -5, 10, 6);
|
||||
var ray = new CollisionRay(new Vector2(0, 1), Vector2.UnitX, 1);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mock = new Mock<IPhysBody>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
mock.Setup(foo => foo.Entity).Returns(new Entity(new ServerEntityManager(), EntityUid.FirstUid)); // requires IPhysBody not have null owner
|
||||
mock.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock.Setup(foo => foo.CollisionLayer).Returns(1);
|
||||
mock.Setup(foo => foo.CollisionMask).Returns(1);
|
||||
manager.AddBody(mock.Object);
|
||||
|
||||
// Act
|
||||
var results = manager.IntersectRay(new MapId(0), ray).ToList();
|
||||
|
||||
Assert.That(results.Count, Is.EqualTo(1));
|
||||
|
||||
var result = results.First();
|
||||
|
||||
// Assert
|
||||
Assert.That(result.Distance, Is.EqualTo(5));
|
||||
Assert.That(result.HitPos.X, Is.EqualTo(5));
|
||||
Assert.That(result.HitPos.Y, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RayCastMissUp()
|
||||
{
|
||||
// Arrange
|
||||
var box = new Box2(5, -5, 10, 6);
|
||||
var ray = new CollisionRay(new Vector2(4.99999f, 1), Vector2.UnitY, 1);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mock = new Mock<IPhysBody>();
|
||||
mock.Setup(foo => foo.WorldAABB).Returns(box);
|
||||
mock.Setup(foo => foo.Entity).Returns(new Entity(new ServerEntityManager(), EntityUid.FirstUid)); // requires IPhysBody not have null owner
|
||||
mock.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock.Setup(foo => foo.CollisionLayer).Returns(1);
|
||||
mock.Setup(foo => foo.CollisionMask).Returns(1);
|
||||
manager.AddBody(mock.Object);
|
||||
|
||||
// Act
|
||||
var results = manager.IntersectRay(new MapId(0), ray);
|
||||
|
||||
// Assert
|
||||
Assert.That(results.Count(), Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultiHitRayCast()
|
||||
{
|
||||
// Arrange
|
||||
var b1 = new Box2(5, -5, 10, 6);
|
||||
var b2 = new Box2(6, -10, 7, 10);
|
||||
var ray = new CollisionRay(Vector2.UnitY, Vector2.UnitX, 1);
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var e1 = new Entity(new ServerEntityManager(), EntityUid.FirstUid);
|
||||
var e2 = new Entity(new ServerEntityManager(), EntityUid.FirstUid);
|
||||
|
||||
var m1 = new Mock<IPhysBody>();
|
||||
m1.Setup(foo => foo.WorldAABB).Returns(b1);
|
||||
m1.Setup(foo => foo.Entity).Returns(e1);
|
||||
m1.Setup(foo => foo.CanCollide).Returns(true);
|
||||
m1.Setup(foo => foo.CollisionLayer).Returns(1);
|
||||
m1.Setup(foo => foo.CollisionMask).Returns(1);
|
||||
manager.AddBody(m1.Object);
|
||||
|
||||
var m2 = new Mock<IPhysBody>();
|
||||
m2.Setup(foo => foo.WorldAABB).Returns(b2);
|
||||
m2.Setup(foo => foo.Entity).Returns(e2);
|
||||
m2.Setup(foo => foo.CanCollide).Returns(true);
|
||||
m2.Setup(foo => foo.CollisionLayer).Returns(1);
|
||||
m2.Setup(foo => foo.CollisionMask).Returns(1);
|
||||
manager.AddBody(m2.Object);
|
||||
|
||||
var results = manager.IntersectRay(new MapId(0), ray, returnOnFirstHit: false).ToList();
|
||||
|
||||
Assert.That(results.Count, Is.EqualTo(2));
|
||||
Assert.That(results[0].HitEntity.Uid, Is.EqualTo(e1.Uid));
|
||||
Assert.That(results[1].HitEntity.Uid, Is.EqualTo(e2.Uid));
|
||||
Assert.That(results[0].Distance, Is.EqualTo(5));
|
||||
Assert.That(results[0].HitPos.X, Is.EqualTo(5));
|
||||
Assert.That(results[0].HitPos.Y, Is.EqualTo(1));
|
||||
Assert.That(results[1].Distance, Is.EqualTo(6));
|
||||
Assert.That(results[1].HitPos.X, Is.EqualTo(6));
|
||||
Assert.That(results[1].HitPos.Y, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoCollisionTestTrue()
|
||||
{
|
||||
// Arrange
|
||||
var manager = new PhysicsManager();
|
||||
|
||||
var mockEntity0 = new Mock<IEntity>().Object;
|
||||
var mockEntity1 = new Mock<IEntity>().Object;
|
||||
|
||||
var mock0 = new Mock<IPhysBody>();
|
||||
mock0.Setup(foo => foo.WorldAABB).Returns(new Box2(-3, -3, 6, 6));
|
||||
mock0.Setup(foo => foo.MapID).Returns(new MapId(1));
|
||||
mock0.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock0.Setup(foo => foo.CollisionLayer).Returns(0x4);
|
||||
mock0.Setup(foo => foo.CollisionMask).Returns(0x04);
|
||||
mock0.Setup(foo => foo.Entity).Returns(mockEntity0);
|
||||
var staticBody = mock0.Object;
|
||||
manager.AddBody(staticBody);
|
||||
|
||||
var mock1 = new Mock<IPhysBody>();
|
||||
mock1.Setup(foo => foo.WorldAABB).Returns(new Box2(5, -5, 10, 6));
|
||||
mock1.Setup(foo => foo.MapID).Returns(new MapId(1));
|
||||
mock1.Setup(foo => foo.CanCollide).Returns(true);
|
||||
mock1.Setup(foo => foo.CollisionLayer).Returns(0x4);
|
||||
mock1.Setup(foo => foo.CollisionMask).Returns(0x04);
|
||||
mock1.Setup(foo => foo.Entity).Returns(mockEntity1);
|
||||
var testBody = mock1.Object;
|
||||
manager.AddBody(testBody);
|
||||
|
||||
// Act
|
||||
var results = manager.GetCollidingEntities(testBody, Vector2.Zero).ToImmutableList();
|
||||
|
||||
// Assert
|
||||
Assert.That(results.Count, Is.EqualTo(1));
|
||||
Assert.That(results[0], Is.EqualTo(mockEntity0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics
|
||||
{
|
||||
[TestFixture, Parallelizable, TestOf(typeof(CollisionSolver))]
|
||||
class CollisionSolverTests
|
||||
{
|
||||
[Test]
|
||||
public void CircleCircle_NotCollide()
|
||||
{
|
||||
var a = new Circle(new Vector2(0,0), 0.5f);
|
||||
var b = new Circle(new Vector2(1, 1), 0.5f);
|
||||
|
||||
CollisionSolver.CalculateCollisionFeatures(in a, in b, 1, out var results);
|
||||
|
||||
Assert.That(results.Collided, Is.EqualTo(false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CircleCircle_Collide()
|
||||
{
|
||||
var a = new Circle(new Vector2(0, 0), 1f);
|
||||
var b = new Circle(new Vector2(1.5f, 0), 1f);
|
||||
|
||||
CollisionSolver.CalculateCollisionFeatures(in a, in b, 1, out var results);
|
||||
|
||||
Assert.That(results.Collided);
|
||||
Assert.That(results.Normal, Is.EqualTo(Vector2.UnitX));
|
||||
Assert.That(results.Penetration, Is.EqualTo(0.5f / 2));
|
||||
Assert.That(results.Contacts, Is.Not.Null);
|
||||
Assert.That(results.Contacts.Length, Is.EqualTo(1));
|
||||
Assert.That(results.Contacts[0], Is.EqualTo(new Vector2(0.75f, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CircleCircle_SamePos()
|
||||
{
|
||||
var circle = new Circle(new Vector2(0, 0), 0.5f);
|
||||
|
||||
CollisionSolver.CalculateCollisionFeatures(in circle, in circle, 1, out var results);
|
||||
|
||||
Assert.That(results.Collided, Is.EqualTo(false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InverseTransformPoint()
|
||||
{
|
||||
var obb = new OrientedRectangle(Vector2.One, Vector2.Zero, MathF.PI / 2);
|
||||
var worldPoint = new Vector2(1, 3);
|
||||
|
||||
var localPoint = obb.InverseTransformPoint(worldPoint);
|
||||
|
||||
Assert.That(localPoint, Is.Approximately(new Vector2(2, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TransformPoint()
|
||||
{
|
||||
var obb = new OrientedRectangle(Vector2.One, Vector2.Zero, MathF.PI / 2);
|
||||
var worldPoint = new Vector2(2, 0);
|
||||
|
||||
var localPoint = obb.TransformPoint(worldPoint);
|
||||
|
||||
Assert.That(localPoint, Is.Approximately(new Vector2(1, 3)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TransformPointRoundTrip()
|
||||
{
|
||||
var obb = new OrientedRectangle(new Vector2(3, 5), Vector2.Zero, MathF.PI / 4);
|
||||
var worldPoint = new Vector2(11, 13);
|
||||
|
||||
var localPoint = obb.InverseTransformPoint(worldPoint);
|
||||
var result = obb.TransformPoint(localPoint);
|
||||
|
||||
Assert.That(result, Is.EqualTo(worldPoint));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClosestPoint()
|
||||
{
|
||||
var obb = new OrientedRectangle(Vector2.One, Vector2.One * 0.5f, MathF.PI / 2);
|
||||
var worldPoint = new Vector2(13, -13);
|
||||
|
||||
var closestPoint = obb.ClosestPointWorld(worldPoint);
|
||||
|
||||
Assert.That(closestPoint, Is.Approximately(new Vector2(1.5f, 0.5f)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RectCircle_NotCollide()
|
||||
{
|
||||
var a = new OrientedRectangle(Vector2.Zero, Vector2.One * 0.5f, MathF.PI / 4);
|
||||
var b = new Circle(new Vector2(1, 1), 0.5f);
|
||||
|
||||
CollisionSolver.CalculateCollisionFeatures(in a, in b, 1, out var results);
|
||||
|
||||
Assert.That(results.Collided, Is.EqualTo(false));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RectCircle_Collide()
|
||||
{
|
||||
var a = new OrientedRectangle(Vector2.Zero, new Vector2(3, 1), MathF.PI / 2);
|
||||
var b = new Circle(new Vector2(1.5f, 0), 1f);
|
||||
|
||||
CollisionSolver.CalculateCollisionFeatures(in a, in b, 1, out var results);
|
||||
|
||||
Assert.That(results.Collided);
|
||||
Assert.That(results.Normal, Is.Approximately(Vector2.UnitX));
|
||||
Assert.That(results.Penetration, Is.EqualTo(0.5f / 2));
|
||||
Assert.That(results.Contacts, Is.Not.Null);
|
||||
Assert.That(results.Contacts.Length, Is.EqualTo(1));
|
||||
Assert.That(results.Contacts[0], Is.Approximately(new Vector2(0.75f, 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BoxBox_NotCollide()
|
||||
{
|
||||
var a = new AlignedRectangle(new Vector2(1, 0), new Vector2(0.5f, 0.5f));
|
||||
var b = new AlignedRectangle(Vector2.Zero, new Vector2(0.5f, 0.5f));
|
||||
|
||||
CollisionSolver.CalculateCollisionFeatures(in a, in b, 1, out var results);
|
||||
|
||||
Assert.That(results.Collided, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BoxBox_Collide()
|
||||
{
|
||||
var a = new AlignedRectangle(new Vector2(1, 0), new Vector2(0.5f, 0.5f));
|
||||
var b = new AlignedRectangle(new Vector2(0.25f, 0.15f), new Vector2(0.5f, 0.5f));
|
||||
|
||||
CollisionSolver.CalculateCollisionFeatures(in a, in b, 1, out var results);
|
||||
|
||||
Assert.That(results.Collided, Is.True);
|
||||
Assert.That(results.Normal, Is.EqualTo(new Vector2(-1, 0)));
|
||||
Assert.That(results.Penetration, Is.EqualTo(0.25f / 2));
|
||||
Assert.That(results.Contacts, Is.Not.Null);
|
||||
Assert.That(results.Contacts.Length, Is.EqualTo(1));
|
||||
Assert.That(results.Contacts[0], Is.EqualTo(new Vector2(0.5f, 0.15f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Robust.UnitTesting.Shared.Physics
|
||||
{
|
||||
public class Quaternion_Test
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Dynamics.Shapes;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(IPhysShape))]
|
||||
public class Shape_Test : RobustUnitTest
|
||||
{
|
||||
[Test]
|
||||
public void TestPolyNormals()
|
||||
{
|
||||
var poly = new PolygonShape
|
||||
{
|
||||
Vertices = new List<Vector2>
|
||||
{
|
||||
new(1, -1),
|
||||
new(1, 1),
|
||||
new(-1, 1),
|
||||
new(-1, -1),
|
||||
}
|
||||
};
|
||||
|
||||
Assert.That(poly.Normals.Count, Is.EqualTo(4));
|
||||
|
||||
Assert.That(poly.Normals[0], Is.EqualTo(new Vector2(1, 0)), $"Vert is {poly.Vertices[0]}");
|
||||
Assert.That(poly.Normals[1], Is.EqualTo(new Vector2(0, 1)), $"Vert is {poly.Vertices[1]}");
|
||||
Assert.That(poly.Normals[2], Is.EqualTo(new Vector2(-1, 0)), $"Vert is {poly.Vertices[2]}");
|
||||
Assert.That(poly.Normals[3], Is.EqualTo(new Vector2(0, -1)), $"Vert is {poly.Vertices[3]}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Erin Catto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
These tests are derived from box2d's testbed tests but done in a way as to be automated and useful for CI.
|
||||
*/
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Dynamics.Shapes;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics
|
||||
{
|
||||
[TestFixture]
|
||||
public class Testbed_Test : RobustIntegrationTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestBoxStack()
|
||||
{
|
||||
var server = StartServer();
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
|
||||
var physicsSystem = entitySystemManager.GetEntitySystem<SharedPhysicsSystem>();
|
||||
MapId mapId;
|
||||
|
||||
var columnCount = 1;
|
||||
var rowCount = 15;
|
||||
PhysicsComponent[] bodies = new PhysicsComponent[columnCount * rowCount];
|
||||
Vector2 firstPos = Vector2.Zero;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapId = mapManager.CreateMap();
|
||||
physicsSystem.Maps[mapId].Gravity = new Vector2(0, -9.8f);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
// TODO: Need a blank entity we can spawn for testbed.
|
||||
var ground = entityManager.SpawnEntity("BlankEntity", new MapCoordinates(0, 0, mapId)).AddComponent<PhysicsComponent>();
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-20, 0), new Vector2(20, 0));
|
||||
var horizontalFixture = new Fixture(ground, horizontal)
|
||||
{
|
||||
CollisionLayer = 1,
|
||||
CollisionMask = 1,
|
||||
Hard = true
|
||||
};
|
||||
ground.AddFixture(horizontalFixture);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
|
||||
var verticalFixture = new Fixture(ground, vertical)
|
||||
{
|
||||
CollisionLayer = 1,
|
||||
CollisionMask = 1,
|
||||
Hard = true
|
||||
};
|
||||
ground.AddFixture(verticalFixture);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
|
||||
};
|
||||
|
||||
PhysShapeAabb shape;
|
||||
|
||||
for (var j = 0; j < columnCount; j++)
|
||||
{
|
||||
for (var i = 0; i < rowCount; i++)
|
||||
{
|
||||
var x = 0.0f;
|
||||
|
||||
var box = entityManager.SpawnEntity("BlankEntity",
|
||||
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)).AddComponent<PhysicsComponent>();
|
||||
|
||||
box.BodyType = BodyType.Dynamic;
|
||||
box.SleepingAllowed = false;
|
||||
shape = new PhysShapeAabb {LocalBounds = Box2.UnitCentered};
|
||||
box.FixedRotation = true;
|
||||
// TODO: Need to detect shape and work out if we need to use fixedrotation
|
||||
|
||||
var fixture = new Fixture(box, shape)
|
||||
{
|
||||
CollisionMask = 1,
|
||||
CollisionLayer = 1,
|
||||
Hard = true,
|
||||
};
|
||||
box.AddFixture(fixture);
|
||||
|
||||
bodies[j * rowCount + i] = box;
|
||||
}
|
||||
}
|
||||
|
||||
firstPos = bodies[0].Owner.Transform.WorldPosition;
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
// Check that gravity workin
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(firstPos != bodies[0].Owner.Transform.WorldPosition);
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
await server.WaitRunTicks(150);
|
||||
|
||||
// Assert settled, none below 0, etc.
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
for (var j = 0; j < columnCount; j++)
|
||||
{
|
||||
for (var i = 0; i < bodies.Length; i++)
|
||||
{
|
||||
var body = bodies[j * columnCount + i];
|
||||
var worldPos = body.Owner.Transform.WorldPosition;
|
||||
|
||||
// TODO: Multi-column support but I cbf right now
|
||||
// Can't be more exact as some level of sinking is allowed.
|
||||
Assert.That(worldPos.EqualsApprox(new Vector2(0.0f, i + 0.5f), 0.1f), $"Expected y-value of {i + 0.5f} but found {worldPos.Y}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TestCircleStack()
|
||||
{
|
||||
var server = StartServer();
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entitySystemManager = server.ResolveDependency<IEntitySystemManager>();
|
||||
var physicsSystem = entitySystemManager.GetEntitySystem<SharedPhysicsSystem>();
|
||||
MapId mapId;
|
||||
|
||||
var columnCount = 1;
|
||||
var rowCount = 15;
|
||||
PhysicsComponent[] bodies = new PhysicsComponent[columnCount * rowCount];
|
||||
Vector2 firstPos = Vector2.Zero;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
mapId = mapManager.CreateMap();
|
||||
physicsSystem.Maps[mapId].Gravity = new Vector2(0, -9.8f);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
// TODO: Need a blank entity we can spawn for testbed.
|
||||
var ground = entityManager.SpawnEntity("BlankEntity", new MapCoordinates(0, 0, mapId)).AddComponent<PhysicsComponent>();
|
||||
|
||||
var horizontal = new EdgeShape(new Vector2(-20, 0), new Vector2(20, 0));
|
||||
var horizontalFixture = new Fixture(ground, horizontal)
|
||||
{
|
||||
CollisionLayer = 1,
|
||||
CollisionMask = 1,
|
||||
Hard = true
|
||||
};
|
||||
ground.AddFixture(horizontalFixture);
|
||||
|
||||
var vertical = new EdgeShape(new Vector2(10, 0), new Vector2(10, 10));
|
||||
var verticalFixture = new Fixture(ground, vertical)
|
||||
{
|
||||
CollisionLayer = 1,
|
||||
CollisionMask = 1,
|
||||
Hard = true
|
||||
};
|
||||
ground.AddFixture(verticalFixture);
|
||||
|
||||
var xs = new[]
|
||||
{
|
||||
0.0f, -10.0f, -5.0f, 5.0f, 10.0f
|
||||
};
|
||||
|
||||
PhysShapeCircle shape;
|
||||
|
||||
for (var j = 0; j < columnCount; j++)
|
||||
{
|
||||
for (var i = 0; i < rowCount; i++)
|
||||
{
|
||||
var x = 0.0f;
|
||||
|
||||
var circle = entityManager.SpawnEntity("BlankEntity",
|
||||
new MapCoordinates(new Vector2(xs[j] + x, 0.55f + 2.1f * i), mapId)).AddComponent<PhysicsComponent>();
|
||||
|
||||
circle.BodyType = BodyType.Dynamic;
|
||||
circle.SleepingAllowed = false;
|
||||
shape = new PhysShapeCircle {Radius = 0.5f};
|
||||
|
||||
var fixture = new Fixture(circle, shape)
|
||||
{
|
||||
CollisionMask = 1,
|
||||
CollisionLayer = 1,
|
||||
Hard = true,
|
||||
};
|
||||
circle.AddFixture(fixture);
|
||||
|
||||
bodies[j * rowCount + i] = circle;
|
||||
}
|
||||
}
|
||||
|
||||
firstPos = bodies[0].Owner.Transform.WorldPosition;
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
// Check that gravity workin
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(firstPos != bodies[0].Owner.Transform.WorldPosition);
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
await server.WaitRunTicks(150);
|
||||
|
||||
// Assert settled, none below 0, etc.
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
for (var j = 0; j < columnCount; j++)
|
||||
{
|
||||
for (var i = 0; i < bodies.Length; i++)
|
||||
{
|
||||
var body = bodies[j * columnCount + i];
|
||||
var worldPos = body.Owner.Transform.WorldPosition;
|
||||
|
||||
// TODO: Multi-column support but I cbf right now
|
||||
// Can't be more exact as some level of sinking is allowed.
|
||||
Assert.That(worldPos.EqualsApprox(new Vector2(0.0f, i + 0.5f), 0.1f), $"Expected y-value of {i + 0.5f} but found {worldPos.Y}");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user