* 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:
metalgearsloth
2021-03-01 03:09:36 +11:00
committed by GitHub
co-authored by Metal Gear Sloth
parent 30df989e8d
commit fefcc7cba3
106 changed files with 13108 additions and 3175 deletions
@@ -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}");
}
}
});
}
}
}